Initial commit.
authorMario de Sousa <msousa@fe.up.pt>
Sun, 05 Mar 2017 00:05:46 +0000
changeset 0 ae252e0fd9b8
child 1 59783e8ee3d2
Initial commit.
COPYING
COPYING.LESSER
Makefile
README
mb_addr.h
mb_ascii.c
mb_ascii_private.h
mb_ds_util.h
mb_layer1.h
mb_layer1_prototypes.h
mb_master.c
mb_master.h
mb_master_private.h
mb_rtu.c
mb_rtu_private.h
mb_slave.c
mb_slave.h
mb_slave_and_master.c
mb_slave_and_master.h
mb_slave_private.h
mb_tcp.c
mb_tcp_private.h
mb_time_util.h
mb_types.h
mb_util.h
sin_util.c
sin_util.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COPYING	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     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
+state 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU 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.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COPYING.LESSER	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  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 that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU 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 as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,44 @@
+
+default: all
+
+all:  libmb.a libmb.so
+
+OBJ_FILES = mb_ascii.o mb_rtu.o mb_tcp.o mb_master.o mb_slave.o mb_slave_and_master.o sin_util.o
+
+libmb.a:  $(OBJ_FILES)
+	ar cr libmb.a $(OBJ_FILES)
+
+libmb.so: $(OBJ_FILES)
+	gcc -shared -fPIC -o libmb.so $(OBJ_FILES)
+
+clean:
+	-rm -rf *.o libmb.a libmb.so
+
+
+
+# use gcc
+CC = gcc
+
+#get warnings, debugging information and optimization
+CFLAGS  = -Wall -Wpointer-arith -Wstrict-prototypes -Wwrite-strings
+# CFLAGS += -Werror
+CFLAGS += -ggdb -O3 -funroll-loops
+# Note: if the optimizer crashes, we'll leave out the -O3 for those files
+
+# Required for compilation with beremiz, and to create shared object library
+CFLAGS += -fPIC
+
+
+
+#how to make things from other directories if they are missing
+../% /%:
+	$(MAKE) -C $(@D) $(@F)
+
+Makefile.depend depend:
+#	gcc -MM -MG -I$(LLIB) *.c \
+	gcc -MM -MG *.c \
+		| perl -pe 's/:/ Makefile.depend:/' \
+		> Makefile.depend
+
+include Makefile.depend
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2001-2003,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+  Modbus Protocol Libraries
+  =========================
+
+  This directory contains the libararies that implement the modbus protocol
+  stack.
+
+  This protocol has been implemented as a two layer stack. Layer two includes
+  the mb_master and the mb_slave protocols. Layer one is composed of
+  the mb_rtu, mb_ascii and mb_tcp protocols.
+
+  Layer1 protocols all implement the same interface, defined in mb_layer1.h
+  Layer2 protocols implement different interfaces, defined in mb_master.h
+  and mb_slave.h
+
+  Which layer1 protocol that will be used by the program will depend on which 
+  layer1 protocol implementation is linked to the final binary/executable.
+  It is not possible to define during run-time which layer1 protocol is to
+  be used. Each compiled program can only support a single layer1 protocol.
+
+  Users of these libraries should only use functions defined in the layer2
+  protocol header files (i.e. mb_master.h and mb_slave.h)
+
+  If writing a program that will simultaneously be a master and a slave,
+  then only use the mb_slave_and_master.h header file!
+  In this case, do not forget to link the final binary to both the
+  master and slave protocol implementations (as well as the chosen
+  layer1 protocol implementation).
+
+
+
+            ------------------------------------------
+            |                    |                   |
+  layer 2   |    mb_master.h     |    mb_slave.h     |
+            |    mb_master.c     |    mb_slave.c     |
+            |                    |                   |
+            |----------------------------------------|
+            |              mb_layer1.h               |
+  Layer 1   |            |              |            |
+            |  mb_rtu.c  |  mb_ascii.c  |  mb_tcp.c  |
+            |            |              |            |
+            ------------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_addr.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+#ifndef MODBUS_LAYER2_H
+#define MODBUS_LAYER2_H
+
+#include <time.h> /* struct timespec data type */
+
+//#include <sys/socket.h>
+//#include <netinet/in.h>
+#include <netinet/ip.h> /* superset of previous */ // Required for INADDR_ANY
+       
+       
+/* Library Error codes */
+#define PORT_FAILURE   -101
+#define INTERNAL_ERROR -102
+#define TIMEOUT        -103
+#define INVALID_FRAME  -104
+#define MODBUS_ERROR   -105
+
+/* NOTE: Modbus error codes are defined in mb_util.h */
+
+
+
+
+typedef enum {optimize_speed, optimize_size} optimization_t;
+
+
+typedef enum {
+        naf_ascii,
+        naf_rtu,
+        naf_tcp,
+  } node_addr_family_t;
+
+typedef struct {
+        const char *host;
+        const char *service;
+        int         close_on_silence;  
+  } node_addr_tcp_t;
+
+typedef struct {
+        const char *device;
+        int         baud;       /* plain baud rate, eg 2400; zero for the default 9600 */
+        int         parity;     /* 0 for none, 1 for odd, 2 for even                   */
+        int         data_bits;
+        int         stop_bits;
+        int         ignore_echo; /* 1 => ignore echo; 0 => do not ignore echo */
+  } node_addr_rtu_t;
+
+typedef node_addr_rtu_t node_addr_ascii_t;
+
+typedef union {
+        node_addr_ascii_t ascii;
+        node_addr_rtu_t   rtu;
+        node_addr_tcp_t   tcp;
+  } node_addr_common_t;
+
+typedef struct {
+        node_addr_family_t  naf;
+        node_addr_common_t  addr;
+  } node_addr_t;
+
+#endif  /* MODBUS_LAYER2_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_ascii.c	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,1784 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#include <fcntl.h>      /* File control definitions */
+#include <stdio.h>      /* Standard input/output */
+#include <string.h>
+#include <stdlib.h>
+#include <termio.h>     /* POSIX terminal control definitions */
+#include <sys/time.h>   /* Time structures for select() */
+#include <unistd.h>     /* POSIX Symbolic Constants */
+#include <assert.h>
+#include <errno.h>      /* Error definitions */
+#include <ctype.h>
+#include <time.h>       /* clock_gettime()   */
+#include <limits.h>     /* required for INT_MAX */
+
+#include "mb_layer1.h"
+#include "mb_ascii_private.h"
+
+
+/* #define DEBUG */         /* uncomment to see the data sent and received */
+
+
+
+
+/************************************/
+/**                                **/
+/** Include common code...         **/
+/**                                **/
+/************************************/
+
+#include "mb_ds_util.h"    /* data structures... */
+#include "mb_time_util.h"  /* time conversion routines... */
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****                Purpose and Formats                   ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+/*
+
+   This file implements the ascii formating of the modbus protocol.
+   Many values, protocol related, are hardcoded into the code, as it
+   seems very unlikely this code will ever get re-used for anything
+   else but this specific protocol.
+
+   Modbus ASCII frames have no timing restrictions whatsoever, and
+   abide by the following format:
+
+   Header
+   ------
+     size : 1 byte
+     value: ':' (i.e. '\0x3A')
+
+   Body
+   ----
+     size : variable, multiple of 2
+     value: binary data converted to ascii format,
+            i.e. each binary data byte is converted into two ascii characters
+            representing the byte in hexadecimal. Allowable characters are
+            '0' to '9' and 'A' to 'D'
+
+   LRC
+   ---
+     size : 2 bytes
+     value: Longitudinal Redundancy Check of data, excluding any headers, tails,
+            etc...
+
+   Tail
+   ----
+     size : 2 bytes
+     value: 'CR' + 'LF'  (i.e. '\0x0D' + '\0x0A')
+
+
+*/
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****                Forward Declarations                  ****/
+/****                    and Defaults                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+typedef enum {fp_header, fp_body, fp_lrc, fp_tail, fp_done} frame_part_t;
+
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****              Local Utility functions...              ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+/*****************************************/
+/**                                     **/
+/**             lrc functions           **/
+/**                                     **/
+/*****************************************/
+
+
+static inline void lrc_init(u8 *lrc) {
+  *lrc = 0;
+}
+
+static inline void lrc_add_single(u8 *lrc, u8 data) {
+  *lrc += data;
+}
+
+static inline void lrc_add_many(u8 *lrc, u8 *data, int count) {
+  for (; count > 0; count--, *lrc += *data++);
+}
+
+static inline void lrc_end(u8 *lrc) {
+  *lrc = 1 + ~(*lrc);
+}
+
+
+
+/**************************************/
+/**                                  **/
+/**    Initialise a struct termios   **/
+/**                                  **/
+/**************************************/
+static int termios_init(struct termios *tios,
+                        int baud,
+                        int parity,
+                        int data_bits,
+                        int stop_bits) {
+  speed_t baud_rate;
+
+  if (tios == NULL)
+    return -1;
+
+  /* reset all the values... */
+  /* NOTE: the following are initialised later on...
+  tios->c_iflag = 0;
+  tios->c_oflag = 0;
+  tios->c_cflag = 0;
+  tios->c_lflag = 0;
+  */
+  tios->c_line  = 0;
+
+ /* The minimum number of characters that should be received
+  * to satisfy a call to read().
+  */
+  tios->c_cc[VMIN ] = 0;
+
+ /* The maximum inter-arrival interval between two characters,
+  * in deciseconds.
+  *
+  * NOTE: we could use this to detect the end of RTU frames,
+  *       but we prefer to use select() that has higher resolution,
+  *       even though this higher resolution is most probably not
+  *       supported, and the effective resolution is 10ms,
+  *       one tenth of a decisecond.
+  */
+  tios->c_cc[VTIME] = 0;
+
+  /* configure the input modes... */
+  tios->c_iflag =  IGNBRK |  /* ignore BREAK condition on input         */
+                   IGNPAR |  /* ignore framing errors and parity errors */
+                   IXANY;    /* enable any character to restart output  */
+  /*               BRKINT       Only active if IGNBRK is not set.
+   *                            generate SIGINT on BREAK condition,
+   *                            otherwise read BREAK as character \0.
+   *               PARMRK       Only active if IGNPAR is not set.
+   *                            replace bytes with parity errors with
+   *                            \377 \0, instead of \0.
+   *               INPCK        enable input parity checking
+   *               ISTRIP       strip off eighth bit
+   *               IGNCR        ignore carriage return on input
+   *               INLCR        only active if IGNCR is not set.
+   *                            translate newline to carriage return  on input
+   *               ICRNL        only active if IGNCR is not set.
+   *                            translate carriage return to newline on input
+   *               IUCLC        map uppercase characters to lowercase on input
+   *               IXON         enable XON/XOFF flow control on output
+   *               IXOFF        enable XON/XOFF flow control on input
+   *               IMAXBEL      ring bell when input queue is full
+   */
+
+  /* configure the output modes... */
+  tios->c_oflag = OPOST;     /* enable implementation-defined output processing */
+  /*              ONOCR         don't output CR at column 0
+   *              OLCUC         map lowercase characters to uppercase on output
+   *              ONLCR         map NL to CR-NL on output
+   *              OCRNL         map CR to NL on output
+   *              OFILL         send fill characters for a delay, rather than
+   *                            using a timed delay
+   *              OFDEL         fill character is ASCII DEL. If unset, fill
+   *                            character is ASCII NUL
+   *              ONLRET        don't output CR
+   *              NLDLY         NL delay mask. Values are NL0 and NL1.
+   *              CRDLY         CR delay mask. Values are CR0, CR1, CR2, or CR3.
+   *              TABDLY        horizontal tab delay mask. Values are TAB0, TAB1,
+   *                            TAB2, TAB3, or XTABS. A value of XTABS expands
+   *                            tabs to spaces (with tab stops every eight columns).
+   *              BSDLY         backspace delay mask. Values are BS0 or BS1.
+   *              VTDLY         vertical tab delay mask. Values are VT0 or VT1.
+   *              FFDLY         form feed delay mask. Values are FF0 or FF1.
+   */
+
+  /* configure the control modes... */
+  tios->c_cflag = CREAD |    /* enable receiver.               */
+                  CLOCAL;    /* ignore modem control lines     */
+  /*              HUPCL         lower modem control lines after last process
+   *                            closes the device (hang up).
+   *              CRTSCTS       flow control (Request/Clear To Send).
+   */
+       if (data_bits == 5) tios->c_cflag |= CS5;
+  else if (data_bits == 6) tios->c_cflag |= CS6;
+  else if (data_bits == 7) tios->c_cflag |= CS7;
+  else if (data_bits == 8) tios->c_cflag |= CS8;
+  else return -1;
+
+       if (stop_bits == 1) tios->c_cflag &=~ CSTOPB;
+  else if (stop_bits == 2) tios->c_cflag |= CSTOPB;
+  else return -1;
+
+         if(parity == 0) { /* none */
+    tios->c_cflag &=~ PARENB;
+    tios->c_cflag &=~ PARODD;
+  } else if(parity == 2)  { /* even */
+    tios->c_cflag |= PARENB;
+    tios->c_cflag &=~ PARODD;
+  } else if(parity == 1)  { /* odd */
+    tios->c_cflag |= PARENB;
+    tios->c_cflag |= PARODD;
+  } else return -1;
+
+
+  /* configure the local modes... */
+  tios->c_lflag = IEXTEN;    /* enable implementation-defined input processing   */
+  /*              ISIG          when any of the characters INTR, QUIT, SUSP, or DSUSP
+   *                            are received, generate the corresponding signal.
+   *              ICANON        enable canonical mode. This enables the special
+   *                            characters EOF, EOL, EOL2, ERASE, KILL, REPRINT,
+   *                            STATUS, and WERASE, and buffers by lines.
+   *              ECHO          echo input characters.
+   */
+
+  /* Set the baud rate */
+  /* Must be done before reseting all the values to 0! */
+  switch(baud) {
+    case 110:    baud_rate = B110;    break;
+    case 300:    baud_rate = B300;    break;
+    case 600:    baud_rate = B600;    break;
+    case 1200:   baud_rate = B1200;   break;
+    case 2400:   baud_rate = B2400;   break;
+    case 4800:   baud_rate = B4800;   break;
+    case 9600:   baud_rate = B9600;   break;
+    case 19200:  baud_rate = B19200;  break;
+    case 38400:  baud_rate = B38400;  break;
+    case 57600:  baud_rate = B57600;  break;
+    case 115200: baud_rate = B115200; break;
+    default: return -1;
+  } /* switch() */
+
+  if ((cfsetispeed(tios, baud_rate) < 0) ||
+      (cfsetospeed(tios, baud_rate) < 0))
+    return -1;;
+
+  return 0;
+}
+
+
+
+
+
+/*****************************************/
+/**                                     **/
+/**     u8/ascii conversion functions   **/
+/**                                     **/
+/*****************************************/
+
+/* Convert binary data to ascii format.
+ * Both xxx_data_lengths specify the available bytes in
+ * in the respective arrays.
+ */
+/* NOTE: this function is only called from a single location
+ *       so we might just as well make it inline...
+ */
+static inline int bin_2_asc(u8 *bin_data, int bin_data_length,
+                            u8 *asc_data, int asc_data_length) {
+  u8  nibble;
+  int count = 0;
+  u8  *last_bin_data = bin_data + bin_data_length;
+    /* we need L2_TO_ASC_CODING ascii bytes for each bin byte, therefore the
+     * '- (L2_TO_ASC_CODING - 1)'
+     */
+  u8  *last_asc_data = asc_data + asc_data_length - (L2_TO_ASC_CODING - 1);
+
+  while ((bin_data < last_bin_data) &&
+         (asc_data < last_asc_data)) {
+
+    nibble = (*bin_data & 0xF0) >> 4;
+    *(asc_data++) = (nibble <= 9)?nibble + '0':nibble - 10 + 'A';
+
+    nibble = (*bin_data & 0x0F);
+    *(asc_data++) = (nibble <= 9)?nibble + '0':nibble - 10 + 'A';
+
+    count++;
+    bin_data++;
+  }
+
+  /* return number of bytes converted... */
+  return count;
+}
+
+
+/* Convert from lowercase to upper case. */
+/* It probably does not make sense calling the generic toupper()
+ * whose functionality depends on the current locale.
+ * Our own specific function is most probably much faster...
+ */
+static inline u8 local_toupper(u8 val) {
+  if ((val >= 'a') && (val <= 'z'))
+    return val - 'a' + 'A';
+  return val;
+}
+
+/* Convert ascii data to bin format.
+ * *asc_data must be a two byte array.
+ *
+ * If a non-ascii character is found, returns -1
+ */
+/* NOTE: this function is only called from a single location
+ *       so we might just as well make it inline...
+ */
+static inline int asc_2_bin(u8 *asc_data, u8 *bin_data) {
+  if ((isxdigit(asc_data[0]) == 0) ||
+      (isxdigit(asc_data[1]) == 0))
+    return -1;
+
+  asc_data[0] = local_toupper(asc_data[0]);
+  asc_data[1] = local_toupper(asc_data[1]);
+
+  /* hi */ *(bin_data) =  ((asc_data[0] <= '9')?
+                           (asc_data[0] - '0'):(asc_data[0] - 'A' + 10)) * 0x10;
+  /* lo */ *(bin_data) += (asc_data[1] <= '9')?
+                           (asc_data[1] - '0'):(asc_data[1] - 'A' + 10);
+
+  return 0;
+}
+
+
+
+
+/************************************/
+/**                                **/
+/** A data structure - send buffer **/
+/**                                **/
+/************************************/
+
+/* data structure used to store the data to be sent in ascii format. */
+/* The binary data is converted into ascii format before transmission. The
+ * frame is not converted as a single whole, but is rather done in chunks.
+ * The size of the chunks depends on the data size of the send_buffer.
+ *
+ * A lrc variable keeps a tab on the current value of the lrc as the data
+ * is being converted.
+ *
+ * Three special functions add the header, lrc and tail to the ascii frame.
+ */
+
+/* NOTE: The algorithms in the insert functions require a minimum buffer
+ *       size to work correctly...
+ */
+#define SEND_BUF_MIN_LENGTH   ASC_FRAME_MIN_ELE_LENGTH
+
+typedef struct {
+        lb_buf_t data_buf;
+
+        u8 lrc; /* the current value of the lrc, in binary format */
+        } send_buf_t;
+
+/* A small auxiliary function... */
+static inline u8 *send_buf_init(send_buf_t *buf, int size, int max_data_start) {
+  /* The algorithms in other functions require a minimum size
+   * to work correctly...
+   */
+  if (size < SEND_BUF_MIN_LENGTH)
+    return NULL;
+
+  lrc_init(&buf->lrc);
+  return lb_init(&buf->data_buf, size, max_data_start);
+}
+
+/* A small auxiliary function... */
+static inline void send_buf_done(send_buf_t *buf) {
+  lb_done(&buf->data_buf);
+}
+
+/* A small auxiliary function... */
+static inline void send_buf_reset(send_buf_t *buf) {
+  lrc_init(&buf->lrc);
+  lb_data_purge_all(&buf->data_buf);
+}
+
+/* A small auxiliary function... */
+static inline int send_buf_data_count(send_buf_t *buf) {
+  return lb_data_count(&buf->data_buf);
+}
+
+/* A small auxiliary function... */
+static inline int send_buf_free_count(send_buf_t *buf) {
+  return lb_free_count(&buf->data_buf);
+}
+/* A small auxiliary function... */
+static inline u8 *send_buf_data(send_buf_t *buf) {
+  return lb_data(&buf->data_buf);
+}
+
+/* A small auxiliary function... */
+static inline u8 *send_buf_free(send_buf_t *buf) {
+  return lb_free(&buf->data_buf);
+}
+
+/* A small auxiliary function... */
+static inline int send_buf_data_add(send_buf_t *buf, u8 *data, int data_count) {
+  int res = bin_2_asc(data,  data_count, send_buf_free(buf), send_buf_free_count(buf));
+  if (res <=0) return res;
+  lb_data_add(&buf->data_buf, L2_TO_ASC_CODING * res);
+  lrc_add_many(&buf->lrc, data, res);
+  return res;
+}
+
+/* A small auxiliary function... */
+static inline void send_buf_data_purge(send_buf_t *buf, int count) {
+  lb_data_purge(&buf->data_buf, count);
+}
+
+/* A small auxiliary function... */
+static inline void send_buf_data_purge_all(send_buf_t *buf) {
+  lb_data_purge_all(&buf->data_buf);
+}
+
+/* A small auxiliary function... */
+static inline int send_buf_lrc_append(send_buf_t *buf) {
+#if ASC_FRAME_LRC_LENGTH != 2
+#error Code assumes LRC length of 2 bytes, but ASC_FRAME_LRC_LENGTH != 2
+#endif
+  if (lb_free_count(&buf->data_buf) < ASC_FRAME_LRC_LENGTH)
+    return -1;
+  lrc_end(&buf->lrc);
+  bin_2_asc(&buf->lrc, sizeof(buf->lrc),
+            lb_free(&buf->data_buf), ASC_FRAME_LRC_LENGTH);
+  lb_data_add(&buf->data_buf, ASC_FRAME_LRC_LENGTH);
+  return 0;
+}
+
+static inline int send_buf_header_append(send_buf_t *buf) {
+#if ASC_FRAME_HEADER_LENGTH != 1
+#error Code assumes HEADER length of 1 bytes, but ASC_FRAME_HEADER_LENGTH != 1
+#endif
+  if (lb_free_count(&buf->data_buf) < ASC_FRAME_HEADER_LENGTH)
+    return -1;
+
+  /* add the ':' frame header */
+  *lb_free(&buf->data_buf) = ASC_FRAME_HEADER;
+  lb_data_add(&buf->data_buf, ASC_FRAME_HEADER_LENGTH);
+
+  return 0;
+}
+
+static inline int send_buf_tail_append(send_buf_t *buf) {
+#if ASC_FRAME_TAIL_LENGTH != 2
+#error Code assumes TAIL length of 2 bytes, but ASC_FRAME_TAIL_LENGTH != 2
+#endif
+  if (lb_free_count(&buf->data_buf) < ASC_FRAME_TAIL_LENGTH)
+    return -1;
+
+  /* add the CR+LF frame delimiter */
+  lb_free(&buf->data_buf)[0] = ASC_FRAME_TAIL_0;
+  lb_free(&buf->data_buf)[1] = ASC_FRAME_TAIL_1;
+  lb_data_add(&buf->data_buf, ASC_FRAME_TAIL_LENGTH);
+
+  return 0;
+}
+
+
+
+
+/************************************/
+/**                                **/
+/** A data structure - recv buffer **/
+/**                                **/
+/************************************/
+
+/* data structure used to store the data received from the bus. */
+
+/* The ascii data received from the bus is added to the buffer, and is
+ * dynamically converted to binary format. Once a valid frame has been
+ * converted, conversion stops until this valid frame is deleted/purged.
+ */
+
+/* NOTE: The algorithms in the insert functions require a minimum buffer
+ *       size to work correctly...
+ */
+#define RECV_BUF_MIN_LENGTH   ASC_FRAME_MIN_ELE_LENGTH
+
+#define RECV_BUF_BIN_BUF_SIZE (MAX_L2_FRAME_LENGTH +                     \
+                               ASC_FRAME_LRC_LENGTH / L2_TO_ASC_CODING)
+
+typedef struct {
+        lb_buf_t asc_data_buf;
+        u8       bin_data_buf[RECV_BUF_BIN_BUF_SIZE];
+        int      bin_data_free_ofs;
+
+        frame_part_t frame_part;
+        u8 lrc; /* the current value of the lrc, in binary format */
+        u8 lrc_1; /* the previous value of the lrc...             */
+          /* NOTE: We do a running conversion between ascii and binary format,
+           *       i.e. we start converting from ascii to binary before we
+           *       have received the complete ascii frame. This means that
+           *       we also do a running computation of our local version of
+           *       the frame lrc.
+           *       The lrc, transmitted at the end of the ascii frame,
+           *       but before the frame tail, also gets converted to binary
+           *       before we get a chance to realize that it is the lrc value,
+           *       and should therefore not be taken into account when computing
+           *       our local version of the lrc.
+           *       So we keep the previous value of the running lrc, and use
+           *       that to confirm whether we have a valid frame.
+           */
+        } recv_buf_t;
+
+/* A small auxiliary function... */
+static inline u8 *recv_buf_init(recv_buf_t *buf, int size, int max_data_start) {
+  /* The algorithms in other functions require a minimum size
+   * to work correctly...
+   */
+  if (size < RECV_BUF_MIN_LENGTH)
+    return NULL;
+
+  lrc_init(&buf->lrc);
+  buf->bin_data_free_ofs = 0;
+  buf->frame_part = fp_header;
+  return lb_init(&buf->asc_data_buf, size, max_data_start);
+}
+
+/* A small auxiliary function... */
+static inline void recv_buf_done(recv_buf_t *buf) {
+  lb_done(&buf->asc_data_buf);
+}
+
+/* A small auxiliary function... */
+static inline void recv_buf_reset(recv_buf_t *buf) {
+  lrc_init(&buf->lrc);
+  buf->bin_data_free_ofs = 0;
+  buf->frame_part = fp_header;
+  lb_data_purge_all(&buf->asc_data_buf);
+}
+
+/* A small auxiliary function... */
+static inline u8 *recv_buf_data(recv_buf_t *buf) {
+  return lb_data(&buf->asc_data_buf);
+}
+
+/* A small auxiliary function... */
+static inline u8 *recv_buf_free(recv_buf_t *buf) {
+  return lb_free(&buf->asc_data_buf);
+}
+
+/* The function that really does all the conversion work... */
+/* It finds frame limits, converts the data into binary format,
+ * and checks for correct lrc in the received frame.
+ */
+/* NOTE: called indirectly from various locations! Do NOT inline! */
+static void recv_buf_data_parse(recv_buf_t *buf) {
+  int count;
+  u8  *data;
+
+  data  = lb_data(&buf->asc_data_buf);
+  count = lb_data_count(&buf->asc_data_buf);
+
+  /* NOTE: We need at least ASC_FRAME_MIN_ELE_LENGTH bytes to
+   *       to be able to find that element of minimum length
+   */
+  while ((count >= ASC_FRAME_MIN_ELE_LENGTH) && (buf->frame_part != fp_done)) {
+    /* Check for frame header... */
+ /* The following few lines of code assume that ASC_FRAME_HEADER_LENGTH is 1! */
+#if ASC_FRAME_HEADER_LENGTH != 1
+#error The code is written in such a way that can only handle ASC_FRAME_HEADER_LENGTH == 1
+#endif
+    if (data[0] == ASC_FRAME_HEADER) {
+      /* found the beginning of a frame...
+       * Even if we were previously converting a frame without errors,
+       * if we receive a new frame header we discard the previous
+       * frame that went unfinished!
+       */
+      data += ASC_FRAME_HEADER_LENGTH;
+      count -= ASC_FRAME_HEADER_LENGTH;
+      buf->frame_part = fp_body;
+      lrc_init(&buf->lrc);
+      buf->bin_data_free_ofs = 0;
+      continue;
+    }
+
+    /* Check for frame tail... */
+    /*
+     * Note that the while() condition guarantees that we have at least
+     * two ascii bytes to handle.
+     */
+ /* The following few lines of code assume that ASC_FRAME_TAIL_LENGTH is 2! */
+#if ASC_FRAME_TAIL_LENGTH != 2
+#error The code is written in such a way that can only handle ASC_FRAME_TAIL_LENGTH == 2
+#endif
+    if ((data[0] == ASC_FRAME_TAIL_0) &&
+        (data[1] == ASC_FRAME_TAIL_1)) {
+      /* end of binary data...  */
+      data  += ASC_FRAME_TAIL_LENGTH;
+      count -= ASC_FRAME_TAIL_LENGTH;
+
+      /* let's check the lrc... */
+      if (buf->bin_data_free_ofs <= 0)
+        /* we have reached the end of a frame that did not include
+         * any binary data, not even the lrc value itself!
+         */
+        goto frame_error;
+
+      /* Remember that we do not use the most recent lrc value, as this
+       * incorrectly includes the ascii bytes in the frame that code the
+       * frame's lrc. (pls read the note in the recv_but_t typedef)
+       */
+ /* The following few lines of code assume that
+  * (ASC_FRAME_LRC_LENGTH / L2_TO_ASC_CODING) is 1!
+  */
+#if L2_TO_ASC_CODING != ASC_FRAME_LRC_LENGTH
+#error The code is written in such a way that can only handle L2_TO_ASC_CODING == ASC_FRAME_LRC_LENGTH
+#endif
+      lrc_end(&(buf->lrc_1));
+      if (buf->lrc_1 == buf->bin_data_buf[buf->bin_data_free_ofs-1]) {
+        /* we have received a correct frame... */
+        buf->frame_part = fp_done;
+        continue;
+      } else {
+        /* we have found a frame with an lrc error */
+        goto frame_error;
+      }
+    }
+
+    if (buf->frame_part == fp_header) {
+      /* we are searching for beginning of a frame...
+       * but we did not find it in the previous condition!
+       * We continue looking in the next ascii byte
+       */
+      data++;
+      count--;
+      continue;
+      }
+
+    if (buf->frame_part == fp_body) {
+      /* we have previously found the beginning of a frame,
+       * and are now converting the body into binary format...
+       *
+       * Note that the while() condition guarantees that we have at least
+       * two ascii bytes to convert into binary format.
+       */
+
+      /* this is normal data... let's convert... */
+      if (asc_2_bin(data, buf->bin_data_buf + buf->bin_data_free_ofs) < 0)
+        /* error converting from ascii to binary.
+         * This must be due to an invalid ascii character in the ascii data.
+         * We discard the current frame...
+         *
+         * Note that we *do not* increment the data pointer,
+         * nor do we decrement the count variable. One of the ascii bytes could
+         * be the begining of a new valid frame, and we don't want to skip it!
+         */
+        goto frame_error;
+
+      buf->lrc_1 = buf->lrc;
+      lrc_add_single(&(buf->lrc), *(buf->bin_data_buf + buf->bin_data_free_ofs));
+
+      data += L2_TO_ASC_CODING;
+      count -= L2_TO_ASC_CODING;
+      if (++(buf->bin_data_free_ofs) >= RECV_BUF_BIN_BUF_SIZE)
+        /* Whoops, this shouldn't be hapening!
+         * The frame we are receiving is larger than the alocated buffer!
+         * Our only alternative is to discard the frame
+         * we are currently receiving...
+         */
+        goto frame_error;
+
+      continue;
+    }
+
+    /* if none of the above, then it must be some transmission error */
+    /* Actually, the code will never fall through since if we are in this loop,
+     * (frame_part == header) || (frame_part == body) is always true!
+     */
+    data++;
+    count--;
+frame_error:
+    lrc_init(&buf->lrc);
+    buf->bin_data_free_ofs = 0;
+    buf->frame_part = fp_header;
+  } /* while () */
+
+  lb_data_purge(&buf->asc_data_buf, lb_data_count(&buf->asc_data_buf) - count);
+}
+
+/* A small auxiliary function... */
+static inline void recv_buf_search_frame(recv_buf_t *buf) {
+  if (buf->frame_part != fp_done)
+    recv_buf_data_parse(buf);
+}
+
+/* add ascii format data to buffer */
+static inline void recv_buf_data_add(recv_buf_t *buf, int count) {
+  lb_data_add(&buf->asc_data_buf, count);
+}
+
+/* A small auxiliary function... */
+static inline int recv_buf_data_count(recv_buf_t *buf) {
+  return lb_data_count(&buf->asc_data_buf);
+}
+
+/* A small auxiliary function... */
+static inline int recv_buf_free_count(recv_buf_t *buf) {
+  return lb_free_count(&buf->asc_data_buf);
+}
+
+/* Return pointer to frame, if a valid frame is available */
+static inline u8 *recv_buf_frame(recv_buf_t *buf) {
+  recv_buf_search_frame(buf);
+  if (buf->frame_part == fp_done)
+    /* we have found a frame...! */
+    return buf->bin_data_buf;
+
+  /* no frame... */
+  return NULL;
+}
+
+/* Return number of bytes in frame, if a valid frame is available */
+static inline int recv_buf_frame_count(recv_buf_t *buf) {
+  recv_buf_search_frame(buf);
+  if (buf->frame_part == fp_done)
+    /* we have found a frame...! */
+    return buf->bin_data_free_ofs - ASC_FRAME_LRC_LENGTH/L2_TO_ASC_CODING;
+
+  /* no frame... */
+  return -1;
+}
+
+/* Delete valid binary format frame! */
+static inline void recv_buf_frame_purge(recv_buf_t *buf) {
+  /* NOTE: we only delete valid frames!!
+   *       Partially converted frames are not deleted, the
+   *       remaining bytes may be received later!
+   */
+  if (buf->frame_part == fp_done) {
+    buf->frame_part = fp_header;
+    buf->bin_data_free_ofs = 0;
+  }
+}
+
+
+
+/************************************/
+/**                                **/
+/**  A data structure - nd entry   **/
+/**                                **/
+/************************************/
+
+/* NOTE: nd = node descriptor */
+
+typedef struct {
+         /* The file descriptor associated with this node */
+         /* NOTE: if the node is not yet in use, i.e. if the node is free,
+          *       then fd will be set to -1
+          */
+        int    fd;
+        struct timeval time_15_char_;
+
+         /* Modbus ascii frames are delimited by a ':' (colon) at the begining of
+          * a frame, and CR-LF sequence at the end the frame.
+          *
+          * Unless we want to take 'ages' reading the data off the serial line
+          * one byte at a time, we risk reading beyond the boundary of the
+          * frame currently being interpreted. The extra data belongs to the
+          * subsequent frame, and must therefore be buffered to be handled
+          * when the next frame is being interpreted.
+          *
+          * The receive buffer is therefore a static variable.
+          */
+        recv_buf_t recv_buf_;
+
+         /* The send ascii buffer could be a local function variable
+          * instead of a static variable, but we might just as well
+          * allocate the memory at startup and not risk running out
+          * of memory while the module is running.
+          */
+        send_buf_t send_buf_;
+
+          /* The old settings of the serial port, to be reset when the library is closed... */
+        struct termios old_tty_settings_;
+
+          /* ignore echo flag.
+           * If set to 1, then it means that we will be reading every byte we
+           * ourselves write out to the bus, so we must ignore those bytes read
+           * before we really read the data sent by remote nodes.
+           *
+           * This comes in useful when using a RS232-RS485 converter that does
+           * not correctly control the RTS-CTS lines...
+           */
+        int ignore_echo;
+ } nd_entry_t;
+
+
+static inline void nd_entry_init(nd_entry_t *nde) {
+  nde->fd = -1; /* The node is free... */
+}
+
+static int nd_entry_connect(nd_entry_t *nde,
+                            node_addr_t *node_addr,
+                            optimization_t opt) {
+
+  int parity_bits, start_bits, char_bits;
+  struct termios settings;
+  int buf_size;
+
+  /*
+  if (nde == NULL)
+    goto error_exit_0;
+  */
+  if (nde->fd >= 0)
+    goto error_exit_0;
+
+  /* initialise the termios data structure */
+  if (termios_init(&settings,
+                   node_addr->addr.ascii.baud,
+                   node_addr->addr.ascii.parity,
+                   node_addr->addr.ascii.data_bits,
+                   node_addr->addr.ascii.stop_bits)
+      < 0) {
+#ifdef DEBUG
+    fprintf(stderr,   "Invalid serial line settings"
+                      "(baud=%d, parity=%d, data_bits=%d, stop_bits=%d)",
+                      node_addr->addr.ascii.baud,
+                      node_addr->addr.ascii.parity,
+                      node_addr->addr.ascii.data_bits,
+                      node_addr->addr.ascii.stop_bits);
+#endif
+    goto error_exit_1;
+  }
+
+    /* set the ignore_echo flag */
+  nde->ignore_echo = node_addr->addr.ascii.ignore_echo;
+
+    /* initialise send buffer */
+  buf_size = (opt == optimize_size)?SEND_BUFFER_SIZE_SMALL:
+                                    SEND_BUFFER_SIZE_LARGE;
+  if (send_buf_init(&nde->send_buf_, buf_size, buf_size - SEND_BUF_MIN_LENGTH)
+      == NULL) {
+#ifdef DEBUG
+    fprintf(stderr, "Out of memory: error initializing send buffer");
+#endif
+    goto error_exit_1;
+  }
+
+    /* initialise recv buffer */
+  buf_size = (opt == optimize_size)?RECV_BUFFER_SIZE_SMALL:
+                                    RECV_BUFFER_SIZE_LARGE;
+  if (recv_buf_init(&nde->recv_buf_, buf_size, buf_size - RECV_BUF_MIN_LENGTH)
+      == NULL) {
+#ifdef DEBUG
+    fprintf(stderr, "Out of memory: error initializing receive buffer");
+#endif
+    goto error_exit_2;
+  }
+
+    /* open the serial port */
+  if((nde->fd = open(node_addr->addr.ascii.device, O_RDWR | O_NOCTTY | O_NDELAY))
+     < 0) {
+#ifdef DEBUG
+    fprintf(stderr, "Error opening device %s (errno=%d)",
+                     node_addr->addr.ascii.device, errno);
+#endif
+    goto error_exit_3;
+  }
+
+  if(tcgetattr(nde->fd, &nde->old_tty_settings_) < 0) {
+#ifdef DEBUG
+    fprintf(stderr, "Error reading device's %s original settings.",
+                      node_addr->addr.ascii.device);
+#endif
+    goto error_exit_4;
+  }
+
+  if(tcsetattr(nde->fd, TCSANOW, &settings) < 0) {
+#ifdef DEBUG
+    fprintf(stderr, "Error configuring device %s"
+                    "(baud=%d, parity=%d, data_bits=%d, stop_bits=%d)",
+                    node_addr->addr.ascii.device,
+                    node_addr->addr.ascii.baud,
+                    node_addr->addr.ascii.parity,
+                    node_addr->addr.ascii.data_bits,
+                    node_addr->addr.ascii.stop_bits);
+#endif
+    goto error_exit_4;
+  }
+
+  parity_bits   = (node_addr->addr.ascii.parity == 0)?0:1;
+  start_bits    = 1;
+  char_bits     = start_bits  + node_addr->addr.ascii.data_bits +
+                  parity_bits + node_addr->addr.ascii.stop_bits;
+/*  time_35_char_ = d_to_timeval(3.5*char_bits/baud); */
+  nde->time_15_char_ = d_to_timeval(1.5*char_bits/node_addr->addr.ascii.baud);
+
+#ifdef DEBUG
+  printf("nd_entry_connect(): %s open\n", node_addr->addr.ascii.device );
+  printf("nd_entry_connect(): returning fd=%d\n", nde->fd);
+#endif
+  return nde->fd;
+
+  error_exit_4:
+    close(nde->fd);
+  error_exit_3:
+    recv_buf_done(&nde->recv_buf_);
+  error_exit_2:
+    send_buf_done(&nde->send_buf_);
+  error_exit_1:
+    nde->fd = -1; /* set the node as free... */
+  error_exit_0:
+    return -1;
+}
+
+
+
+static int nd_entry_free(nd_entry_t *nde) {
+  if (nde->fd < 0)
+    /* already free */
+    return -1;
+
+  /* reset the tty device old settings... */
+  if(tcsetattr(nde->fd, TCSANOW, &nde->old_tty_settings_) < 0)
+    fprintf(stderr, "Error reconfiguring serial port to it's original settings.");
+
+  recv_buf_done(&nde->recv_buf_);
+  send_buf_done(&nde->send_buf_);
+  close(nde->fd);
+  nde->fd = -1;
+
+  return 0;
+}
+
+
+static inline int nd_entry_is_free(nd_entry_t *nde) {
+  return (nde->fd < 0);
+}
+
+
+
+
+/************************************/
+/**                                **/
+/**  A data structure - nd table   **/
+/**                                **/
+/************************************/
+
+typedef struct {
+      /* the array of node descriptors, and current size... */
+    nd_entry_t *node;
+    int        node_count;      /* total number of nodes in the node[] array */
+} nd_table_t;
+
+
+
+#if 1
+/* nd_table_init()
+ *   Version 1 of the nd_table_init() function. 
+ *   If called more than once, 2nd and any subsequent calls will
+ *   be interpreted as a request to confirm that it was already correctly 
+ *   initialized with the requested number of nodes.
+ */
+static int nd_table_init(nd_table_t *ndt, int nd_count) {
+  int count;
+
+  if (ndt->node != NULL) {
+    /* this function has already been called, and the node table is already initialised */
+    return (ndt->node_count == nd_count)?0:-1;
+  }
+
+  /* initialise the node descriptor metadata array... */
+  ndt->node = malloc(sizeof(nd_entry_t) * nd_count);
+  if (ndt->node == NULL) {
+#ifdef DEBUG
+    fprintf(stderr, "Out of memory: error initializing node address buffer");
+#endif
+    return -1;
+  }
+  ndt->node_count = nd_count;
+
+    /* initialise the state of each node in the array... */
+  for (count = 0; count < ndt->node_count; count++) {
+    nd_entry_init(&ndt->node[count]);
+  } /* for() */
+
+  return nd_count; /* number of succesfully created nodes! */
+}
+#else
+/* nd_table_init()
+ *   Version 2 of the nd_table_init() function. 
+ *   If called more than once, 2nd and any subsequent calls will
+ *   be interpreted as a request to reserve an extra new_nd_count
+ *   number of nodes. This will be done using realloc().
+ */
+static int nd_table_init(nd_table_t *ndt, int new_nd_count) {
+  int count;
+
+  /* initialise the node descriptor metadata array... */
+  ndt->node = realloc(ndt->node, sizeof(nd_entry_t) * (ndt->node_count + new_nd_count));
+  if (ndt->node == NULL) {
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "Out of memory: error initializing node address buffer\n");
+#endif
+    return -1;
+  }
+
+  /* initialise the state of each newly added node in the array... */
+  for (count = ndt->node_count; count < ndt->node_count + new_nd_count; count++) {
+    nd_entry_init(&ndt->node[count]);
+  } /* for() */
+  ndt->node_count += new_nd_count;
+
+  return new_nd_count; /* number of succesfully created nodes! */
+}
+#endif
+
+
+static inline nd_entry_t *nd_table_get_nd(nd_table_t *ndt, int nd) {
+  if ((nd < 0) || (nd >= ndt->node_count))
+    return NULL;
+
+  return &ndt->node[nd];
+}
+
+
+static inline void nd_table_done(nd_table_t *ndt) {
+  int i;
+
+  if (ndt->node == NULL) 
+    return;
+
+    /* close all the connections... */
+  for (i = 0; i < ndt->node_count; i++)
+    nd_entry_free(&ndt->node[i]);
+
+  /* Free memory... */
+  free(ndt->node);
+  *ndt = (nd_table_t){.node=NULL, .node_count=0};
+}
+
+
+
+static inline int nd_table_get_free_nd(nd_table_t *ndt) {
+  int count;
+
+  for (count = 0; count < ndt->node_count; count++) {
+    if (nd_entry_is_free(&ndt->node[count]))
+      return count;
+  }
+
+   /* none found... */
+  return -1;
+}
+
+
+static inline int nd_table_free_nd(nd_table_t *ndt, int nd) {
+  if ((nd < 0) || (nd >= ndt->node_count))
+    return -1;
+
+  return nd_entry_free(&ndt->node[nd]);
+}
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****                Global Library State                  ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+ /* The node descriptor table... */
+ /* NOTE: This variable must be correctly initialised here!! */
+static nd_table_t nd_table_ = {.node=NULL, .node_count=0};
+
+ /* The optimization choice... */
+static optimization_t optimization_;
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****             Sending of Modbus ASCII Frames           ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+/* 
+ *     NOTE: for now the transmit_timeout is silently ignored in ASCII version!
+ */
+int modbus_ascii_write(int    nd,
+                       u8    *data,
+                       size_t data_length,
+                       u16    transaction_id,
+                       const struct timespec *transmit_timeout
+                      )
+ {
+  fd_set rfds;
+  struct timeval timeout;
+  int res, bin_data_conv, send_retries;
+  frame_part_t frame_part;
+  nd_entry_t *nd_entry;
+
+  /* check if nd is correct... */
+  if ((nd_entry = nd_table_get_nd(&nd_table_, nd)) == NULL)
+    return -1;
+
+  /* check if nd is initialzed... */
+  if (nd_entry->fd < 0)
+    return -1;
+
+  /* THE MAIN LOOP!!! */
+  send_retries = ASC_FRAME_SEND_RETRY + 1; /* must try at least once... */
+  while (send_retries > 0) {
+
+    /*******************************
+     * synchronise with the bus... *
+     *******************************/
+    /* Remember that a RS485 bus is half-duplex, so we have to wait until
+     * nobody is transmitting over the bus for our turn to transmit.
+     * This will never happen on a modbus network if the master and
+     * slave state machines never get out of synch (granted, it probably
+     * only has two states, but a state machine nonetheless), but we want
+     * to make sure we can re-synchronise if they ever do get out of synch.
+     *
+     * The following lines are an attempt at re-synchronising with the bus.
+     * Unfortunately, due to the completely asynchronous nature of the
+     * modbus-ascii protocol (there are no time boundaries for sending a frame!),
+     * it is impossible to guarantee that we will synchronise correctly.
+     *
+     * Use of RTS/CTS over a half-duplex coms chanel would eliminate this
+     * 'feature', but not all RS232/RS485 converters delay activating the
+     * CTS signal, even though there may be current activity on the bus.
+     *
+     * We first wait until the bus is silent for at least 1.5 character interval.
+     * Note that we only get feedback from the device driver once a whole byte
+     * has been received, so we must wait longer than 1 character interval to make
+     * sure there is no current activity on the bus). We then flush the input and
+     * output queues.
+     */
+      /* NOTES:
+       *   - we do not need to reset the rfds with FD_SET(ttyfd, &rfds)
+       *     before every call to select! We only wait on one file descriptor,
+       *     so if select returns succesfully, it must have that same file
+       *     decriptor set in the rdfs!
+       *     If select returns with a timeout, then we do not get to call
+       *     select again!
+       *   - We do not reset the timeout value. Normally this value is left
+       *     unchanged when select() returns, so we will be witing for longer
+       *     than the desired period.
+       *     On Linux the timeout is changed to reflect the time remaining.
+       *     In this case, things will work more nicely.
+       */
+    FD_ZERO(&rfds);
+    FD_SET(nd_entry->fd, &rfds);
+    timeout = nd_entry->time_15_char_;
+    while ((res = select(nd_entry->fd+1, &rfds, NULL, NULL, &timeout)) != 0) {
+      if (res > 0) {
+        /* we are receiving data over the serial port! */
+        /* Throw the data away!                        */
+        tcflush(nd_entry->fd, TCIFLUSH); /* flush the input stream */
+        /* reset the timeout value! */
+        timeout = nd_entry->time_15_char_;
+      } else {
+        /* some kind of error ocurred */
+        if (errno != EINTR)
+          /* we were not interrupted by a signal */
+          return -1;
+        /* We will be callind select() again.
+         * We need to reset the FD SET !
+         */
+        FD_ZERO(&rfds);
+        FD_SET(nd_entry->fd, &rfds);
+      }
+    } /* while (select()) */
+
+    /* Flush both input and output streams... */
+    /* NOTE: Due to the nature of the modbus protocol,
+     *       when a frame is sent all previous
+     *       frames that may have arrived at the sending node become
+     *       irrelevant.
+     */
+    tcflush(nd_entry->fd, TCIOFLUSH);    /* flush the input & output streams */
+    recv_buf_reset(&nd_entry->recv_buf_);   /* reset the recv buffer            */
+
+    /**********************
+     * write to output... *
+     **********************/
+    send_buf_reset(&nd_entry->send_buf_);
+
+    frame_part = fp_header; /* start off by sending the header... */
+    bin_data_conv = 0; /* binary format data already converted to ascii format... */
+    while ((frame_part != fp_done) ||
+           (send_buf_data_count(&nd_entry->send_buf_) > 0)) {
+
+       /* build the frame we will send over the wire... */
+       /* We use a state machine with four states... */
+      if (frame_part == fp_header) {
+        if (send_buf_header_append(&nd_entry->send_buf_) >= 0)
+          frame_part = fp_body;
+      }
+      if (frame_part == fp_body) {
+        res = send_buf_data_add(&nd_entry->send_buf_, data + bin_data_conv, data_length - bin_data_conv);
+        bin_data_conv += res;
+        if (bin_data_conv == data_length)
+          frame_part = fp_lrc;
+      }
+      if (frame_part == fp_lrc) {
+        if (send_buf_lrc_append(&nd_entry->send_buf_) >= 0)
+          frame_part = fp_tail;
+      }
+      if (frame_part == fp_tail) {
+        if (send_buf_tail_append(&nd_entry->send_buf_) >= 0)
+          frame_part = fp_done;
+      }
+
+       /* send the frame... */
+      if ((res = write(nd_entry->fd,
+                       send_buf_data(&nd_entry->send_buf_),
+                       send_buf_data_count(&nd_entry->send_buf_))) < 0) {
+        if ((errno != EAGAIN ) && (errno != EINTR )) {
+          break;
+        } else {
+          /* there is no harm if we get interrupted while writing the frame.
+           * The ascii version of modbus does not place any timing
+           * constraints whatsoever...
+           *
+           * We simply continue sending the frame...
+           */
+          res = 0;
+        }
+      }
+#ifdef DEBUG
+/* Print each character that has just been sent on the bus */
+  { int i;
+    printf("bytes sent -> [");
+    for(i = 0; i < res; i++)
+      printf("%c", send_buf_data(&nd_entry->send_buf_)[i]);
+    printf("]\n");
+  }
+#endif
+      send_buf_data_purge(&nd_entry->send_buf_, res);
+
+      if ((frame_part == fp_done) &&
+          (send_buf_data_count(&nd_entry->send_buf_) == 0))
+        /* sent frame successfully! */
+        return data_length;
+
+    } /* while(frame_not_sent) */
+    /* NOTE: Some error occured while trying to transmit. It will probably
+     *       not go away by itself, but there is no harm in trying again
+     *       if the upper layer so wishes...
+     */
+    send_retries--;
+  } /* while()  MAIN LOOP */
+
+   /* maximum retries exceeded */
+  return -1;
+}
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****             Receiving Modbus ASCII Frames            ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+/************************************/
+/**                                **/
+/**          Read a frame          **/
+/**                                **/
+/************************************/
+
+/* A function to read a valid frame off the modbus ascii bus.
+ *
+ * NOTES:
+ *        - The returned frame is guaranteed to be a valid frame.
+ *        - The returned length does *not* include the LRC.
+ */
+/* NOTE: This function is only called from one place in the rest of the code,
+ * so we might just as well make it inline...
+ */
+/* RETURNS: number of bytes in received frame
+ *          -1 on read file error
+ *          -2 on timeout
+ */
+static inline int read_frame(nd_entry_t *nd_entry,
+                             u8 **recv_data_ptr,
+                             struct timespec *end_time)
+{
+  /* temporary variables... */
+  fd_set rfds;
+  int res;
+
+  /* start by deleting any previously converted frames... */
+  recv_buf_frame_purge(&nd_entry->recv_buf_);
+
+  /*==============*
+   * read a frame *
+   *==============*/
+#ifdef DEBUG
+  printf("bytes read -> <");
+#endif
+  /* The main loop that reads one frame               */
+  /*  (multiple calls to read() )                     */
+  /* and jumps out as soon as it finds a valid frame. */
+  /* NOTE: Do not change this into a do {...} until() loop!
+   *       The while loop may find valid frames in the data read off the
+   *       bus in previous invocations of this same fucntion, and may
+   *       therefore not execute any loop iteration whatsoever,
+   *       and not call read()
+   */
+  while ((*recv_data_ptr = recv_buf_frame(&nd_entry->recv_buf_)) == NULL) {
+    /* We need more bytes... */
+
+    /*-----------------------------*
+     * check for data availability *
+     *-----------------------------*/
+    /* if we can't find a valid frame in the existing data, or no data
+     * was left over, then we need to read more bytes!
+     */
+      FD_ZERO(&rfds);
+      FD_SET(nd_entry->fd, &rfds);
+      {int sel_res = my_select(nd_entry->fd + 1, &rfds, NULL, end_time);
+        if (sel_res < 0)
+          return -1;
+        if (sel_res == 0)
+          return -2;
+      }
+
+    /*------------------*
+     * read frame bytes *
+     *------------------*/
+     /* Read in as many bytes as possible...  */
+    res = read(nd_entry->fd,
+               recv_buf_free(&nd_entry->recv_buf_),
+               recv_buf_free_count(&nd_entry->recv_buf_));
+    if (res < 0) {
+      if (errno != EINTR)
+        return -1;
+      else
+        res = 0;
+    }
+#ifdef DEBUG
+      {/* display the hex code of each character received */
+        int i;
+        for (i=0; i < res; i++)
+          printf("%c", recv_buf_free(&nd_entry->recv_buf_)[i]);
+      }
+#endif
+    recv_buf_data_add(&nd_entry->recv_buf_, res);
+  } /* while ()*/
+#ifdef DEBUG
+    printf(">\n");
+#endif
+
+  /* We have a frame! */
+  return recv_buf_frame_count(&nd_entry->recv_buf_);
+}
+
+
+
+
+
+/**************************************/
+/**                                  **/
+/**    Read a Modbus ASCII frame     **/
+/**                                  **/
+/**************************************/
+
+/* The public function that reads a valid modbus frame.
+ *
+ * The returned frame is guaranteed to be different to the
+ * the frame stored in send_data, and to start with the
+ * same slave address stored in send_data[0].
+ *
+ * If send_data is NULL, send_data_length = 0, or
+ * ignore_echo == 0, then the first valid frame read off
+ * the bus is returned.
+ *
+ * return value: The length (in bytes) of the valid frame,
+ *               -1 on error
+ *               -2 on timeout
+ */
+
+int modbus_ascii_read(int *nd,
+                      u8 **recv_data_ptr,
+                      u16 *transaction_id,
+                      const u8 *send_data,
+                      int send_length,
+                      const struct timespec *recv_timeout) {
+
+  struct timespec end_time, *ts_ptr;
+  int res, recv_length;
+  int iter;  /* Warning: if you change the var type of iter from int,
+              *          then you must also change the INT_MAX constant
+              *          further on in this function...
+              */
+  u8 *local_recv_data_ptr;
+  nd_entry_t *nd_entry;
+
+  /* Check input parameters... */
+  if (nd == NULL)
+    return -1;
+
+  if (recv_data_ptr == NULL)
+    recv_data_ptr = &local_recv_data_ptr;
+
+  if ((send_data == NULL) && (send_length != 0))
+    return -1;
+
+  /* check if nd is correct... */
+  if ((nd_entry = nd_table_get_nd(&nd_table_, *nd)) == NULL)
+    return -1;
+
+  /* check if nd is initialzed... */
+  if (nd_entry->fd < 0)
+    return -1;
+
+  /* We will potentially read many frames, and we cannot reset the timeout
+   * for every frame we read. We therefore determine the absolute time_out,
+   * and use this as a parameter for each call to read_frame() instead of
+   * using a relative timeout.
+   *
+   * NOTE: see also the timeout related comment in the read_frame()= function!
+   */
+  ts_ptr = NULL;
+  if (recv_timeout != NULL) {
+     ts_ptr = &end_time;
+    *ts_ptr = timespec_add_curtime(*recv_timeout);
+  }
+
+  /* NOTE: When using a half-duplex RS-485 bus, some (most ?) RS232-485
+   *       converters will send back to the RS232 port whatever we write,
+   *       so we will read in whatever we write out onto the bus.
+   *       We will therefore have to compare
+   *       the first frame we read with the one we sent. If they are
+   *       identical it is because we are in fact working on a RS-485
+   *       bus and must therefore read in a second frame which will be
+   *       the true response to our query.
+   *       If the first frame we receive is different to the last frame we
+   *       just sent, then we are *not* working on a RS-485 bus, and
+   *       that is already the real response to our query.
+   *
+   *       Flushing the input cache immediately *after* sending a frame
+   *       could solve this issue, but we have no guarantee that this
+   *       process would not get swapped out between the write() and
+   *       flush() calls, and we could therefore be flushing the response
+   *       frame along with the last frame we sent!
+   */
+
+  iter = 0;
+  while ((res = recv_length = read_frame(nd_entry, recv_data_ptr, ts_ptr)) >= 0) {
+    if (iter < INT_MAX) iter++;
+
+    if ((send_length <= 0) || (nd_entry->ignore_echo == 0))
+      /* any valid frame will do... */
+      return recv_length;
+
+    if ((send_length > L2_FRAME_SLAVEID_OFS + 1) && (iter == 1))
+     /* We have a frame in send_data,
+      * so we must make sure we are not reading in the frame just sent...
+      *
+      * We must only do this for the first frame we read. Subsequent
+      * frames are guaranteed not to be the previously sent frame
+      * since the modbus_ascii_write() resets the recv buffer.
+      * Remember too that valid modbus responses may be exactly the same
+      * as the request frame!!
+      */
+      if (recv_length == send_length)
+        if (memcmp(*recv_data_ptr, send_data, recv_length) == 0)
+          /* recv == send !!! */
+          /* read in another frame. */
+          continue;
+
+    /* The frame read is either:
+     *  - different to the frame in send_data
+     *  - or there is only the slave id in send_data[L2_FRAME_SLAVEID_OFS]
+     *  - or both of the above...
+     */
+    if (send_length > L2_FRAME_SLAVEID_OFS)
+      if (recv_length > L2_FRAME_SLAVEID_OFS)
+        /* check that frame is from/to the correct slave... */
+        if ((*recv_data_ptr)[L2_FRAME_SLAVEID_OFS] == send_data[L2_FRAME_SLAVEID_OFS])
+          /* yep, it is... */
+          return recv_length;
+
+    /* The frame we have received is not acceptable...
+     * Let's read a new frame.
+     */
+  } /* while(...) */
+
+  /* error reading response! */
+  /* Return the error returned by read_frame! */
+  return res;
+}
+
+
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****        Initialising and Shutting Down Library        ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+/******************************/
+/**                          **/
+/**   Load Default Values    **/
+/**                          **/
+/******************************/
+
+static void set_defaults(int *baud,
+                         int *parity,
+                         int *data_bits,
+                         int *stop_bits) {
+  /* Set the default values, if required... */
+  if (*baud == 0)
+    *baud = DEF_BAUD_RATE;
+  if (*data_bits == 0)
+    *data_bits = DEF_DATA_BITS;
+  if (*stop_bits == 0) {
+    if (*parity == 0)
+      *stop_bits = DEF_STOP_BITS_NOP; /* no parity */
+    else
+      *stop_bits = DEF_STOP_BITS_PAR; /* parity used */
+  }
+}
+
+
+
+
+
+/******************************/
+/**                          **/
+/**    Initialise Library    **/
+/**                          **/
+/******************************/
+
+int modbus_ascii_init(int nd_count,
+                      optimization_t opt,
+                      int *extra_bytes)
+{
+#ifdef DEBUG
+  printf("modbus_asc_init(): called...\n");
+  printf("creating %d node descriptors\n", nd_count);
+  if (opt == optimize_speed)
+    printf("optimizing for speed\n");
+  if (opt == optimize_size)
+    printf("optimizing for size\n");
+#endif
+
+    /* check input parameters...*/
+  if (0 == nd_count) {
+    if (extra_bytes != NULL)
+      // Not the corect value for this layer. 
+      // What we set it to in case this layer is not used!
+      *extra_bytes = 0; 
+    return 0;
+  }
+  if (nd_count <= 0)
+    goto error_exit_0;
+
+  if (extra_bytes == NULL)
+    goto error_exit_0;
+
+    /* initialise nd table... */
+  if (nd_table_init(&nd_table_, nd_count) < 0)
+    goto error_exit_0;
+
+    /* remember the optimization choice for later reference... */
+  optimization_ = opt;
+
+#ifdef DEBUG
+  printf("modbus_asc_init(): returning succesfuly...\n");
+#endif
+  return 0;
+
+error_exit_0:
+  if (extra_bytes != NULL)
+    *extra_bytes = 0; // The value we set this to in case of error.
+  return -1;
+}
+
+
+
+/******************************/
+/**                          **/
+/**    Open node descriptor  **/
+/**                          **/
+/******************************/
+
+/* Open a node for master or slave operation.
+ * Returns the node descriptor, or -1 on error.
+ *
+ * This function is mapped onto both
+ * modbus_connect() and modbus_listen()
+ */
+int modbus_ascii_connect(node_addr_t node_addr) {
+  int node_descriptor;
+  nd_entry_t *nd_entry;
+
+#ifdef DEBUG
+  printf("modbus_ascii_connect(): called...\n");
+  printf("opening %s\n", node_addr.addr.ascii.device);
+  printf("baud_rate = %d\n", node_addr.addr.ascii.baud);
+  printf("parity = %d\n", node_addr.addr.ascii.parity);
+  printf("data_bits = %d\n", node_addr.addr.ascii.data_bits);
+  printf("stop_bits = %d\n", node_addr.addr.ascii.stop_bits);
+  printf("ignore_echo = %d\n", node_addr.addr.ascii.ignore_echo);
+#endif
+
+  /* Check for valid address family */
+  if (node_addr.naf != naf_ascii)
+    /* wrong address type... */
+    goto error_exit_0;
+
+  /* find a free node descriptor */
+  if ((node_descriptor = nd_table_get_free_nd(&nd_table_)) < 0)
+    /* if no free nodes to initialize, then we are finished... */
+    goto error_exit_0;
+  if ((nd_entry = nd_table_get_nd(&nd_table_, node_descriptor)) == NULL)
+    /* strange, this should not occur... */
+    goto error_exit_0;
+
+  /* set the default values... */
+  set_defaults(&(node_addr.addr.ascii.baud),
+               &(node_addr.addr.ascii.parity),
+               &(node_addr.addr.ascii.data_bits),
+               &(node_addr.addr.ascii.stop_bits));
+
+  if (nd_entry_connect(nd_entry, &node_addr, optimization_) < 0)
+    goto error_exit_0;
+
+#ifdef DEBUG
+  printf("modbus_ascii_connect(): %s open\n", node_addr.addr.ascii.device );
+  printf("modbus_ascii_connect(): returning nd=%d\n", node_descriptor);
+#endif
+  return node_descriptor;
+
+  error_exit_0:
+    return -1;
+}
+
+
+
+int modbus_ascii_listen(node_addr_t node_addr) {
+  return modbus_ascii_connect(node_addr);
+}
+
+
+
+/******************************/
+/**                          **/
+/**   Close node descriptor  **/
+/**                          **/
+/******************************/
+
+int modbus_ascii_close(int nd) {
+  return nd_table_free_nd(&nd_table_, nd);
+}
+
+
+
+/******************************/
+/**                          **/
+/**    Shutdown Library      **/
+/**                          **/
+/******************************/
+
+int modbus_ascii_done(void) {
+  nd_table_done(&nd_table_);
+  return 0;
+}
+
+
+
+
+
+/******************************/
+/**                          **/
+/**                          **/
+/**                          **/
+/******************************/
+int modbus_ascii_silence_init(void) {
+  return 0;
+}
+
+
+
+
+/******************************/
+/**                          **/
+/**                          **/
+/**                          **/
+/******************************/
+
+
+double modbus_ascii_get_min_timeout(int baud,
+                                    int parity,
+                                    int data_bits,
+                                    int stop_bits) {
+  int parity_bits, start_bits, char_bits;
+
+  set_defaults(&baud, &parity, &data_bits, &stop_bits);
+  parity_bits = (parity == 0)?0:1;
+  start_bits  = 1;
+  char_bits   = start_bits + data_bits + parity_bits + stop_bits;
+  return (double)((MAX_ASC_FRAME_LENGTH * char_bits) / baud);
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_ascii_private.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#ifndef MODBUS_ASCII_PRIVATE_H
+#define MODBUS_ASCII_PRIVATE_H
+
+
+
+#include "mb_util.h"
+
+
+/* serial port default configuration... */
+#define DEF_DATA_BITS 7
+#define DEF_STOP_BITS_PAR 1 /* default stop bits if parity is used     */
+#define DEF_STOP_BITS_NOP 2 /* default stop bits if parity is not used */
+#define DEF_BAUD_RATE 9600
+
+
+/* Send retries of ascii frames... */
+#define ASC_FRAME_SEND_RETRY 0
+  /* NOTES:
+   *  - the above are the retries at the layer1 level,
+   *    higher layers may decide to retry for themselves!
+   *  - For ascii frames, it doesn't make much sense to retry
+   *    if the first try failed...
+   */
+
+
+/* Buffer sizes... */
+#define SEND_BUFFER_SIZE_SMALL  (MAX_ASC_FRAME_LENGTH / 2)
+#define SEND_BUFFER_SIZE_LARGE  (MAX_ASC_FRAME_LENGTH)
+#define RECV_BUFFER_SIZE_SMALL  (MAX_ASC_FRAME_LENGTH / 2)
+#define RECV_BUFFER_SIZE_LARGE  (MAX_ASC_FRAME_LENGTH)
+
+
+/* Frame lengths... */
+
+/* The smallest element in an ascii frame.
+ * This is used later to define the smallest value of certain buffers...
+ */
+#define ASC_FRAME_MIN_ELE_LENGTH   ASC_FRAME_HEADER_LENGTH
+
+#if     ASC_FRAME_MIN_ELE_LENGTH < ASC_FRAME_TAIL_LENGTH
+#undef  ASC_FRAME_MIN_ELE_LENGTH
+#define ASC_FRAME_MIN_ELE_LENGTH   ASC_FRAME_TAIL_LENGTH
+#endif
+
+#if     ASC_FRAME_MIN_ELE_LENGTH < ASC_FRAME_LRC_LENGTH
+#undef  ASC_FRAME_MIN_ELE_LENGTH
+#define ASC_FRAME_MIN_ELE_LENGTH   ASC_FRAME_LRC_LENGTH
+#endif
+
+#if     ASC_FRAME_MIN_ELE_LENGTH < L2_TO_ASC_CODING
+#undef  ASC_FRAME_MIN_ELE_LENGTH
+#define ASC_FRAME_MIN_ELE_LENGTH   L2_TO_ASC_CODING
+#endif
+
+
+#endif  /* MODBUS_ASCII_PRIVATE_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_ds_util.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+ /* Data structures used by the modbus protocols... */
+
+
+#ifndef __MODBUS_DS_UTIL_H
+#define __MODBUS_DS_UTIL_H
+
+
+#include "mb_types.h"  /* get the data types */
+
+/**************************************/
+/**                                  **/
+/** A data structure - linear buffer **/
+/**                                  **/
+/**************************************/
+
+/* An unbounded FIFO data structure.
+ *
+ * The user/caller writes and reads directly from the data structure's buffer,
+ * which eliminates slow copying of bytes between the user's and the structure's
+ * local memory.
+ *
+ * The data structure stores the current data linearly in a single memory array,
+ * i.e. the current data is stored from start to finish from a low address
+ * to a high address, and does *not* circle back to the bottom of the address
+ * space as is usual in a circular buffer. This allows the user/caller to
+ * pass the structure's own byte array on to other functions such as
+ * read() and write() for file operations.
+ *
+ * The FIFO is implemented by allocating more memory than the maximum number
+ * of bytes it will ever hold, and using the extra bytes at the top of the
+ * array as the bottom data bytes are released. When we run out of extra bytes,
+ * (actually, when the number of un-used bytes at the beginning is larger than
+ * a configured maximum), the whole data is moved down, freeing once again the
+ * extra bytes at the top of the array.
+ *
+ * Remember that we can optimize the data structure so that whenever it becomes
+ * empty, we can reset it to start off at the bottom of the byte array, i.e. we
+ * can set the start = end = 0; instead of simply setting the start = end, which
+ * may point to any position in the array.
+ *
+ * Taking the above into consideration, it would probably be a little more efficient
+ * to implement it as a circular buffer with an additional linearize() function
+ * the user could call whenever (s)he required the data to be stored linearly.
+ * Nevertheless, since it has already been implemented as a linear buffer, and since
+ * under normal circumstances the start and end pointers will be reset to 0 quite
+ * often (and therefore we get no additional benefit under normal circumstances),
+ * we will leave it as it is for the time being...
+ *
+ *
+ * The user is expected to call
+ *   lb_init() -> to initialize the structure
+ *   lb_done() -> to free the data structure's memory
+ *
+ * The user can store data starting off from...
+ *   lb_free() -> pointer to address of free memory
+ *   lb_free_count() -> number of free bytes available
+ * and then call
+ *   lb_data_add()
+ * to add the data to the data structure
+ *
+ * Likewise, the user can read the data directly from
+ *   lb_data() -> pointer to address of data location
+ *   lb_free_count() -> number of data bytes available
+ * and free the data using
+ *   lb_data_purge()
+ * to remove the data from the data structure
+ */
+
+
+typedef struct {
+        u8 *data;
+        int data_size;      /* size of the *data buffer                   */
+        int data_start;     /* offset within *data were valid data starts */
+        int data_end;       /* offset within *data were valid data ends   */
+        int max_data_start; /* optimization parameter! When should it be normalised? */
+        } lb_buf_t;
+
+/* NOTE: lb = Linear Buffer */
+
+static inline u8 *lb_init(lb_buf_t *buf, int size, int max_data_start) {
+  if (size <= 0)
+    return NULL;
+
+  if (max_data_start >= size)
+    max_data_start = size - 1;
+
+  buf->data_size  = size;
+  buf->data_start = 0;
+  buf->data_end   = 0;
+  buf->max_data_start = max_data_start;
+  buf->data = (u8 *)malloc(size);
+  return buf->data;
+}
+
+static inline void lb_done(lb_buf_t *buf) {
+  free(buf->data);
+  buf->data = NULL;
+}
+
+static inline u8 *lb_normalize(lb_buf_t *buf) {
+  return (u8 *)memmove(buf->data,
+                       buf->data + buf->data_start,
+                       buf->data_end - buf->data_start);
+}
+
+static inline u8 *lb_data(lb_buf_t *buf) {
+  return buf->data + buf->data_start;
+}
+
+static inline int lb_data_count(lb_buf_t *buf) {
+  return buf->data_end - buf->data_start;
+}
+
+static inline void lb_data_add(lb_buf_t *buf, int count) {
+  if ((buf->data_end += count) >= buf->data_size)
+    buf->data_end = buf->data_size - 1;
+}
+
+static inline u8 *lb_data_purge(lb_buf_t *buf, int count) {
+  buf->data_start += count;
+  if (buf->data_start > buf->data_end)
+    buf->data_start = buf->data_end;
+
+  if ((buf->data_end == buf->data_size) || (buf->data_start >= buf->max_data_start))
+    return lb_normalize(buf);
+
+  return buf->data + buf->data_start;
+}
+
+static inline void lb_data_purge_all(lb_buf_t *buf) {
+  buf->data_start = buf->data_end = 0;
+}
+
+static inline u8 *lb_free(lb_buf_t *buf) {
+  return buf->data + buf->data_end;
+}
+
+static inline int lb_free_count(lb_buf_t *buf) {
+  return buf->data_size - buf->data_end;
+}
+
+
+
+
+#endif  /* __MODBUS_DS_UTIL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_layer1.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2001,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#ifndef MODBUS_LAYER1_H
+#define MODBUS_LAYER1_H
+
+#include <time.h> /* struct timespec data type */
+
+#include "mb_types.h" /* get the data types */
+#include "mb_addr.h"  /* get definitions of common variable types */
+
+
+/* Define max registers and bits  */
+#define MAX_READ_BITS           2000    /* Functions 0x01 and 0x02 */
+#define MAX_READ_REGS           125     /* Functions 0x03 and 0x04 */
+#define MAX_WRITE_COILS         1968    /* Function 0x0F */
+#define MAX_WRITE_REGS          123     /* Function 0x10 */
+
+
+/* Declare TCP layer1 functions */
+#define modbus_write             modbus_tcp_write           
+#define modbus_read              modbus_tcp_read            
+#define modbus_init              modbus_tcp_init            
+#define modbus_done              modbus_tcp_done            
+#define modbus_connect           modbus_tcp_connect         
+#define modbus_listen            modbus_tcp_listen          
+#define modbus_close             modbus_tcp_close           
+#define modbus_silence_init      modbus_tcp_silence_init    
+#define modbus_get_min_timeout   modbus_tcp_get_min_timeout 
+
+#include "mb_layer1_prototypes.h"
+
+#undef modbus_write          
+#undef modbus_read           
+#undef modbus_init           
+#undef modbus_done           
+#undef modbus_connect        
+#undef modbus_listen         
+#undef modbus_close          
+#undef modbus_silence_init   
+#undef modbus_get_min_timeout
+
+
+
+
+
+/* Declare RTU layer1 functions */
+#define modbus_write             modbus_rtu_write           
+#define modbus_read              modbus_rtu_read            
+#define modbus_init              modbus_rtu_init            
+#define modbus_done              modbus_rtu_done            
+#define modbus_connect           modbus_rtu_connect         
+#define modbus_listen            modbus_rtu_listen          
+#define modbus_close             modbus_rtu_close           
+#define modbus_silence_init      modbus_rtu_silence_init    
+#define modbus_get_min_timeout   modbus_rtu_get_min_timeout 
+
+#include "mb_layer1_prototypes.h"
+
+#undef modbus_write          
+#undef modbus_read           
+#undef modbus_init           
+#undef modbus_done           
+#undef modbus_connect        
+#undef modbus_listen         
+#undef modbus_close          
+#undef modbus_silence_init   
+#undef modbus_get_min_timeout
+
+
+
+
+
+/* Declare ASCII layer1 functions */
+#define modbus_write             modbus_ascii_write           
+#define modbus_read              modbus_ascii_read            
+#define modbus_init              modbus_ascii_init            
+#define modbus_done              modbus_ascii_done            
+#define modbus_connect           modbus_ascii_connect         
+#define modbus_listen            modbus_ascii_listen          
+#define modbus_close             modbus_ascii_close           
+#define modbus_silence_init      modbus_ascii_silence_init    
+#define modbus_get_min_timeout   modbus_ascii_get_min_timeout 
+
+#include "mb_layer1_prototypes.h"
+
+#undef modbus_write          
+#undef modbus_read           
+#undef modbus_init           
+#undef modbus_done           
+#undef modbus_connect        
+#undef modbus_listen         
+#undef modbus_close          
+#undef modbus_silence_init   
+#undef modbus_get_min_timeout
+
+
+
+
+#define modbus_write             (*modbus_write          ) 
+#define modbus_read              (*modbus_read           ) 
+#define modbus_init              (*modbus_init           ) 
+#define modbus_done              (*modbus_done           ) 
+#define modbus_connect           (*modbus_connect        ) 
+#define modbus_listen            (*modbus_listen         ) 
+#define modbus_close             (*modbus_close          ) 
+#define modbus_silence_init      (*modbus_silence_init   ) 
+#define modbus_get_min_timeout   (*modbus_get_min_timeout) 
+
+typedef struct {
+  #include "mb_layer1_prototypes.h"
+} layer1_funct_ptr_t;
+
+#undef modbus_write          
+#undef modbus_read           
+#undef modbus_init           
+#undef modbus_done           
+#undef modbus_connect        
+#undef modbus_listen         
+#undef modbus_close          
+#undef modbus_silence_init   
+#undef modbus_get_min_timeout
+
+
+
+extern layer1_funct_ptr_t fptr_[4];
+
+
+
+
+
+#endif  /* MODBUS_LAYER1_H */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_layer1_prototypes.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+
+ /* write a modbus frame */
+ /* WARNING: when calling this function, the *frame_data buffer
+  *          must be allocated with an extra *extra_bytes
+  *          beyond those required for the frame_length.
+  *          This is because the extra bytes will be used
+  *          to store the crc before sending the frame.
+  *
+  *          The *extra_bytes value will be returned by the
+  *          modbus_init() function call.
+  */
+ /* NOTE: calling this function will flush the input stream,
+  *       which means any frames that may have arrived
+  *       but have not yet been read using modbus_read()
+  *       will be permanently lost...
+  */
+int modbus_write(int    nd,
+                 u8    *frame_data,
+                 size_t frame_length,
+                 u16    transaction_id,
+                 const struct timespec *transmit_timeout
+                );
+
+ /* read a modbus frame */
+/*
+ * The frame is read from:
+ *   -  the node descriptor nd, if nd >= 0
+ *   -  any valid and initialised node descriptor, if nd = -1
+ *      In this case, the node where the data is eventually read from
+ *      is returned in *nd.
+ *      NOTE: (only avaliable if using TCP)
+ */
+ /* NOTE: calling modbus_write() will flush the input stream,
+  *       which means any frames that may have arrived
+  *       but have not yet been read using modbus_read()
+  *       will be permanently lost...
+  *
+  * NOTE: Ususal select semantics for (a: recv_timeout == NULL) and
+  *       (b: *recv_timeout == 0) also apply.
+  *       (a) Indefinite timeout
+  *       (b) Try once, and and quit if no data available.
+  */
+ /* NOTE: send_data and send_length is used to pass to the modbus_read() function
+  *        the frame that was previously sent over the same connection (node).
+  *        This data is then allows the modbus_read() function to ignore any 
+  *        data that is read but identical to the previously sent data. This
+  *        is used when using serial ports that echoes back all the data that is 
+  *        sent out over the same serial port. When using some RS232 to RS485 
+  *        converters, this functionality is essential as not all these converters
+  *        are capable of not echoing back the sent data.
+  *       These parameters are ignored when using TCP! 
+  */
+ 
+ /* RETURNS: number of bytes read
+  *          -1 on read from file/node error
+  *          -2 on timeout
+  */
+int modbus_read(int *nd,                /* node descriptor */
+                u8 **recv_data_ptr,
+                u16 *transaction_id,
+                const u8 *send_data,
+                int send_length,      
+                const struct timespec *recv_timeout);
+
+
+ /* init the library */
+int modbus_init(int nd_count,        /* maximum number of nodes... */
+                optimization_t opt,
+                int *extra_bytes);
+
+ /* shutdown the library...*/
+int modbus_done(void);
+
+
+/* Open a node for master / slave operation.
+ * Returns the node descriptor, or -1 on error.
+ */
+int modbus_connect(node_addr_t node_addr);
+int modbus_listen(node_addr_t node_addr);
+
+/* Close a node, needs a node descriptor as argument... */
+int modbus_close(int nd);
+
+/* Tell the library that the user will probably not be communicating
+ * for some time...
+ * This will allow the library to release any resources it will not
+ * be needing during the silence.
+ * NOTE: This is onlyused by the TCP version to close down tcp connections
+ *       when the silence will going to be longer than  second.
+ */
+int modbus_silence_init(void);
+
+ /* determine the minmum acceptable timeout... */
+ /* NOTE: timeout values passed to modbus_read() lower than the value returned
+  *       by this function may result in frames being aborted midway, since they
+  *       take at least modbus_get_min_timeout() seconds to transmit.
+  */
+double modbus_get_min_timeout(int baud,
+                              int parity,
+                              int data_bits,
+                              int stop_bits);
+
+
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_master.c	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,1277 @@
+/*
+ * Copyright (c) 2001-2003,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+/* mb_master.c */
+
+
+#include <fcntl.h>	/* File control definitions */
+#include <stdio.h>	/* Standard input/output */
+#include <string.h>
+#include <stdlib.h>
+#include <termio.h>	/* POSIX terminal control definitions */
+#include <sys/time.h>	/* Time structures for select() */
+#include <unistd.h>	/* POSIX Symbolic Constants */
+#include <errno.h>	/* Error definitions */
+
+#include <pthread.h>    /* pthread_mutex_[un]lock() */
+
+#include <netinet/in.h> /* required for htons() and ntohs() */
+#include "mb_layer1.h"
+#include "mb_master.h"
+#include "mb_master_private.h"
+
+/* #define DEBUG */		/* uncomment to see the data sent and received */
+
+#define modbus_write             fptr_[layer1_fin].modbus_write           
+#define modbus_read              fptr_[layer1_fin].modbus_read            
+#define modbus_init              fptr_[layer1_fin].modbus_init            
+#define modbus_done              fptr_[layer1_fin].modbus_done            
+#define modbus_connect           fptr_[layer1_fin].modbus_connect         
+#define modbus_listen            fptr_[layer1_fin].modbus_listen          
+#define modbus_close             fptr_[layer1_fin].modbus_close           
+#define modbus_silence_init      fptr_[layer1_fin].modbus_silence_init    
+#define modbus_get_min_timeout   fptr_[layer1_fin].modbus_get_min_timeout 
+
+/* the lower two bits of ttyfd are used to store the index to layer1 function pointers */
+/* layer1_fin index to fptr_[] is in lowest 2 bits of fd */
+#define get_ttyfd()     int layer1_fin = fd & 3; int ttyfd = fd / 4;
+
+
+
+/******************************************/
+/******************************************/
+/**                                      **/
+/**         Global Variables...          **/
+/**                                      **/
+/******************************************/
+/******************************************/
+   /* The layer 1 (RTU, ASCII, TCP) implementation will be adding some headers and CRC (at the end)
+    *  of the packet we build here (actually currently it is only at the end). Since we want to 
+    *  re-use the same buffer so as not to continuosly copy the same info from buffer to buffer,
+    *  we need tp allocate more bytes than the ones we need for this layer. Therefore, the
+    *  extra_bytes parameter.
+    *
+    *  Note that we add one more extra byte. This is because some packets will not be 
+    *  starting off at byte 0, but rather at byte 1 of the buffer. This is in order to guarantee
+    *  that the data that is sent on the buffer is aligned on even bytes (the 16 bit words!).
+    *  This will allow us to reference this memory as an u16 *, without producing 'bus error'
+    *  messages in some embedded devices that do not allow acessing u16 on odd numbered addresses.
+    */ 
+static int buff_extra_bytes_;
+#define QUERY_BUFFER_SIZE       (MAX_L2_FRAME_LENGTH + buff_extra_bytes_ + 1)
+
+
+/******************************************/
+/******************************************/
+/**                                      **/
+/**       Local Utility functions...     **/
+/**                                      **/
+/******************************************/
+/******************************************/
+
+
+/*
+ * Function to determine next transaction id.
+ *
+ * We use a library wide transaction id, which means that we
+ * use a new transaction id no matter what slave to which we will
+ * be sending the request...
+ */
+static inline u16 next_transaction_id(void) {
+  static u16 next_id = 0;
+  return next_id++;
+}
+
+
+/*
+ * Functions to convert u16 variables
+ * between network and host byte order
+ *
+ * NOTE: Modbus uses MSByte first, just like
+ *       tcp/ip, so we use the htons() and
+ *       ntoh() functions to guarantee
+ *       code portability.
+ */
+static inline u16 mb_hton(u16 h_value) {return htons(h_value);}
+static inline u16 mb_ntoh(u16 m_value) {return ntohs(m_value);}
+static inline u8  msb    (u16   value) {return (value >> 8) & 0xFF;}
+static inline u8  lsb    (u16   value) {return  value & 0xFF;}
+
+
+
+
+/*************************************************/
+/*************************************************/
+/**                                             **/
+/**   Common functions for Modbus Protocol.     **/
+/**                                             **/
+/*************************************************/
+/*************************************************/
+
+/* build the common elements of a query frame */
+static inline int build_packet(u8  slave,
+                               u8  function,
+                               u16 start_addr,
+                               u16 count,
+                               u8 *packet) {
+  union {
+      u16 u16;
+      u8  u8[2];
+  } tmp;
+  
+  packet[0] = slave,
+  packet[1] = function;
+    /* NOTE:
+     *  Modbus uses high level addressing starting off from 1, but
+     *  this is sent as 0 on the wire!
+     *  We could expect the user to specify high level addressing 
+     *   starting at 1, and do the conversion to start off at 0 here.
+     *   However, to do this we would then need to use an u32 data type
+     *   to correctly hold the address supplied by the user (which could
+     *   correctly be 65536, which does not fit in an u16), which would
+     *   in turn require us to check whether the address supplied by the user
+     *   is correct (i.e. <= 65536). 
+     *  I decided to go with the other option of using an u16, and 
+     *   requiring the user to use addressing starting off at 0! 
+     */
+  /* NOTE: we do not use up casting - i.e. the following
+   *       *((u16 *)(packet+2)) = mb_hton(start_addr);
+   *       because packet+2 is NOT aligned with an even address, and would
+   *       therefore result in 'bus error' when using compilers that do not 
+   *       automatically do the required decomposing of this supposedly 
+   *       single bus access into two distinct bus accesses.
+   *       (Note that some compilers do do this decomposing automatically
+   *       in which case the following is not necessary).
+   *       At the moment, I (Mario de Sousa) know of at least one cross-compiler
+   *       that does not do the decomposing automatically, i.e. the 
+   *       AVR32 cross-compiler.
+   */
+  tmp.u16 = mb_hton(start_addr);
+  packet[2] = tmp.u8[0];
+  packet[3] = tmp.u8[1];
+  tmp.u16 = mb_hton(count);
+  packet[4] = tmp.u8[0];
+  packet[5] = tmp.u8[1];
+  
+  return 6;
+}
+
+
+
+/* Execute a Query/Response transaction between client and server */ 
+/* returns: <0    -> ERROR: error codes
+ *          >2    -> SUCCESS: frame length
+ *           0..2 -> will never be returned!
+ */
+static int mb_transaction(u8  *packet,
+                          int query_length,
+                          u8  **data,
+                          int fd,
+                          int send_retries,
+                          u8  *error_code,
+                          const struct timespec *response_timeout) {
+  int error = TIMEOUT;
+  int response_length = INTERNAL_ERROR;
+  u16 send_transaction_id, recv_transaction_id;
+  get_ttyfd(); /* declare the ttyfd variable, ... */
+  
+    /* We must also initialize the recv_transaction_id with the same value,
+     * since some layer 1 protocols do not support transaction id's, so
+     * simply return the recv_transaction_id variable without any changes...
+     */
+  /* NOTE: we re-use the same transaction id for all send re-tries., since, in truth, 
+   * it is still the same transaction. This will also simplify re-synchronising with
+   * some slaves that keep a buffer of outstanding requests, and will reply to all of
+   * them, in FIFO order. In this case, once an error occurs we will be swamping the
+   * slave with requests. By using the same transaction id, we may correctly consider
+   * the reply to the first request sent as the reply to the third request! This means
+   * we stop re-trying the sending of further requests, and no longer swamp the slave...
+   */
+  send_transaction_id = recv_transaction_id = next_transaction_id();
+
+  for (send_retries++; send_retries > 0; send_retries--) {
+    error = TIMEOUT;
+
+    if (modbus_write(ttyfd, packet, query_length, send_transaction_id, response_timeout) < 0)
+      {error = PORT_FAILURE; continue;}
+
+      /* if we receive a correct response but with a wrong transaction id or wrong modbus function, we try to 
+       * receive another frame instead of returning an error or re-sending the request! This first frame could 
+       * have been a response to a previous request of ours that timed out waiting for a response, and the 
+       * response we are waiting for could be coming 'any minute now'.
+       */
+    do {
+      response_length = modbus_read(&ttyfd, data, &recv_transaction_id,
+                                    packet, query_length, response_timeout);
+
+      /* TIMEOUT condition */
+      /* However, if we had previously received an invalid frame, or some other error,
+       * we return that error instead!
+       * Note that the 'error' variable was initialised with the TIMEOUT error
+       * condition, so if no previous error ocurred, we will be returning the
+       * TIMEOUT error condition here!
+       */
+      if(response_length == -2)  return error;
+      /* NOTE we want to break out of this while loop without even running the while()
+       * condition, as that condition is only valid if response_length > 3 !!
+       */
+      if(response_length  <  0)  {error = PORT_FAILURE; break;}
+      /* This should never occur! Modbus_read() should only return valid frames! */
+      if(response_length  <  3)  return INTERNAL_ERROR;
+
+    } while (/* we have the wrong transaction id */
+             (send_transaction_id != recv_transaction_id)
+             /* not a response frame to _our_ query */
+            ||
+             (((*data)[1] & ~0x80) != packet[1])
+            /* NOTE: no need to check whether (*data)[0] = slave!              */
+            /*       This has already been done by the modbus_read() function! */
+            );
+
+    if(response_length < 0)  {error = PORT_FAILURE; continue;}
+
+    /* Now check whether we received a Modbus Exception frame */
+    if (((*data)[1] & 0x80) != 0) {       /* we have an exception frame! */
+      /* NOTE: we have already checked above that data[2] exists! */
+      if (error_code != NULL)  *error_code = (*data)[2];
+      return MODBUS_ERROR;
+    }
+    /* success! Let's get out of the send retry loop... */
+    return response_length;
+  }
+  /* reached the end of the retries... */
+  return error;
+}
+
+
+/**************************************/
+/**************************************/
+/**                                  **/
+/**   Modbus Protocol Functions.     **/
+/**                                  **/
+/**************************************/
+/**************************************/
+
+
+
+/* Execute a transaction for functions that READ BITS.
+ * Bits are stored on an int array, one bit per int.
+ * Called by:  read_input_bits()
+ *             read_output_bits()
+ */
+static int read_bits(u8  function,
+                     u8  slave,
+                     u16 start_addr,
+                     u16 count,
+                     u16 *dest,
+                     int dest_size,
+                     int ttyfd,
+                     int send_retries,
+                     u8  *error_code,
+                     const struct timespec *response_timeout,
+                     pthread_mutex_t *data_access_mutex) {
+  
+  u8 packet[QUERY_BUFFER_SIZE];
+  u8 *data;
+  int response_length, query_length;
+  int temp, i, bit, dest_pos = 0;
+  int coils_processed = 0;
+  
+  query_length = build_packet(slave, function, start_addr, count, packet);
+  if (query_length < 0)  return INTERNAL_ERROR;
+  
+  response_length = mb_transaction(packet, query_length, &data, ttyfd,
+                                   send_retries, error_code, response_timeout);
+  
+  if (response_length  < 0)                  return response_length;
+  /* NOTE: Integer division. (count+7)/8 is equivalent to ceil(count/8) */
+  if (response_length != 3 + (count+7)/8)    return INVALID_FRAME;
+  if (data[2]         !=     (count+7)/8)    return INVALID_FRAME;
+  
+  if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
+  for( i = 0; (i < data[2]) && (i < dest_size); i++ ) {
+    temp = data[3 + i];
+    for( bit = 0x01; (bit & 0xff) && (coils_processed < count); ) {
+      dest[dest_pos] = (temp & bit)?1:0;
+      coils_processed++;
+      dest_pos++;
+      bit = bit << 1;
+    }
+  }
+  if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
+  
+  return response_length;
+}
+
+
+
+/* Execute a transaction for functions that READ BITS.
+ * Bits are stored on an u32 array, 32 bits per u32.
+ * Unused bits in last u32 are set to 0.
+ * Called by:  read_input_bits_u32()
+ *             read_output_bits_u32()
+ */
+static int read_bits_u32(u8  function,
+                         u8  slave,
+                         u16 start_addr,
+                         u16 count, /* number of bits !! */
+                         u32 *dest,
+                         int ttyfd,
+                         int send_retries,
+                         u8  *error_code,
+                         const struct timespec *response_timeout) {
+  u8 packet[QUERY_BUFFER_SIZE];
+  u8 *data;
+  int response_length, query_length;                         
+  int byte_count, i, dest_pos = 0;
+  
+  query_length = build_packet(slave, function, start_addr, count, packet);
+  if (query_length < 0)  return INTERNAL_ERROR;
+  
+  response_length = mb_transaction(packet, query_length, &data, ttyfd,
+                                   send_retries, error_code, response_timeout);
+  
+  if (response_length < 0)                   return response_length;  
+  /* NOTE: Integer division. (count+7)/8 is equivalent to ceil(count/8) */
+  if (response_length != 3 + (count+7)/8)    return INVALID_FRAME;
+  if (data[2]         !=     (count+7)/8)    return INVALID_FRAME;
+  
+  byte_count = data[2];
+  data += 3;
+  /* handle groups of 4 bytes... */
+  for(i = 0, dest_pos = 0; i + 3 < byte_count; i += 4, dest_pos++)
+    dest[dest_pos] = data[i] + data[i+1]*0x100 + data[i+2]*0x10000 + data[i+3]*0x1000000;
+  /* handle any remaining bytes... begining with the last! */
+  if (i < byte_count) dest[dest_pos] = 0;
+  for(byte_count--; i <= byte_count; byte_count--)
+    dest[dest_pos] = dest[dest_pos]*0x100 + data[byte_count];
+  
+  return response_length;
+}
+
+
+
+/* FUNCTION 0x01   - Read Coils
+ * Bits are stored on an int array, one bit per int.
+ */
+inline int read_output_bits(u8  slave,
+                            u16 start_addr,
+                            u16 count,
+                            u16 *dest,
+                            int dest_size,
+                            int ttyfd,
+                            int send_retries,
+                            u8  *error_code,
+                            const struct timespec *response_timeout,
+                            pthread_mutex_t *data_access_mutex) {
+  if( count > MAX_READ_BITS ) {
+    count = MAX_READ_BITS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many coils requested.\n" );
+    #endif
+  }
+
+  return read_bits(0x01 /* function */,
+                   slave, start_addr, count, dest, dest_size, ttyfd, 
+                   send_retries, error_code, response_timeout, data_access_mutex);
+}
+
+
+
+/* FUNCTION 0x01   - Read Coils
+ * Bits are stored on an u32 array, 32 bits per u32.
+ * Unused bits in last u32 are set to 0.
+ */
+inline int read_output_bits_u32(u8  slave,
+                                u16 start_addr,
+                                u16 count,
+                                u32 *dest,
+                                int ttyfd,
+                                int send_retries,
+                                u8  *error_code,
+                                const struct timespec *response_timeout) {
+  if( count > MAX_READ_BITS ) {
+    count = MAX_READ_BITS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many coils requested.\n" );
+    #endif
+  }
+
+  return read_bits_u32(0x01 /* function */,
+                       slave, start_addr, count, dest, ttyfd,
+                       send_retries, error_code, response_timeout);
+}
+
+
+/* FUNCTION 0x02   - Read Discrete Inputs
+ * Bits are stored on an int array, one bit per int.
+ */
+inline int read_input_bits(u8  slave,
+                           u16 start_addr,
+                           u16 count,
+                           u16 *dest,
+                           int dest_size,
+                           int ttyfd,
+                           int send_retries,
+                           u8  *error_code,
+                           const struct timespec *response_timeout,
+                           pthread_mutex_t *data_access_mutex) {
+  if( count > MAX_READ_BITS ) {
+    count = MAX_READ_BITS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many coils requested.\n" );
+    #endif
+  }
+
+  return read_bits(0x02 /* function */,
+                   slave, start_addr, count, dest, dest_size, ttyfd,
+                   send_retries, error_code, response_timeout, data_access_mutex);
+}
+
+
+
+/* FUNCTION 0x02   - Read Discrete Inputs
+ * Bits are stored on an u32 array, 32 bits per u32.
+ * Unused bits in last u32 are set to 0.
+ */
+inline int read_input_bits_u32(u8  slave,
+                               u16 start_addr,
+                               u16 count,
+                               u32 *dest,
+                               int ttyfd,
+                               int send_retries,
+                               u8  *error_code,
+                               const struct timespec *response_timeout) {
+  if( count > MAX_READ_BITS ) {
+    count = MAX_READ_BITS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many coils requested.\n" );
+    #endif
+  }
+
+  return read_bits_u32(0x02 /* function */,
+                       slave, start_addr, count, dest, ttyfd,
+                       send_retries, error_code, response_timeout);
+}
+
+
+
+
+
+
+/* Execute a transaction for functions that READ REGISTERS.
+ * Called by:  read_input_words()
+ *             read_output_words()
+ */
+static int read_registers(u8  function,
+                          u8  slave,
+                          u16 start_addr,
+                          u16 count,
+                          u16 *dest,
+                          int dest_size,
+                          int ttyfd,
+                          int send_retries,
+                          u8  *error_code,
+                          const struct timespec *response_timeout,
+                          pthread_mutex_t *data_access_mutex) {
+  u8 *data;
+  u8 packet[QUERY_BUFFER_SIZE];
+  int response_length;
+  int query_length;
+  int temp,i;
+  
+  query_length = build_packet(slave, function, start_addr, count, packet);
+  if (query_length < 0)    return INTERNAL_ERROR;
+  
+  response_length = mb_transaction(packet, query_length, &data, ttyfd, 
+                                   send_retries, error_code, response_timeout);
+  
+  if (response_length  < 0)              return response_length;  
+  if (response_length != 3 + 2*count)    return INVALID_FRAME;  
+  if (data[2]         !=     2*count)    return INVALID_FRAME;
+  
+  if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
+  for(i = 0; (i < (data[2]*2)) && (i < dest_size); i++ ) {
+    temp = data[3 + i *2] << 8;    /* copy reg hi byte to temp hi byte*/
+    temp = temp | data[4 + i * 2]; /* copy reg lo byte to temp lo byte*/
+    dest[i] = temp;
+  }
+  if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
+  
+  return response_length;
+}
+
+
+
+/* Execute a transaction for functions that READ REGISTERS.
+ * return the array with the data to the calling function
+ * Called by:  read_input_words_u16_ref()
+ *             read_output_words_u16_ref()
+ */
+
+static int read_registers_u16_ref(u8  function,
+                                  u8  slave,
+                                  u16 start_addr,
+                                  u16 count,
+                                  u16 **dest,
+                                  int ttyfd,
+                                  int send_retries,
+                                  u8  *error_code,
+                                  const struct timespec *response_timeout) {
+  u8 *data;
+  u8 packet[QUERY_BUFFER_SIZE];
+  int response_length;
+  int query_length;
+  int i, byte_count;
+  
+  query_length = build_packet(slave, function, start_addr, count, packet);
+  if (query_length < 0)    return INTERNAL_ERROR;
+  
+  response_length = mb_transaction(packet, query_length, &data, ttyfd,
+                                   send_retries, error_code, response_timeout);
+  
+  if (response_length < 0)               return response_length;  
+  if (response_length != 3 + 2*count)    return INVALID_FRAME;  
+  if (data[2]         !=     2*count)    return INVALID_FRAME;
+  
+  byte_count = data[2];
+  data = data + 3; /* & data[3] */
+  
+  if (ntohs(0x0102) != 0x0102) {
+   /* little endian host... => we need to swap the bytes! */
+    for(i = 0; i < byte_count; i++ ) {
+      /* the following 3 lines result in the two values being exchanged! */ 
+      data[i  ] = data[i] ^ data[i+1];
+      data[i+1] = data[i] ^ data[i+1];
+      data[i  ] = data[i] ^ data[i+1];
+    }
+  }
+  *dest = (u16 *)data;  
+  return byte_count;
+}
+
+
+
+
+/* Execute a transaction for functions that READ REGISTERS.
+ * u16 registers are stored in array of u32, two registers per u32.
+ * Unused bits of last u32 element are set to 0.
+ * Called by:  read_input_words_u32()
+ *             read_output_words_u32()
+ */
+static int read_registers_u32(u8  function,
+                              u8  slave,
+                              u16 start_addr,
+                              u16 count,
+                              u32 *dest,
+                              int ttyfd,
+                              int send_retries,
+                              u8  *error_code,
+                              const struct timespec *response_timeout) {
+  u8 *data;
+  u8 packet[QUERY_BUFFER_SIZE];
+  int response_length;
+  int query_length;
+  int i, byte_count, dest_pos;
+  
+  query_length = build_packet(slave, function, start_addr, count, packet);
+  if (query_length < 0)    return INTERNAL_ERROR;
+  
+  response_length = mb_transaction(packet, query_length, &data, ttyfd,
+                                   send_retries, error_code, response_timeout);
+  
+  if (response_length  < 0)              return response_length;
+  if (response_length != 3 + 2*count)    return INVALID_FRAME;
+  if (data[2]         !=     2*count)    return INVALID_FRAME;
+  
+  byte_count = data[2];
+  data += 3;
+  
+  if (ntohs(0x0102) == 0x0102) {
+   /* big endian host... */
+    /* handle groups of 4 bytes... */
+    for(i = 0, dest_pos = 0; i + 3 < byte_count; i += 4, dest_pos++) {
+      *(((u8 *)(dest + dest_pos))+ 0) = *(data+i+3);
+      *(((u8 *)(dest + dest_pos))+ 1) = *(data+i+4);
+      *(((u8 *)(dest + dest_pos))+ 2) = *(data+i+0);
+      *(((u8 *)(dest + dest_pos))+ 3) = *(data+i+1);
+    }
+    /* handle any remaining bytes...
+     * since byte_count is supposed to be multiple of 2,
+     * (and has already been verified above 'if (data[2] != 2*count)')
+     * this will be either 2, or none at all!
+     */
+    if (i + 1 < byte_count)
+      *(((u8 *)(dest + dest_pos))+ 0) = 0;
+      *(((u8 *)(dest + dest_pos))+ 1) = 0;
+      *(((u8 *)(dest + dest_pos))+ 2) = *(data+i+0);
+      *(((u8 *)(dest + dest_pos))+ 3) = *(data+i+1);
+  } else {
+   /* little endian host... */
+    /* handle groups of 4 bytes... */
+    for(i = 0, dest_pos = 0; i + 3 < byte_count; i += 4, dest_pos++) {
+      *(((u8 *)(dest + dest_pos))+ 0) = *(data+i+1);
+      *(((u8 *)(dest + dest_pos))+ 1) = *(data+i+0);
+      *(((u8 *)(dest + dest_pos))+ 2) = *(data+i+3);
+      *(((u8 *)(dest + dest_pos))+ 3) = *(data+i+2);
+    }
+    /* handle any remaining bytes...
+     * since byte_count is supposed to be multiple of 2,
+     * (and has already been verified above 'if (data[2] != 2*count)')
+     * this will be either 2, or none at all!
+     */
+    if (i + 1 < byte_count)
+      *(((u8 *)(dest + dest_pos))+ 0) = *(data+i+1);
+      *(((u8 *)(dest + dest_pos))+ 1) = *(data+i+0);
+      *(((u8 *)(dest + dest_pos))+ 2) = 0;
+      *(((u8 *)(dest + dest_pos))+ 3) = 0;
+  }
+  
+  return response_length;
+}
+
+
+
+
+
+
+
+/* FUNCTION 0x03   - Read Holding Registers */
+inline int read_output_words(u8  slave,
+                             u16 start_addr,
+                             u16 count,
+                             u16 *dest,
+                             int dest_size,
+                             int ttyfd,
+                             int send_retries,
+                             u8  *error_code,
+                             const struct timespec *response_timeout,
+                             pthread_mutex_t *data_access_mutex) {
+  if( count > MAX_READ_REGS ) {
+    count = MAX_READ_REGS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many registers requested.\n" );
+    #endif
+  }
+
+  return read_registers(0x03 /* function */,
+                        slave, start_addr, count, dest, dest_size, ttyfd,
+                        send_retries, error_code, response_timeout, data_access_mutex);
+}
+
+
+
+
+/* FUNCTION 0x03   - Read Holding Registers
+ * u16 registers are stored in array of u32, two registers per u32.
+ * Unused bits of last u32 element are set to 0.
+ */
+inline int read_output_words_u32(u8  slave,
+                                 u16 start_addr,
+                                 u16 count,
+                                 u32 *dest,
+                                 int ttyfd,
+                                 int send_retries,
+                                 u8  *error_code,
+                                 const struct timespec *response_timeout) {
+  if( count > MAX_READ_REGS ) {
+    count = MAX_READ_REGS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many registers requested.\n" );
+    #endif
+  }
+  
+  return read_registers_u32(0x03 /* function */,
+                            slave, start_addr, count, dest, ttyfd,
+                            send_retries, error_code, response_timeout);
+}
+
+
+
+
+/* FUNCTION 0x03   - Read Holding Registers
+ * return the array with the data to the calling function
+ */
+inline int read_output_words_u16_ref(u8  slave,
+                                     u16 start_addr,
+                                     u16 count,
+                                     u16 **dest,
+                                     int ttyfd,
+                                     int send_retries,
+                                     u8  *error_code,
+                                     const struct timespec *response_timeout) {
+  if( count > MAX_READ_REGS ) {
+    count = MAX_READ_REGS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many registers requested.\n" );
+    #endif
+  }
+  
+  return read_registers_u16_ref(0x03 /* function */,
+                                slave, start_addr, count, dest, ttyfd, send_retries,
+                                error_code, response_timeout);
+}
+
+
+
+
+/* FUNCTION 0x04   - Read Input Registers */
+inline int read_input_words(u8  slave,
+                            u16 start_addr,
+                            u16 count,
+                            u16 *dest,
+                            int dest_size,
+                            int ttyfd,
+                            int send_retries,
+                            u8  *error_code,
+                            const struct timespec *response_timeout,
+                            pthread_mutex_t *data_access_mutex) {
+  if( count > MAX_READ_REGS ) {
+    count = MAX_READ_REGS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many input registers requested.\n" );
+    #endif
+  }
+  
+  return read_registers(0x04 /* function */,
+                        slave, start_addr, count, dest, dest_size, ttyfd, send_retries,
+                        error_code, response_timeout, data_access_mutex);
+}
+
+
+/* FUNCTION 0x04   - Read Input Registers
+ * u16 registers are stored in array of u32, two registers per u32.
+ * Unused bits of last u32 element are set to 0.
+ */
+inline int read_input_words_u32(u8  slave,
+                                u16 start_addr,
+                                u16 count,
+                                u32 *dest,
+                                int ttyfd,
+                                int send_retries,
+                                u8  *error_code,
+                                const struct timespec *response_timeout) {
+  if( count > MAX_READ_REGS ) {
+    count = MAX_READ_REGS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many input registers requested.\n" );
+    #endif
+  }
+  
+  return read_registers_u32(0x04 /* function */,
+                            slave, start_addr, count, dest, ttyfd, send_retries,
+                            error_code, response_timeout);
+}
+
+
+
+
+/* FUNCTION 0x04   - Read Input Registers
+ * return the array with the data to the calling function
+ */
+inline int read_input_words_u16_ref(u8  slave,
+                                    u16 start_addr,
+                                    u16 count,
+                                    u16 **dest,
+                                    int ttyfd,
+                                    int send_retries,
+                                    u8  *error_code,
+                                    const struct timespec *response_timeout) {
+  if( count > MAX_READ_REGS ) {
+    count = MAX_READ_REGS;
+    #ifdef DEBUG
+    fprintf( stderr, "Too many input registers requested.\n" );
+    #endif
+  }
+  
+  return read_registers_u16_ref(0x04 /* function */,
+                                slave, start_addr, count, dest, ttyfd, send_retries,
+                                error_code, response_timeout);
+}
+
+
+
+/* Execute a transaction for functions that WRITE a sinlge BIT.
+ * Called by:  write_output_bit()
+ *             write_output_word()
+ */
+static int set_single(u8  function,
+                      u8  slave,
+                      u16 addr,
+                      u16 value,
+                      int ttyfd,
+                      int send_retries,
+                      u8  *error_code,
+                      const struct timespec *response_timeout,
+                      pthread_mutex_t *data_access_mutex) {
+  u8 packet[QUERY_BUFFER_SIZE];
+  u8 *data;
+  int query_length, response_length;
+  
+  if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
+  query_length = build_packet(slave, function, addr, value, packet);
+  if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
+  if (query_length < 0)    return INTERNAL_ERROR;
+  
+  response_length = mb_transaction(packet, query_length, &data, ttyfd, send_retries,
+                                   error_code, response_timeout);
+  
+  if (response_length  < 0)  return response_length;  
+  if (response_length != 6)  return INVALID_FRAME;
+  
+  if ((data[2] != packet[2]) || (data[3] != packet[3]) ||
+      (data[4] != packet[4]) || (data[5] != packet[5]))
+    return INVALID_FRAME;
+  
+  return response_length;
+}
+
+
+
+
+
+
+/* FUNCTION 0x05   - Force Single Coil */
+inline int write_output_bit(u8  slave,
+                            u16 coil_addr,
+                            u16 state,
+                            int fd,
+                            int send_retries,
+                            u8  *error_code,
+                            const struct timespec *response_timeout,
+                            pthread_mutex_t *data_access_mutex) {
+  if (state) state = 0xFF00;
+  
+  return set_single(0x05 /* function */,
+                    slave, coil_addr, state, fd, send_retries,
+                    error_code, response_timeout, data_access_mutex);
+}
+
+
+
+
+
+/* FUNCTION 0x06   - Write Single Register */
+inline int write_output_word(u8  slave,
+                             u16 reg_addr,
+                             u16 value,
+                             int fd,
+                             int send_retries,
+                             u8  *error_code,
+                             const struct timespec *response_timeout,
+                             pthread_mutex_t *data_access_mutex) {
+  return set_single(0x06 /* function */, 
+                    slave, reg_addr, value, fd, send_retries,
+                    error_code, response_timeout, data_access_mutex);
+}
+
+
+
+
+/* FUNCTION 0x0F   - Force Multiple Coils */
+int write_output_bits(u8  slave,
+                      u16 start_addr,
+                      u16 coil_count,
+                      u16 *data,
+                      int ttyfd,
+                      int send_retries,
+                      u8  *error_code,
+                      const struct timespec *response_timeout,
+                      pthread_mutex_t *data_access_mutex) {
+  int byte_count, i;
+  u8  bit;
+  int coil_check = 0;
+  int data_array_pos = 0;
+  int query_length, response_length;
+  u8 packet[QUERY_BUFFER_SIZE];
+  u8  *rdata;
+  
+  if( coil_count > MAX_WRITE_COILS ) {
+    coil_count = MAX_WRITE_COILS;
+    #ifdef DEBUG
+    fprintf( stderr, "Writing to too many coils.\n" );
+    #endif
+  }
+  
+  query_length = build_packet(slave, 0x0F /* function */,
+                              start_addr, coil_count, packet);
+  if (query_length < 0)    return INTERNAL_ERROR;
+  
+  /* NOTE: Integer division. (count+7)/8 is equivalent to ceil(count/8) */
+  byte_count = (coil_count+7)/8;
+  packet[query_length] = byte_count;
+  
+  if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
+  bit = 0x01;
+  for(i = 0; i < byte_count; i++) {
+    packet[++query_length] = 0;
+    while((bit & 0xFF) && (coil_check++ < coil_count)) {
+      if(data[data_array_pos++]) {packet[query_length] |=  bit;}
+      else                       {packet[query_length] &= ~bit;}
+      bit <<= 1;
+    }
+    bit = 0x01;
+  }
+  if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
+  
+  response_length = mb_transaction(packet, ++query_length, &rdata, ttyfd, send_retries,
+                                   error_code, response_timeout);
+  
+  if (response_length  < 0)      return response_length;
+  if (response_length != 6)      return INVALID_FRAME;
+  if ((rdata[2] != packet[2]) || 
+      (rdata[3] != packet[3]) ||
+      (rdata[4] != packet[4]) ||
+      (rdata[5] != packet[5]))   return INVALID_FRAME;
+  
+  return response_length;
+}
+
+
+
+/* FUNCTION 0x0F   - Force Multiple Coils
+ * Bits should be stored on an u32 array, 32 bits per u32.
+ * Unused bits in last u32 should be set to 0.
+ */
+int write_output_bits_u32(u8  slave,
+                          u16 start_addr,
+                          u16 coil_count,
+                          u32 *data,
+                          int ttyfd,
+                          int send_retries,
+                          u8  *error_code,
+                          const struct timespec *response_timeout) {
+  int org_pos, byte_count, i;
+  int query_length, response_length;
+  u8 packet[QUERY_BUFFER_SIZE];
+  u8  *rdata;
+  
+  if( coil_count > MAX_WRITE_COILS ) {
+    coil_count = MAX_WRITE_COILS;
+    #ifdef DEBUG
+    fprintf( stderr, "Writing to too many coils.\n" );
+    #endif
+  }
+  
+  query_length = build_packet(slave, 0x0F /* function */,
+                              start_addr, coil_count, packet);
+  if (query_length < 0)    return INTERNAL_ERROR;
+  
+  /* NOTE: Integer division. This is equivalent of determining the ceil(count/8) */
+  byte_count = (coil_count+7)/8;
+  packet[query_length] = byte_count;
+  
+  /* handle groups of 4 bytes... */
+  for(i = 0, org_pos = 0; i + 3 < byte_count; i += 4, org_pos++) {
+    packet[++query_length] = data[org_pos] & 0xFF; data[org_pos] >>= 8;
+    packet[++query_length] = data[org_pos] & 0xFF; data[org_pos] >>= 8;
+    packet[++query_length] = data[org_pos] & 0xFF; data[org_pos] >>= 8;
+    packet[++query_length] = data[org_pos] & 0xFF;
+  }
+  /* handle any remaining bytes... */
+  for(; i < byte_count; i++) {
+    packet[++query_length] = data[org_pos] & 0xFF; 
+    data[org_pos] >>= 8;
+  }
+  
+  response_length = mb_transaction(packet, ++query_length, &rdata, ttyfd, send_retries,
+                                   error_code, response_timeout);
+  
+  if (response_length  < 0)       return response_length;
+  if (response_length != 6)       return INVALID_FRAME;
+  if ((rdata[2] != packet[2]) ||
+      (rdata[3] != packet[3]) ||
+      (rdata[4] != packet[4]) ||
+      (rdata[5] != packet[5]))    return INVALID_FRAME;
+  
+  return response_length;
+}
+
+
+
+
+
+/* FUNCTION 0x10   - Force Multiple Registers */
+int write_output_words(u8  slave,
+                       u16 start_addr,
+                       u16 reg_count,
+                       u16 *data,
+                       int ttyfd,
+                       int send_retries,
+                       u8  *error_code,
+                       const struct timespec *response_timeout,
+                       pthread_mutex_t *data_access_mutex) {
+  u8  byte_count;
+  int i, query_length, response_length;
+  u8 packet[QUERY_BUFFER_SIZE];
+  u8  *rdata;
+  
+  if( reg_count > MAX_WRITE_REGS ) {
+    reg_count = MAX_WRITE_REGS;
+    #ifdef DEBUG
+    fprintf( stderr, "Trying to write to too many registers.\n" );
+    #endif
+  }
+  
+  query_length = build_packet(slave, 0x10 /* function */,
+                              start_addr, reg_count, packet);
+  if (query_length < 0)    return INTERNAL_ERROR;
+  
+  byte_count = reg_count*2;
+  packet[query_length] = byte_count;
+  
+  if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
+  for( i = 0; i < reg_count; i++ ) {
+    packet[++query_length] = data[i] >> 8;
+    packet[++query_length] = data[i] & 0x00FF;
+  }
+  if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
+  
+  response_length = mb_transaction(packet, ++query_length, &rdata, ttyfd, send_retries,
+                                   error_code, response_timeout);
+  
+  if (response_length  < 0)       return response_length;  
+  if (response_length != 6)       return INVALID_FRAME;  
+  if ((rdata[2] != packet[2]) ||
+      (rdata[3] != packet[3]) ||      
+      (rdata[4] != packet[4]) ||
+      (rdata[5] != packet[5]))    return INVALID_FRAME;
+  
+  return response_length;
+}
+
+
+
+
+/* FUNCTION 0x10   - Force Multiple Registers
+ * u16 registers are stored in array of u32, two registers per u32.
+ * Unused bits of last u32 element are set to 0.
+ */
+int write_output_words_u32(u8  slave,
+                           u16 start_addr,
+                             /* number of 16 bit registers packed in the u32 array! */
+                           u16 reg_count,
+                           u32 *data,
+                           int ttyfd,
+                           int send_retries,
+                           u8  *error_code,
+                           const struct timespec *response_timeout) {
+  u8  byte_count;
+  int i, query_length, response_length;
+  u8 packet_[QUERY_BUFFER_SIZE];
+  u8 *packet = packet_; /* remove the const'ness of packet_ */
+  u8  *rdata;
+  
+  if( reg_count > MAX_WRITE_REGS ) {
+    reg_count = MAX_WRITE_REGS;
+    #ifdef DEBUG
+    fprintf( stderr, "Trying to write to too many registers.\n" );
+    #endif
+  }
+  
+  /* Make sure that the de-referencing and up-casting going on later on in 
+   * this function, i.e. code like the following line:
+   * *((u16 *)packet) = XXX
+   * will result in u16 words starting off on even addresses.
+   * If we don't do this, some compilers (e.g. AVR32 cross-compiler) will 
+   * generate code which, when executed, will result in 'bus error'.
+   *
+   * The following packet++ means that the first byte of the packet array is
+   * essentially never used. Notice too that the size of thepacket array
+   * already takes into account this un-used byte.
+   */
+  packet++;
+  
+  query_length = build_packet(slave, 0x10 /* function */,
+                              start_addr, reg_count, packet);
+  if (query_length < 0)  return INTERNAL_ERROR;
+  
+  byte_count = reg_count*2;
+  packet[query_length] = byte_count;
+  
+  /* handle groups of 4 bytes... */
+  for(i = 0; 4*i + 3 < byte_count; i++) {
+    *((u16 *)(packet+(++query_length))) = mb_hton(data[i]);        ++query_length;
+    *((u16 *)(packet+(++query_length))) = mb_hton(data[i] >> 16);  ++query_length;
+  }
+  
+  /* handle any remaining bytes...
+   * since byte_count is supposed to be multiple of 2,
+   * (and has already been verified above 'if (data[2] != 2*count)')
+   * this will be either 2, or none at all!
+   */
+  if (4*i + 1 < byte_count) {
+    *((u16 *)(packet+(++query_length))) = mb_hton(data[i]);        ++query_length;
+  }
+  
+  response_length = mb_transaction(packet, ++query_length, &rdata, ttyfd, send_retries,
+                                   error_code, response_timeout);
+    
+  if (response_length  < 0)       return response_length;  
+  if (response_length != 6)       return INVALID_FRAME;  
+  if ((rdata[2] != packet[2]) ||
+      (rdata[3] != packet[3]) ||
+      (rdata[4] != packet[4]) ||
+      (rdata[5] != packet[5]))    return INVALID_FRAME;
+  
+  return response_length;
+}
+
+
+
+
+
+
+/************************************************/
+/************************************************/
+/**                                            **/
+/**   Modbus Library Management Functions.     **/
+/**                                            **/
+/************************************************/
+/************************************************/
+
+
+
+/* Initialise the Modbus Master Layer */
+int mb_master_init__(int extra_bytes) {
+  #ifdef DEBUG
+  fprintf(stderr, "mb_master_init__(extra_bytes=%d), QUERY_BUFFER_SIZE=%d\n", extra_bytes, QUERY_BUFFER_SIZE);
+  #endif
+  buff_extra_bytes_ = extra_bytes;
+  return 0;
+}
+
+
+/* Shut down the Modbus Master Layer */
+int mb_master_done__(void) {
+        return 0;
+}
+
+
+#if 0
+int mb_master_init(int nd_count) {
+  int extra_bytes;
+  
+  #ifdef DEBUG
+  fprintf( stderr, "mb_master_init()\n");
+  fprintf( stderr, "creating %d nodes\n", nd_count);
+  #endif
+  
+  /* initialise layer 1 library */
+  if (modbus_init(nd_count, DEF_OPTIMIZATION, &extra_bytes) < 0)
+    goto error_exit_0;
+  
+  /* initialise this library */
+  if (mb_master_init__(extra_bytes) < 0)
+    goto error_exit_1;
+  
+  return 0;
+  
+error_exit_1:
+  modbus_done();
+error_exit_0:
+  return -1;
+}
+
+
+int mb_master_done(void) {
+  mb_master_done__();
+  return modbus_done();
+}
+#endif
+
+
+/* Establish a connection to a remote server/slave */
+/* NOTE: We use the lower 2 bits of the returned node id to identify which 
+ *       layer1 implementation to use. 
+ *           0 -> TCP 
+ *           1 -> RTU 
+ *           2 -> ASCII 
+ *           4 -> unused 
+ *       The node id used by the layer1 is shifted left 2 bits
+ *       before returning the node id to the caller!
+ */
+int mb_master_connect(node_addr_t node_addr) {
+  int res = -1;
+  
+  #ifdef DEBUG
+  fprintf( stderr, "mb_master_tcp connect()\n");
+  #endif
+  
+  /* call layer 1 library */
+  switch(node_addr.naf) {
+    case naf_tcp:  
+      res = modbus_tcp_connect(node_addr);
+      if (res >= 0) res = res*4 + 0 /* offset into fptr_ with TCP functions */;
+      return res;
+    case naf_rtu:  
+      res = modbus_rtu_connect(node_addr);
+      if (res >= 0) res = res*4 + 1 /* offset into fptr_ with RTU functions */;
+      return res;
+    case naf_ascii:  
+      res = modbus_ascii_connect(node_addr);
+      if (res >= 0) res = res*4 + 2 /* offset into fptr_ with ASCII functions */;
+      return res;
+  }
+  
+  return -1;
+}
+
+
+
+
+
+/* Shut down a connection to a remote server/slave */
+int mb_master_close(int fd) {
+  #ifdef DEBUG
+  fprintf( stderr, "mb_master_close(): nd = %d\n", fd);
+  #endif
+  get_ttyfd(); /* declare the ttyfd variable, ... */
+  /* call layer 1 library */
+  return modbus_close(ttyfd);
+}
+
+
+
+
+
+
+/* Tell the library that communications will be suspended for some time. */
+/* RTU and ASCII versions ignore this function
+ * TCP version closes all the open tcp connections (connections are automatically
+ *   re-established the next time an IO function to the slave is requested).
+ *   To be more precise, the TCP version makes an estimate of how long
+ *   the silence will be based on previous invocations to this exact same
+ *   function, and will only close the connections if this silence is
+ *   expected to be longer than 1 second!
+ *   (The closing of connections is specified in Modbus specification)
+ */
+int mb_master_tcp_silence_init(void) {
+  #ifdef DEBUG
+  fprintf( stderr, "mb_master_silence_init():\n");
+  #endif
+  /* call layer 1 library */
+  return modbus_tcp_silence_init();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_master.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2001-2003,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+/* mb_master.h */
+
+
+#ifndef MODBUS_MASTER_H
+#define MODBUS_MASTER_H
+
+#include <time.h> /* struct timespec data structure */
+
+#include "mb_types.h" /* get the data types */
+#include "mb_addr.h"  /* get definition of common variable types and error codes */
+
+
+
+/***********************************************************************
+
+	 Note: All functions used for sending or receiving data via
+	       modbus return these return values.
+
+
+	Returns:	string_length if OK
+			-1 on internal error or port failure
+			-2 on timeout
+			-3 if a valid yet un-expected frame is received!
+			-4 for modbus exception errors
+			   (in this case exception code is returned in *error_code)
+
+***********************************************************************/
+
+
+/* FUNCTION 0x01   - Read Coils
+ * Bits are stored on an int array, one bit per int.
+ */
+inline int read_output_bits(u8  slave,
+                            u16 start_addr,
+                            u16 count,
+                            u16 *dest,
+                            int dest_size,
+                            int fd,
+                            int send_retries,
+                            u8  *error_code,
+                            const struct timespec *response_timeout,
+                            pthread_mutex_t *data_access_mutex);
+#define read_coils(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10) \
+        read_output_bits(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10)
+
+ 
+/* FUNCTION 0x01   - Read Coils
+ * Bits are stored on an u32 array, 32 bits per u32.
+ * Unused bits in last u32 are set to 0.
+ */
+inline int read_output_bits_u32(u8  slave,
+                                u16 start_addr,
+                                u16 count,
+                                u32 *dest,
+                                int fd,
+                                int send_retries,
+                                u8  *error_code,
+                                const struct timespec *response_timeout);
+#define read_coils_u32(p1,p2,p3,p4,p5,p6,p7,p8) \
+        read_output_bits_u32(p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+
+
+/* FUNCTION 0x02   - Read Discrete Inputs
+ * Bits are stored on an int array, one bit per int.
+ */
+inline int read_input_bits(u8  slave,
+                           u16 start_addr,
+                           u16 count,
+                           u16 *dest,
+                           int dest_size,
+                           int fd,
+                           int send_retries,
+                           u8  *error_code,
+                           const struct timespec *response_timeout,
+                           pthread_mutex_t *data_access_mutex);
+#define read_discrete_inputs(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10) \
+        read_input_bits     (p1,p2,p3,p4,p5,p6,p7,p8,p9,p10)
+
+
+/* FUNCTION 0x02   - Read Discrete Inputs
+ * Bits are stored on an u32 array, 32 bits per u32.
+ * Unused bits in last u32 are set to 0.
+ */
+inline int read_input_bits_u32(u8  slave,
+                               u16 start_addr,
+                               u16 count,
+                               u32 *dest,
+                               int fd,
+                               int send_retries,
+                               u8  *error_code,
+                               const struct timespec *response_timeout);
+#define read_discrete_inputs_u32(p1,p2,p3,p4,p5,p6,p7,p8) \
+        read_input_bits_u32     (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+        
+
+/* FUNCTION 0x03   - Read Holding Registers */
+inline int read_output_words(u8  slave,
+                             u16 start_addr,
+                             u16 count,
+                             u16 *dest,
+                             int dest_size,
+                             int fd,
+                             int send_retries,
+                             u8  *error_code,
+                             const struct timespec *response_timeout,
+                             pthread_mutex_t *data_access_mutex);
+#define read_holding_registers(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10) \
+        read_output_words     (p1,p2,p3,p4,p5,p6,p7,p8,p9,p10)
+
+
+/* FUNCTION 0x03   - Read Holding Registers
+ * u16 registers are stored in array of u32, two registers per u32.
+ * Unused bits of last u32 element are set to 0.
+ */
+inline int read_output_words_u32(u8  slave,
+                                 u16 start_addr,
+                                 u16 count,
+                                 u32 *dest,
+                                 int fd,
+                                 int send_retries,
+                                 u8  *error_code,
+                                 const struct timespec *response_timeout);
+#define read_holding_registers_u32(p1,p2,p3,p4,p5,p6,p7,p8) \
+        read_output_words_u32     (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+/* FUNCTION 0x03   - Read Holding Registers
+ * return the array with the data to the calling function
+ */
+inline int read_output_words_u16_ref(u8  slave,
+                                     u16 start_addr,
+                                     u16 count,
+                                     u16 **dest,
+                                     int ttyfd,
+                                     int send_retries,
+                                     u8  *error_code,
+                                     const struct timespec *response_timeout);
+#define read_holding_registers_u16_ref(p1,p2,p3,p4,p5,p6,p7,p8) \
+        read_output_words_u16_ref     (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+
+/* FUNCTION 0x04   - Read Input Registers */
+inline int read_input_words(u8  slave,
+                            u16 start_addr,
+                            u16 count,
+                            u16 *dest,
+                            int dest_size,
+                            int fd,
+                            int send_retries,
+                            u8  *error_code,
+                            const struct timespec *response_timeout,
+                            pthread_mutex_t *data_access_mutex);
+#define read_input_registers(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10) \
+        read_input_words    (p1,p2,p3,p4,p5,p6,p7,p8,p9,p10)
+
+
+
+/* FUNCTION 0x04   - Read Input Registers
+ * u16 registers are stored in array of u32, two registers per u32.
+ * Unused bits of last u32 element are set to 0.
+ */
+inline int read_input_words_u32(u8  slave,
+                                u16 start_addr,
+                                u16 count,
+                                u32 *dest,
+                                int fd,
+                                int send_retries,
+                                u8  *error_code,
+                                const struct timespec *response_timeout);
+#define read_input_registers_u32(p1,p2,p3,p4,p5,p6,p7,p8) \
+        read_input_words_u32    (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+
+/* FUNCTION 0x04   - Read Input Registers
+ * return the array with the data to the calling function
+ */
+inline int read_input_words_u16_ref(u8  slave,
+                                    u16 start_addr,
+                                    u16 count,
+                                    u16 **dest,
+                                    int ttyfd,
+                                    int send_retries,
+                                    u8  *error_code,
+                                    const struct timespec *response_timeout);
+#define read_input_registers_u16_ref(p1,p2,p3,p4,p5,p6,p7,p8) \
+        read_input_words_u16_ref    (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+
+
+/* FUNCTION 0x05   - Force Single Coil */
+inline int write_output_bit(u8  slave,
+                            u16 coil_addr,
+                            u16 state,
+                            int fd,
+                            int send_retries,
+                            u8  *error_code,
+                            const struct timespec *response_timeout,
+                            pthread_mutex_t *data_access_mutex);
+#define force_single_coil(p1,p2,p3,p4,p5,p6,p7,p8) \
+        write_output_bit (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+
+
+
+
+/* FUNCTION 0x06   - Write Single Register */
+inline int write_output_word(u8  slave,
+                             u16 reg_addr,
+                             u16 value,
+                             int fd,
+                             int send_retries,
+                             u8  *error_code,
+                             const struct timespec *response_timeout,
+                             pthread_mutex_t *data_access_mutex);
+#define write_single_register(p1,p2,p3,p4,p5,p6,p7,p8) \
+        write_output_word    (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+
+
+
+/* FUNCTION 0x0F   - Force Multiple Coils */
+int write_output_bits(u8  slave,
+                      u16 start_addr,
+                      u16 coil_count,
+                      u16 *data,
+                      int fd,
+                      int send_retries,
+                      u8  *error_code,
+                      const struct timespec *response_timeout,
+                      pthread_mutex_t *data_access_mutex);
+#define force_multiple_coils(p1,p2,p3,p4,p5,p6,p7,p8,p9) \
+        write_output_bits   (p1,p2,p3,p4,p5,p6,p7,p8,p9)
+
+
+/* FUNCTION 0x0F   - Force Multiple Coils
+ * Bits should be stored on an u32 array, 32 bits per u32.
+ * Unused bits in last u32 should be set to 0.
+ */
+int write_output_bits_u32(u8  slave,
+                          u16 start_addr,
+                          u16 coil_count,
+                          u32 *data,
+                          int fd,
+                          int send_retries,
+                          u8  *error_code,
+                          const struct timespec *response_timeout);
+#define force_multiple_coils_u32(p1,p2,p3,p4,p5,p6,p7,p8) \
+        write_output_bits_u32   (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+
+/* FUNCTION 0x10   - Force Multiple Registers */
+int write_output_words(u8  slave,
+                       u16 start_addr,
+                       u16 reg_count,
+                       u16 *data,
+                       int fd,
+                       int send_retries,
+                       u8  *error_code,
+                       const struct timespec *response_timeout,
+                       pthread_mutex_t *data_access_mutex);
+#define force_multiple_registers(p1,p2,p3,p4,p5,p6,p7,p8,p9) \
+        write_output_words      (p1,p2,p3,p4,p5,p6,p7,p8,p9)
+
+
+
+/* FUNCTION 0x10   - Force Multiple Registers
+ * u16 registers are stored in array of u32, two registers per u32.
+ * Unused bits of last u32 element are set to 0.
+ */
+int write_output_words_u32(u8  slave,
+                           u16 start_addr,
+                           u16 reg_count,
+                           u32 *data,
+                           int fd,
+                           int send_retries,
+                           u8  *error_code,
+                           const struct timespec *response_timeout);
+
+#define force_multiple_registers_u32(p1,p2,p3,p4,p5,p6,p7,p8) \
+        write_output_words_u32      (p1,p2,p3,p4,p5,p6,p7,p8)
+
+
+
+
+
+
+/* Initialise the Modbus Library to work as Master only */
+int mb_master_init(int nd_count);
+/* Shut down the Modbus Library */
+int mb_master_done(void);
+
+
+
+/* Establish a connection to a remote server/slave.
+ * The address type (naf_tcp, naf_rtu, naf_ascii) specifies the lower
+ * layer to use for the newly opened node.
+ */
+int mb_master_connect(node_addr_t node_addr);
+/* Shut down a connection to a remote server/slave */
+int mb_master_close(int nd);
+
+
+
+
+/* Tell the library that communications will be suspended for some time. */
+/* RTU and ASCII versions ignore this function
+ * TCP version closes all the open tcp connections (connections are automatically
+ *   re-established the next time an IO function to the slave is requested).
+ *   To be more precise, the TCP version makes an estimate of how long
+ *   the silence will be based on previous invocations to this exact same
+ *   function, and will only close the connections if this silence is
+ *   expected to be longer than 1 second!
+ */
+int mb_master_tcp_silence_init(void);
+
+
+
+#endif  /* MODBUS_MASTER_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_master_private.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#ifndef MODBUS_MASTER_PRIVATE_H
+#define MODBUS_MASTER_PRIVATE_H
+
+#include "mb_util.h"
+#include "mb_master.h"
+
+
+
+
+#define DEF_LAYER2_SEND_RETRIES 1
+
+#define DEF_IGNORE_ECHO         0
+
+#define DEF_OPTIMIZATION        optimize_speed
+
+
+int mb_master_init__(int extra_bytes);
+int mb_master_done__(void);
+
+
+#endif  /* MODBUS_MASTER_PRIVATE_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_rtu.c	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,2116 @@
+/*
+ * Copyright (c) 2001,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#include <fcntl.h>      /* File control definitions */
+#include <stdio.h>      /* Standard input/output */
+#include <string.h>
+#include <stdlib.h>
+#include <termio.h>     /* POSIX terminal control definitions */
+#include <sys/time.h>   /* Time structures for select() */
+#include <unistd.h>     /* POSIX Symbolic Constants */
+#include <assert.h>
+#include <errno.h>      /* Error definitions */
+#include <time.h>       /* clock_gettime()   */
+#include <limits.h>     /* required for INT_MAX */
+
+#include <netinet/in.h> /* required for htons() and ntohs() */
+
+#include "mb_layer1.h"  /* The public interface this file implements... */
+#include "mb_rtu_private.h"
+
+
+#define ERRMSG
+#define ERRMSG_HEAD "ModbusRTU: "
+
+// #define DEBUG   /* uncomment to see the data sent and received */
+
+#ifdef DEBUG
+#ifndef ERRMSG
+#define ERRMSG
+#endif
+#endif
+
+
+#define SAFETY_MARGIN 10
+
+/************************************/
+/**                                **/
+/** Include common code...         **/
+/**                                **/
+/************************************/
+
+#include "mb_ds_util.h"    /* data structures... */
+#include "mb_time_util.h"  /* time conversion routines... */
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****                Forward Declarations                  ****/
+/****                    and Defaults                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+ /* CRC funtions... */
+typedef u16 (*crc_func_t)(u8 *buf, int cnt);
+static u16 crc_slow(u8 *buf, int cnt);
+static u16 crc_fast(u8 *buf, int cnt);
+
+ /* slow version does not need to be initialised, so we use it as default. */
+#define DEF_CRC_FUNCTION crc_slow
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****              Local Utility functions...              ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+/************************************/
+/**                                **/
+/** Miscelaneous Utility functions **/
+/**                                **/
+/************************************/
+
+/*
+ * Functions to convert u16 variables
+ * between network and host byte order
+ *
+ * NOTE: Modbus uses MSByte first, just like
+ *       tcp/ip, so we use the htons() and
+ *       ntoh() functions to guarantee
+ *       code portability.
+ */
+static inline u16 mb_hton(u16 h_value) 
+  {return htons(h_value);} /*  return h_value; */
+
+static inline u16 mb_ntoh(u16 m_value)
+  {return ntohs(m_value);} /*  return m_value; */
+
+/*  return Most Significant Byte of value; */
+static inline u8 msb(u16 value)
+  {return (value >> 8) & 0xFF;}
+
+/*  return Least Significant Byte of value; */
+static inline u8 lsb(u16 value)
+  {return value & 0xFF;}
+
+#define u16_v(char_ptr)  (*((u16 *)(&(char_ptr))))
+
+
+
+/**************************************/
+/**                                  **/
+/**    Initialise a termios struct   **/
+/**                                  **/
+/**************************************/
+static int termios_init(struct termios *tios,
+                        int baud,
+                        int parity,
+                        int data_bits,
+                        int stop_bits) {
+  speed_t baud_rate;
+
+  if (tios == NULL)
+    return -1;
+
+  /* reset all the values... */
+  /* NOTE: the following are initialised later on...
+  tios->c_iflag = 0;
+  tios->c_oflag = 0;
+  tios->c_cflag = 0;
+  tios->c_lflag = 0;
+  */
+  tios->c_line  = 0;
+
+ /* The minimum number of characters that should be received
+  * to satisfy a call to read().
+  */
+  tios->c_cc[VMIN ] = 0;
+
+ /* The maximum inter-arrival interval between two characters,
+  * in deciseconds.
+  *
+  * NOTE: we could use this to detect the end of RTU frames,
+  *       but we prefer to use select() that has higher resolution,
+  *       even though this higher resolution is most probably not
+  *       supported, and the effective resolution is 10ms,
+  *       one tenth of a decisecond.
+  */
+  tios->c_cc[VTIME] = 0;
+
+  /* configure the input modes... */
+  tios->c_iflag =  IGNBRK |  /* ignore BREAK condition on input         */
+                   IGNPAR |  /* ignore framing errors and parity errors */
+                   IXANY;    /* enable any character to restart output  */
+  /*               BRKINT       Only active if IGNBRK is not set.
+   *                            generate SIGINT on BREAK condition,
+   *                            otherwise read BREAK as character \0.
+   *               PARMRK       Only active if IGNPAR is not set.
+   *                            replace bytes with parity errors with
+   *                            \377 \0, instead of \0.
+   *               INPCK        enable input parity checking
+   *               ISTRIP       strip off eighth bit
+   *               IGNCR        ignore carriage return on input
+   *               INLCR        only active if IGNCR is not set.
+   *                            translate newline to carriage return  on input
+   *               ICRNL        only active if IGNCR is not set.
+   *                            translate carriage return to newline on input
+   *               IUCLC        map uppercase characters to lowercase on input
+   *               IXON         enable XON/XOFF flow control on output
+   *               IXOFF        enable XON/XOFF flow control on input
+   *               IMAXBEL      ring bell when input queue is full
+   */
+
+  /* configure the output modes... */
+  tios->c_oflag = OPOST;     /* enable implementation-defined output processing */
+  /*              ONOCR         don't output CR at column 0
+   *              OLCUC         map lowercase characters to uppercase on output
+   *              ONLCR         map NL to CR-NL on output
+   *              OCRNL         map CR to NL on output
+   *              OFILL         send fill characters for a delay, rather than
+   *                            using a timed delay
+   *              OFDEL         fill character is ASCII DEL. If unset, fill
+   *                            character is ASCII NUL
+   *              ONLRET        don't output CR
+   *              NLDLY         NL delay mask. Values are NL0 and NL1.
+   *              CRDLY         CR delay mask. Values are CR0, CR1, CR2, or CR3.
+   *              TABDLY        horizontal tab delay mask. Values are TAB0, TAB1,
+   *                            TAB2, TAB3, or XTABS. A value of XTABS expands
+   *                            tabs to spaces (with tab stops every eight columns).
+   *              BSDLY         backspace delay mask. Values are BS0 or BS1.
+   *              VTDLY         vertical tab delay mask. Values are VT0 or VT1.
+   *              FFDLY         form feed delay mask. Values are FF0 or FF1.
+   */
+
+  /* configure the control modes... */
+  tios->c_cflag = CREAD |    /* enable receiver.               */
+                  CLOCAL;    /* ignore modem control lines     */
+  /*              HUPCL         lower modem control lines after last process
+   *                            closes the device (hang up).
+   *              CRTSCTS       flow control (Request/Clear To Send).
+   */
+       if (data_bits == 5) tios->c_cflag |= CS5;
+  else if (data_bits == 6) tios->c_cflag |= CS6;
+  else if (data_bits == 7) tios->c_cflag |= CS7;
+  else if (data_bits == 8) tios->c_cflag |= CS8;
+  else return -1;
+
+       if (stop_bits == 1) tios->c_cflag &=~ CSTOPB;
+  else if (stop_bits == 2) tios->c_cflag |= CSTOPB;
+  else return -1;
+
+         if(parity == 0) { /* none */
+    tios->c_cflag &=~ PARENB;
+    tios->c_cflag &=~ PARODD;
+  } else if(parity == 2)  { /* even */
+    tios->c_cflag |=  PARENB;
+    tios->c_cflag &=~ PARODD;
+  } else if(parity == 1)  { /* odd */
+    tios->c_cflag |=  PARENB;
+    tios->c_cflag |=  PARODD;
+  } else return -1;
+
+
+  /* configure the local modes... */
+  tios->c_lflag = IEXTEN;    /* enable implementation-defined input processing   */
+  /*              ISIG          when any of the characters INTR, QUIT, SUSP, or DSUSP
+   *                            are received, generate the corresponding signal.
+   *              ICANON        enable canonical mode. This enables the special
+   *                            characters EOF, EOL, EOL2, ERASE, KILL, REPRINT,
+   *                            STATUS, and WERASE, and buffers by lines.
+   *              ECHO          echo input characters.
+   */
+
+  /* Set the baud rate */
+  /* Must be done before reseting all the values to 0! */
+  switch(baud) {
+    case 110:    baud_rate = B110;    break;
+    case 300:    baud_rate = B300;    break;
+    case 600:    baud_rate = B600;    break;
+    case 1200:   baud_rate = B1200;   break;
+    case 2400:   baud_rate = B2400;   break;
+    case 4800:   baud_rate = B4800;   break;
+    case 9600:   baud_rate = B9600;   break;
+    case 19200:  baud_rate = B19200;  break;
+    case 38400:  baud_rate = B38400;  break;
+    case 57600:  baud_rate = B57600;  break;
+    case 115200: baud_rate = B115200; break;
+    default: return -1;
+  } /* switch() */
+
+  if ((cfsetispeed(tios, baud_rate) < 0) ||
+      (cfsetospeed(tios, baud_rate) < 0))
+    return -1;;
+
+  return 0;
+}
+
+
+/************************************/
+/**                                **/
+/** A data structure - recv buffer **/
+/**                                **/
+/************************************/
+
+/* A data structutre used for the receive buffer, i.e. the buffer
+ * that stores the bytes we receive from the bus.
+ *
+ * What we realy needed here is an unbounded buffer. This may be
+ * implemented by:
+ *   - a circular buffer the size of the maximum frame length
+ *   - a linear buffer somewhat larger than the maximum frame length
+ *
+ * Due to the fact that this library's API hands over the frame data
+ * in a linear buffer, and also reads the data (i,e, calls to read())
+ * into a linear buffer:
+ *  - the circular buffer would be more efficient in aborted frame
+ *    situations
+ *  - the linear is more efficient when no aborted frames are recieved.
+ *
+ * I have decided to optimize for the most often encountered situation,
+ * i.e. when no aborted frames are received.
+ *
+ * The linear buffer has a size larger than the maximum
+ * number of bytes we intend to store in it. We simply start ignoring
+ * the first bytes in the buffer in which we are not interested in, and
+ * continue with the extra bytes of the buffer. When we reach the limit
+ * of these extra bytes, we shift the data down so it once again
+ * uses the first bytes of the buffer. The more number of extra bytes,
+ * the more efficient it will be.
+ *
+ * Note that if we don't receive any aborted frames, it will work as a
+ * simple linear buffer, and no memory shifts will be required!
+ */
+
+typedef struct {
+        lb_buf_t data_buf;
+          /* Flag:
+            *  1 => We have detected a frame boundary using 3.5 character silence
+            *  0 => We have not yet detected any frame boundary
+           */
+        int found_frame_boundary; /* ==1 => valid data ends at a frame boundary. */
+          /* Flag:
+            *  Used in the call to search_for_frame() as the history parameter!
+           */
+        int frame_search_history;
+        } recv_buf_t;
+
+/* A small auxiliary function... */
+static inline u8 *recv_buf_init(recv_buf_t *buf, int size, int max_data_start) {
+  buf->found_frame_boundary = 0;
+  buf->frame_search_history = 0;
+  return lb_init(&buf->data_buf, size, max_data_start);
+}
+
+
+/* A small auxiliary function... */
+static inline void recv_buf_done(recv_buf_t *buf) {
+  buf->found_frame_boundary = 0;
+  buf->frame_search_history = 0;
+  lb_done(&buf->data_buf);
+}
+
+
+/* A small auxiliary function... */
+static inline void recv_buf_reset(recv_buf_t *buf) {
+  buf->found_frame_boundary = 0;
+  buf->frame_search_history = 0;
+  lb_data_purge_all(&buf->data_buf);
+}
+
+
+/************************************/
+/**                                **/
+/**  A data structure - nd entry   **/
+/**                                **/
+/************************************/
+
+/* NOTE: nd = node descriptor */
+
+typedef struct {
+         /* The file descriptor associated with this node */
+         /* NOTE: if the node is not yet in use, i.e. if the node is free,
+          *       then fd will be set to -1
+          */
+        int    fd;
+
+         /* the time it takes to transmit 1.5 characters at the current baud rate */
+        struct timeval time_15_char_;
+         /* the time it takes to transmit 3.5 characters at the current baud rate */
+        struct timeval time_35_char_;
+
+         /* Due to the algorithm used to work around aborted frames, the modbus_read()
+          * function might read beyond the current modbus frame. The extra bytes
+          * must be stored for the subsequent call to modbus_read().
+          */
+        recv_buf_t recv_buf_;
+
+          /* The old settings of the serial port, to be reset when the library is closed... */
+        struct termios old_tty_settings_;
+
+          /* ignore echo flag.
+           * If set to 1, then it means that we will be reading every byte we
+           * ourselves write out to the bus, so we must ignore those bytes read
+           * before we really read the data sent by remote nodes.
+           *
+           * This comes in useful when using a RS232-RS485 converter that does
+           * not correctly control the RTS-CTS lines...
+           */
+        int ignore_echo;
+ } nd_entry_t;
+
+
+static inline void nd_entry_init(nd_entry_t *nde) {
+  nde->fd = -1; /* The node is free... */
+}
+
+
+
+static int nd_entry_connect(nd_entry_t *nde,
+                            node_addr_t *node_addr,
+                            optimization_t opt) {
+
+  int parity_bits, start_bits, char_bits;
+  struct termios settings;
+  int buf_size;
+
+  /*
+  if (nde == NULL)
+    goto error_exit_0;
+  */
+  if (nde->fd >= 0)
+    goto error_exit_0;
+
+  /* initialise the termios data structure */
+  if (termios_init(&settings,
+                   node_addr->addr.rtu.baud,
+                   node_addr->addr.rtu.parity,
+                   node_addr->addr.rtu.data_bits,
+                   node_addr->addr.rtu.stop_bits)
+      < 0) {
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "Invalid serial line settings"
+                    "(baud=%d, parity=%d, data_bits=%d, stop_bits=%d)\n",
+                    node_addr->addr.rtu.baud,
+                    node_addr->addr.rtu.parity,
+                    node_addr->addr.rtu.data_bits,
+                    node_addr->addr.rtu.stop_bits);
+#endif
+    goto error_exit_1;
+  }
+
+    /* set the ignore_echo flag */
+  nde->ignore_echo = node_addr->addr.rtu.ignore_echo;
+
+    /* initialise recv buffer */
+  buf_size = (opt == optimize_size)?RECV_BUFFER_SIZE_SMALL:
+                                    RECV_BUFFER_SIZE_LARGE;
+  if (recv_buf_init(&nde->recv_buf_, buf_size, buf_size - MAX_RTU_FRAME_LENGTH)
+      == NULL) {
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "Out of memory: error initializing receive buffer\n");
+#endif
+    goto error_exit_2;
+  }
+
+    /* open the serial port */
+  if((nde->fd = open(node_addr->addr.rtu.device, O_RDWR | O_NOCTTY | O_NDELAY))
+     < 0) {
+#ifdef ERRMSG
+    perror("open()");
+    fprintf(stderr, ERRMSG_HEAD "Error opening device %s\n",
+                   node_addr->addr.rtu.device);
+#endif
+    goto error_exit_3;
+  }
+
+  if(tcgetattr(nde->fd, &nde->old_tty_settings_) < 0) {
+#ifdef ERRMSG
+    perror("tcgetattr()");
+    fprintf(stderr, ERRMSG_HEAD "Error reading device's %s original settings.\n",
+                      node_addr->addr.rtu.device);
+#endif
+    goto error_exit_4;
+  }
+
+  if(tcsetattr(nde->fd, TCSANOW, &settings) < 0) {
+#ifdef ERRMSG
+    perror("tcsetattr()");
+    fprintf(stderr, ERRMSG_HEAD "Error configuring device %s "
+                    "(baud=%d, parity=%d, data_bits=%d, stop_bits=%d)\n",
+                    node_addr->addr.rtu.device,
+                    node_addr->addr.rtu.baud,
+                    node_addr->addr.rtu.parity,
+                    node_addr->addr.rtu.data_bits,
+                    node_addr->addr.rtu.stop_bits);
+#endif
+    goto error_exit_4;
+  }
+
+  parity_bits   = (node_addr->addr.rtu.parity == 0)?0:1;
+  start_bits    = 1;
+  char_bits     = start_bits  + node_addr->addr.rtu.data_bits +
+                  parity_bits + node_addr->addr.rtu.stop_bits;
+  nde->time_15_char_ = d_to_timeval(SAFETY_MARGIN*1.5*char_bits/node_addr->addr.rtu.baud);
+  nde->time_35_char_ = d_to_timeval(SAFETY_MARGIN*3.5*char_bits/node_addr->addr.rtu.baud);
+
+#ifdef DEBUG
+  fprintf(stderr, "nd_entry_connect(): %s ope{.node=NULL, .node_count=0};n\n", node_addr->addr.rtu.device );
+  fprintf(stderr, "nd_entry_connect(): returning fd=%d\n", nde->fd);
+#endif
+  return nde->fd;
+
+  error_exit_4:
+    close(nde->fd);
+  error_exit_3:
+    recv_buf_done(&nde->recv_buf_);
+  error_exit_2:
+  error_exit_1:
+    nde->fd = -1; /* set the node as free... */
+  error_exit_0:
+    return -1;
+}
+
+
+
+static int nd_entry_free(nd_entry_t *nde) {
+  if (nde->fd < 0)
+    /* already free */
+    return -1;
+
+  /* reset the tty device old settings... */
+#ifdef ERRMSG
+  int res =
+#endif
+            tcsetattr(nde->fd, TCSANOW, &nde->old_tty_settings_);
+#ifdef ERRMSG
+  if(res < 0)
+    fprintf(stderr, ERRMSG_HEAD "Error reconfiguring serial port to it's original settings.\n");
+#endif
+
+  recv_buf_done(&nde->recv_buf_);
+  close(nde->fd);
+  nde->fd = -1;
+
+  return 0;
+}
+
+
+
+
+static inline int nd_entry_is_free(nd_entry_t *nde) {
+  return (nde->fd < 0);
+}
+
+
+
+
+/************************************/
+/**                                **/
+/**  A data structure - nd table   **/
+/**                                **/
+/************************************/
+
+typedef struct {
+      /* the array of node descriptors, and current size... */
+    nd_entry_t *node;
+    int        node_count;      /* total number of nodes in the node[] array */
+} nd_table_t;
+
+
+#if 1
+/* nd_table_init()
+ *   Version 1 of the nd_table_init() function. 
+ *   If called more than once, 2nd and any subsequent calls will
+ *   be interpreted as a request to confirm that it was already correctly 
+ *   initialized with the requested number of nodes.
+ */
+static int nd_table_init(nd_table_t *ndt, int nd_count) {
+  int count;
+
+  if (ndt->node != NULL) {
+    /* this function has already been called, and the node table is already initialised */
+    return (ndt->node_count == nd_count)?0:-1;
+  }
+
+  /* initialise the node descriptor metadata array... */
+  ndt->node = malloc(sizeof(nd_entry_t) * nd_count);
+  if (ndt->node == NULL) {
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "Out of memory: error initializing node address buffer\n");
+#endif
+    return -1;
+  }
+  ndt->node_count = nd_count;
+
+    /* initialise the state of each node in the array... */
+  for (count = 0; count < ndt->node_count; count++) {
+    nd_entry_init(&ndt->node[count]);
+  } /* for() */
+
+  return nd_count; /* number of succesfully created nodes! */
+}
+#else
+/* nd_table_init()
+ *   Version 2 of the nd_table_init() function. 
+ *   If called more than once, 2nd and any subsequent calls will
+ *   be interpreted as a request to reserve an extra new_nd_count
+ *   number of nodes. This will be done using realloc().
+ */
+static int nd_table_init(nd_table_t *ndt, int new_nd_count) {
+  int count;
+
+  /* initialise the node descriptor metadata array... */
+  ndt->node = realloc(ndt->node, sizeof(nd_entry_t) * (ndt->node_count + new_nd_count));
+  if (ndt->node == NULL) {
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "Out of memory: error initializing node address buffer\n");
+#endif
+    return -1;
+  }
+
+  /* initialise the state of each newly added node in the array... */
+  for (count = ndt->node_count; count < ndt->node_count + new_nd_count; count++) {
+    nd_entry_init(&ndt->node[count]);
+  } /* for() */
+  ndt->node_count += new_nd_count;
+
+  return new_nd_count; /* number of succesfully created nodes! */
+}
+#endif
+
+
+static inline nd_entry_t *nd_table_get_nd(nd_table_t *ndt, int nd) {
+  if ((nd < 0) || (nd >= ndt->node_count))
+    return NULL;
+
+  return &ndt->node[nd];
+}
+
+
+static inline void nd_table_done(nd_table_t *ndt) {
+  int i;
+
+  if (ndt->node == NULL) 
+    return;
+
+    /* close all the connections... */
+  for (i = 0; i < ndt->node_count; i++)
+    nd_entry_free(&ndt->node[i]);
+
+  /* Free memory... */
+  free(ndt->node);
+  *ndt = (nd_table_t){.node=NULL, .node_count=0};
+}
+
+
+
+static inline int nd_table_get_free_nd(nd_table_t *ndt) {
+  int count;
+
+  for (count = 0; count < ndt->node_count; count++) {
+    if (nd_entry_is_free(&ndt->node[count]))
+      return count;
+  }
+
+   /* none found... */
+  return -1;
+}
+
+
+static inline int nd_table_free_nd(nd_table_t *ndt, int nd) {
+  if ((nd < 0) || (nd >= ndt->node_count))
+    return -1;
+
+  return nd_entry_free(&ndt->node[nd]);
+}
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****                Global Library State                  ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+ /* The node descriptor table... */
+ /* NOTE: This variable must be correctly initialised here!! */
+static nd_table_t nd_table_ = {.node=NULL, .node_count=0};
+
+ /* The optimization choice... */
+static optimization_t optimization_;
+
+ /* the crc function currently in use... */
+ /* This will depend on the optimisation choice... */
+crc_func_t crc_calc = DEF_CRC_FUNCTION;
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****                      CRC functions                   ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+#if     RTU_FRAME_CRC_LENGTH < 2
+#error  The CRC on modbus RTU frames requires at least 2 bytes in the frame length.
+#endif
+
+
+/************************************/
+/**                                **/
+/**     Read the CRC of a frame    **/
+/**                                **/
+/************************************/
+
+/* NOTE: cnt is number of bytes in the frame _excluding_ CRC! */
+static inline u16 crc_read(u8 *buf, int cnt) {
+  /* For some strange reason, the crc is transmited
+   * LSB first, unlike all other values...
+   */
+  return (buf[cnt + 1] << 8) | buf[cnt];
+}
+
+
+/************************************/
+/**                                **/
+/**    Write the CRC of a frame    **/
+/**                                **/
+/************************************/
+
+/* NOTE: cnt is number of bytes in the frame _excluding_ CRC! */
+static inline void crc_write(u8 *buf, int cnt) {
+  /* For some strange reason, the crc is transmited
+   * LSB first, unlike all other values...
+   *
+   * u16_v(query[string_length]) = mb_hton(temp_crc);  -> This is wrong !!
+   */
+  /* NOTE: We have already checked above that RTU_FRAME_CRC_LENGTH is >= 2 */
+  u16 crc = crc_calc(buf, cnt);
+  buf[cnt]   = lsb(crc);
+  buf[cnt+1] = msb(crc);
+}
+
+
+
+/************************************/
+/**                                **/
+/**     A slow version of the      **/
+/**          CRC function          **/
+/**                                **/
+/************************************/
+
+/* crc optimized for smallest memory footprint */
+static u16 crc_slow(u8 *buf, int cnt)
+{
+  int bit;
+  u16 temp,flag;
+
+  temp=0xFFFF;
+
+  while (cnt-- != 0) {
+    temp=temp ^ *buf++;
+    for (bit=1; bit<=8; bit++) {
+      flag = temp & 0x0001;
+      /* NOTE:
+       *  - since temp is unsigned, we are guaranteed a zero in MSbit;
+       *  - if it were signed, the value placed in the MSbit would be
+       *      compiler dependent!
+       */
+      temp >>= 1;
+      if (flag)
+        temp=temp ^ 0xA001;
+    }
+  }
+  return(temp);
+}
+
+
+
+
+/************************************/
+/**                                **/
+/**     A fast version of the      **/
+/**          CRC function          **/
+/**                                **/
+/************************************/
+static u8 *crc_fast_buf = NULL;
+
+/* crc optimized for speed */
+static u16 crc_fast(u8 *buf, int cnt)
+{
+/* NOTE: The following arrays have been replaced by an equivalent
+ *      array (crc_fast_buf[]) initialised at run-time.
+ */
+/*
+  static u8 buf_lsb[] = {0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
+                         0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40
+                        };
+
+  static u8 buf_msb[] = {0x00, 0xc0, 0xc1, 0x01, 0xc3, 0x03, 0x02, 0xc2,
+                         0xc6, 0x06, 0x07, 0xc7, 0x05, 0xc5, 0xc4, 0x04,
+                         0xcc, 0x0c, 0x0d, 0xcd, 0x0f, 0xcf, 0xce, 0x0e,
+                         0x0a, 0xca, 0xcb, 0x0b, 0xc9, 0x09, 0x08, 0xc8,
+                         0xd8, 0x18, 0x19, 0xd9, 0x1b, 0xdb, 0xda, 0x1a,
+                         0x1e, 0xde, 0xdf, 0x1f, 0xdd, 0x1d, 0x1c, 0xdc,
+                         0x14, 0xd4, 0xd5, 0x15, 0xd7, 0x17, 0x16, 0xd6,
+                         0xd2, 0x12, 0x13, 0xd3, 0x11, 0xd1, 0xd0, 0x10,
+                         0xf0, 0x30, 0x31, 0xf1, 0x33, 0xf3, 0xf2, 0x32,
+                         0x36, 0xf6, 0xf7, 0x37, 0xf5, 0x35, 0x34, 0xf4,
+                         0x3c, 0xfc, 0xfd, 0x3d, 0xff, 0x3f, 0x3e, 0xfe,
+                         0xfa, 0x3a, 0x3b, 0xfb, 0x39, 0xf9, 0xf8, 0x38,
+                         0x28, 0xe8, 0xe9, 0x29, 0xeb, 0x2b, 0x2a, 0xea,
+                         0xee, 0x2e, 0x2f, 0xef, 0x2d, 0xed, 0xec, 0x2c,
+                         0xe4, 0x24, 0x25, 0xe5, 0x27, 0xe7, 0xe6, 0x26,
+                         0x22, 0xe2, 0xe3, 0x23, 0xe1, 0x21, 0x20, 0xe0,
+                         0xa0, 0x60, 0x61, 0xa1, 0x63, 0xa3, 0xa2, 0x62,
+                         0x66, 0xa6, 0xa7, 0x67, 0xa5, 0x65, 0x64, 0xa4,
+                         0x6c, 0xac, 0xad, 0x6d, 0xaf, 0x6f, 0x6e, 0xae,
+                         0xaa, 0x6a, 0x6b, 0xab, 0x69, 0xa9, 0xa8, 0x68,
+                         0x78, 0xb8, 0xb9, 0x79, 0xbb, 0x7b, 0x7a, 0xba,
+                         0xbe, 0x7e, 0x7f, 0xbf, 0x7d, 0xbd, 0xbc, 0x7c,
+                         0xb4, 0x74, 0x75, 0xb5, 0x77, 0xb7, 0xb6, 0x76,
+                         0x72, 0xb2, 0xb3, 0x73, 0xb1, 0x71, 0x70, 0xb0,
+                         0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
+                         0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54,
+                         0x9c, 0x5c, 0x5d, 0x9d, 0x5f, 0x9f, 0x9e, 0x5e,
+                         0x5a, 0x9a, 0x9b, 0x5b, 0x99, 0x59, 0x58, 0x98,
+                         0x88, 0x48, 0x49, 0x89, 0x4b, 0x8b, 0x8a, 0x4a,
+                         0x4e, 0x8e, 0x8f, 0x4f, 0x8d, 0x4d, 0x4c, 0x8c,
+                         0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86,
+                         0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
+                        };
+*/
+  u8 crc_msb = 0xFF;
+  u8 crc_lsb = 0xFF;
+  int index;
+
+  if (cnt <= 0) {
+    fprintf(stderr, "\nInternal program error in file %s at line %d\n\n\n", __FILE__, __LINE__);
+    exit(EXIT_FAILURE);
+  }
+
+  while (cnt-- != 0) {
+    index = 2 * (crc_lsb ^ *buf++);
+    crc_lsb = crc_msb ^ crc_fast_buf[index]/* buf_lsb[index/2] */;
+    crc_msb = crc_fast_buf[index + 1] /* buf_msb[index/2] */;
+  }
+
+  return crc_msb*0x0100 + crc_lsb;
+}
+
+
+/************************************/
+/**                                **/
+/**  init() and done() functions   **/
+/**      of fast CRC version       **/
+/**                                **/
+/************************************/
+
+static inline int crc_fast_init(void) {
+  int i;
+  u8  data[2];
+  u16 tmp_crc;
+
+  if ((crc_fast_buf = (u8 *)malloc(256 * 2)) == NULL)
+    return -1;
+
+  for (i = 0x00; i < 0x100; i++) {
+    data[0] = 0xFF;
+    data[1] = i;
+    data[1] = ~data[1];
+    tmp_crc = crc_slow(data, 2);
+    crc_fast_buf[2*i    ] = lsb(tmp_crc);
+    crc_fast_buf[2*i + 1] = msb(tmp_crc);
+  }
+
+  return 0;
+}
+
+
+static inline void crc_fast_done(void) {
+  free(crc_fast_buf);
+}
+
+
+/************************************/
+/**                                **/
+/**  init() and done() functions   **/
+/**         of generic CRC         **/
+/**                                **/
+/************************************/
+
+static inline int crc_init(optimization_t opt) {
+  switch (opt) {
+    case optimize_speed:
+      if (crc_fast_init() < 0)
+        return -1;
+      crc_calc = crc_fast;
+      return 0;
+    case optimize_size :
+      crc_calc = crc_slow;
+      return 0;
+    default:
+      return -1;
+  }
+
+  /* humour the compiler */
+  return -1;
+}
+
+
+static inline int crc_done(void) {
+  if (crc_calc == crc_fast)
+    crc_fast_done();
+
+  crc_calc = DEF_CRC_FUNCTION;
+  return 0;
+}
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****              Sending of Modbus RTU Frames            ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+/*  W A R N I N G
+ *  =============
+ *     The modbus_rtu_write() function assumes that the caller
+ *     has allocated a few bytes extra for the buffer containing
+ *     the data. These bytes will be used to write the crc.
+ *
+ *     The caller of this function MUST make sure that the data
+ *     buffer, although only containing data_length bytes, has
+ *     been allocated with a size equal to or larger than
+ *     data_length + RTU_FRAME_CRC_LENGTH bytes
+ *
+ *     I know, this is a very ugly hack, but we don't have much
+ *     choice (please read other comments further on for more
+ *     explanations)
+ *
+ *     We will nevertheless try and make this explicit by having the
+ *     library initialisation function (modbus_rtu_init() ) return a
+ *     value specifying how many extra bytes this buffer should have.
+ *     Maybe this way this very ugly hack won't go unnoticed, and we
+ *     won't be having any segmentation faults...!
+ * 
+ *     NOTE: for now the transmit_timeout is silently ignored in RTU version!
+ */
+int modbus_rtu_write(int    nd,
+                     u8    *data,
+                     size_t data_length,
+                     u16    transaction_id,
+                     const struct timespec *transmit_timeout
+                    )
+{
+  fd_set rfds;
+  struct timeval timeout;
+  int res, send_retries;
+  nd_entry_t *nd_entry;
+
+#ifdef DEBUG
+  fprintf(stderr, "modbus_rtu_write(fd=%d) called...\n", nd);
+#endif
+  /* check if nd is correct... */
+  if ((nd_entry = nd_table_get_nd(&nd_table_, nd)) == NULL)
+    return -1;
+
+  /* check if nd is initialzed... */
+  if (nd_entry->fd < 0)
+    return -1;
+
+  /**************************
+  * append crc to frame... *
+  **************************/
+/* WARNING:
+ *     The crc_write() function assumes that we have an extra
+ *     RTU_FRAME_CRC_LENGTH free bytes at the end of the *data
+ *     buffer.
+ *     The caller of this function had better make sure he has
+ *     allocated those extra bytes, or a segmentation fault will
+ *     occur.
+ *     Please read on why we leave this as it is...
+ *
+ * REASONS:
+ *     We want to write the data and the crc in a single call to
+ *     the OS. This is the only way we can minimally try to gurantee
+ *     that we will not be introducing a silence of more than 1.5
+ *     character transmission times between any two characters.
+ *
+ *     We could do the above using one of two methods:
+ *       (a) use a special writev() call in which the data
+ *           to be sent is stored in two buffers (one for the
+ *           data and the other for the crc).
+ *       (b) place all the data in a single linear buffer and
+ *           use the normal write() function.
+ *
+ *     We cannot use (a) since the writev(2) function does not seem
+ *     to be POSIX compliant...
+ *     (b) has the drawback that we would need to allocate a new buffer,
+ *      and copy all the data into that buffer. We have enough copying of
+ *      data between buffers as it is, so we won't be doing it here
+ *      yet again!
+ *
+ *      The only option that seems left over is to have the caller
+ *      of this function allocate a few extra bytes. Let's hope he
+ *      does not forget!
+*/
+  crc_write(data, data_length);
+  data_length += RTU_FRAME_CRC_LENGTH;
+
+#ifdef DEBUG
+/* Print the hex value of each character that is about to be
+ * sent over the bus.
+ */
+  { int i;
+    for(i = 0; i < data_length; i++)
+      fprintf(stderr, "[0x%2X]", data[i]);
+    fprintf(stderr, "\n");
+  }
+#endif
+   /* THE MAIN LOOP!!! */
+  /* NOTE: The modbus standard specifies that the message must
+   *       be sent continuosly over the wire with maximum
+   *       inter-character delays of 1.5 character intervals.
+   *
+   *       If the write() call is interrupted by a signal, then
+   *       this delay will most probably be exceeded. We should then
+   *       re-start writing the query from the begining.
+   *
+   *       BUT, can we really expect the write() call to return
+   *       query_length on every platform when no error occurs?
+   *       The write call would still be correct if it only wrote
+   *       1 byte at a time!
+   *
+   *       To protect ourselves getting into an infinte loop in the
+   *       above cases, we specify a maximum number of retries, and
+   *       hope for the best...! The worst will now be we simply do
+   *       not get to send out a whole frame, and will therefore always
+   *       fail on writing a modbus frame!
+   */
+  send_retries = RTU_FRAME_SEND_RETRY + 1; /* must try at least once... */
+  while (send_retries > 0) {
+
+    /*******************************
+     * synchronise with the bus... *
+     *******************************/
+    /* Remember that a RS485 bus is half-duplex, so we have to wait until
+     * nobody is transmitting over the bus for our turn to transmit.
+     * This will never happen on a modbus network if the master and
+     * slave state machines never get out of synch (granted, it probably
+     * only has two states, but a state machine nonetheless), but we want
+     * to make sure we can re-synchronise if they ever do get out of synch.
+     *
+     * The following lines will guarantee that we will re-synchronise our
+     * state machine with the current state of the bus.
+     *
+     * We first wait until the bus has been silent for at least
+     * char_interval_timeout (i.e. 3.5 character interval). We then flush
+     * any input and output that might be on the cache.
+     */
+      /* NOTES:
+       *   - we do not need to reset the rfds with FD_SET(ttyfd, &rfds)
+       *     before every call to select! We only wait on one file descriptor,
+       *     so if select returns succesfully, it must have that same file
+       *     decriptor set in the rdfs!
+       *     If select returns with a timeout, then we do not get to call
+       *     select again!
+       *   -  On Linux, timeout (i.e. timeout) is modified by select() to
+       *      reflect the amount of time not slept; most other implementations
+       *      do not do this. In the cases in which timeout is not modified,
+       *      we will simply have to wait for longer periods if select is
+       *      interrupted by a signal.
+       */
+    FD_ZERO(&rfds);
+    FD_SET(nd_entry->fd, &rfds);
+    timeout = nd_entry->time_35_char_;
+    while ((res = select(nd_entry->fd+1, &rfds, NULL, NULL, &timeout)) != 0) {
+      if (res > 0) {
+        /* we are receiving data over the serial port! */
+        /* Throw the data away!                        */
+        tcflush(nd_entry->fd, TCIFLUSH); /* flush the input stream */
+        /* reset the timeout value! */
+        timeout = nd_entry->time_35_char_;
+        /* We do not need to reset the FD SET here! */
+      } else {
+        /* some kind of error ocurred */
+        if (errno != EINTR)
+          /* we were not interrupted by a signal */
+          return -1;
+        /* We will be calling select() again.
+         * We need to reset the FD SET !
+         */
+        FD_ZERO(&rfds);
+        FD_SET(nd_entry->fd, &rfds);
+      }
+    } /* while (select()) */
+
+    /* Flush both input and output streams... */
+    /* NOTE: Due to the nature of the modbus protocol,
+     *       when a frame is sent all previous
+     *       frames that may have arrived at the sending node become
+     *       irrelevant.
+     */
+    tcflush(nd_entry->fd, TCIOFLUSH);       /* flush the input & output streams */
+    recv_buf_reset(&nd_entry->recv_buf_);   /* reset the recv buffer            */
+
+    /**********************
+     * write to output... *
+     **********************/
+     /* Please see the comment just above the main loop!! */
+    if ((res = write(nd_entry->fd, data, data_length)) != data_length) {
+      if ((res < 0) && (errno != EAGAIN ) && (errno != EINTR ))
+        return -1;
+    } else {
+      /* query succesfully sent! */
+      /* res == query_length     */
+
+      /*  NOTE: We do not flush the input stream after sending the frame!
+       *        If the process gets swapped out between the end of writing
+       *        to the serial port, and the call to flush the input of the
+       *        same serial port, the response to the modbus query may be
+       *        sent over between those two calls. This would result in the
+       *        tcflush(ttyfd, TCIFLUSH) call flushing out the response
+       *        to the query we have just sent!
+       *        Not a good thing at all... ;-)
+       */
+      return data_length - RTU_FRAME_CRC_LENGTH;
+    }
+    /* NOTE: The maximum inter-character delay of 1.5 character times
+     *       has most probably been exceeded, so we abort the frame and
+     *       retry again...
+     */
+    send_retries--;
+  } /* while()  MAIN LOOP */
+
+   /* maximum retries exceeded */
+  return -1;
+}
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****              Receiving Modbus RTU Frames             ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+#if     MIN_FRAME_LENGTH < 2
+#error  Modbus RTU frames have a minimum length larger than MIN_FRAME_LENGTH.
+#endif
+
+/************************************/
+/**                                **/
+/**     Guess length of frame      **/
+/**         being read.            **/
+/**                                **/
+/************************************/
+
+/*  Auxiliary function to the search_for_frame() function.
+ *
+ *  NOTE: data_byte_count must be >=2 for correct operation, therefore
+ *        the #error condition above.
+ *
+ *  Function to determine the length of the frame currently being read,
+ *  assuming it is a query/response frame.
+ *
+ *  The guess is obtained by analysing the bytes that have already been
+ *  read. Sometimes we cannot be sure what is the frame length, because
+ *  not enough bytes of the frame have been read yet (for example, frames
+ *  that have a byte_count value which has not yet been read). In these
+ *  cases we return not the frame length, but an error (-1).
+ *
+ *  If we find the data does not make any sense (i.e. it cannot be a valid
+ *  modbus frame), we return -1.
+ */
+static int frame_length(u8 *frame_data,
+                        int frame_data_length,
+                          /* The array containing the lengths of frames. */
+                          /*   - query_frame_length[]
+                           *   - response_frame_length[]
+                           */
+                        i8  *frame_length_array) {
+
+  u8  function_code;
+  int res;
+
+   /* check consistency of input parameters... */
+ /*
+  if ((frame_data == NULL) || (frame_length_array == NULL) || (frame_data_length < 2))
+    return -1;
+  */
+
+  function_code = frame_data[L2_FRAME_FUNCTION_OFS];
+
+   /* hard code the length of response to diagnostic function 8 (0x08), with
+    * subfunction 21 (0x15), and sub-sub-function (a.k.a. operation) 3 (0x03),
+    * which contains a byte count...
+    */
+  if ((function_code == 0x08) && (frame_length_array == response_frame_lengths)) {
+    if (frame_data_length < 4) {
+      /* not enough info to determine the sub-function... */
+      return -1;
+    } else {
+      if ((frame_data[2] == 0x00) && (frame_data[3] == 0x15)) {
+        /* we need a couple more bytes to figure out the sub-sub-function... */
+        if (frame_data_length < 6) {
+          /* not enough info to determine the sub-sub-function... */
+          return -1;
+        } else {
+          if ((frame_data[4] == 0x00) && (frame_data[5] == 0x03)) {
+            /* We have found a response frame to diagnostic sub-function ... */
+            if (frame_data_length < 8) {
+              /* not enough info to determine the frame length */
+              return -1;
+            } else {
+              return /*HEADER*/ 6 + mb_ntoh(u16_v(frame_data[6])) + RTU_FRAME_CRC_LENGTH;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  res = frame_length_array[function_code];
+
+  switch(res) {
+    case BYTE_COUNT_3 :
+      if (frame_data_length >= 3)
+        return BYTE_COUNT_3_HEADER  + frame_data[2] + RTU_FRAME_CRC_LENGTH;
+      break;
+    case BYTE_COUNT_34:
+      if (frame_data_length >= 4)
+        return BYTE_COUNT_34_HEADER + mb_ntoh(u16_v(frame_data[2])) + RTU_FRAME_CRC_LENGTH;
+      break;
+    case BYTE_COUNT_7 :
+      if (frame_data_length >= 7)
+        return BYTE_COUNT_7_HEADER  + frame_data[6] + RTU_FRAME_CRC_LENGTH;
+      break;
+    case BYTE_COUNT_11:
+      if (frame_data_length >= 11)
+        return BYTE_COUNT_11_HEADER + frame_data[10] + RTU_FRAME_CRC_LENGTH;
+      break;
+    case BYTE_COUNT_U :
+      return -1;
+    default:
+      return res + RTU_FRAME_CRC_LENGTH;
+  } /* switch() */
+
+  /* unknown frame length */
+  return -1;
+}
+
+
+
+/************************************/
+/**                                **/
+/**      Search for a frame        **/
+/**                                **/
+/************************************/
+
+/* Search for a valid frame in the current data.
+ * If no valid frame is found, then we return -1.
+ *
+ * NOTE: Since frame verification is done by calculating the CRC, which is rather
+ *       CPU intensive, and this function may be called several times with the same,
+ *       data, we keep state regarding the result of previous invocations...
+ *       That is the reason for the *search_history parameter!
+ */
+static int search_for_frame(u8 *frame_data,
+                            int frame_data_length,
+                            int *search_history) {
+  int query_length, resp_length;
+  u8  function_code;
+    /* *search_history flag will have or'ed of following values... */
+#define SFF_HIST_NO_QUERY_FRAME     0x01
+#define SFF_HIST_NO_RESPONSE_FRAME  0x02
+#define SFF_HIST_NO_FRAME  (SFF_HIST_NO_RESPONSE_FRAME + SFF_HIST_NO_QUERY_FRAME)
+
+  if ((*search_history == SFF_HIST_NO_FRAME) ||
+      (frame_data_length < MIN_FRAME_LENGTH) ||
+      (frame_data_length > MAX_RTU_FRAME_LENGTH))
+    return -1;
+
+  function_code = frame_data[L2_FRAME_FUNCTION_OFS];
+
+  /* check for exception frame... */
+  if ((function_code && 0x80) == 0x80) {
+    if (frame_data_length >= EXCEPTION_FRAME_LENGTH + RTU_FRAME_CRC_LENGTH) {
+      /* let's check CRC for valid frame. */
+      if (   crc_calc(frame_data, EXCEPTION_FRAME_LENGTH)
+          == crc_read(frame_data, EXCEPTION_FRAME_LENGTH))
+        return EXCEPTION_FRAME_LENGTH + RTU_FRAME_CRC_LENGTH;
+      else
+        /* We have checked the CRC, and it is not a valid frame! */
+        *search_history |= SFF_HIST_NO_FRAME;
+    }
+    return -1;
+  }
+
+  /* check for valid function code */
+  if ((function_code > MAX_FUNCTION_CODE) || (function_code < 1)) {
+    /* This is an invalid frame!!! */
+    *search_history |= SFF_HIST_NO_FRAME;
+    return -1;
+  }
+
+  /* let's guess the frame length */
+  query_length = resp_length = -1;
+  if ((*search_history & SFF_HIST_NO_QUERY_FRAME) == 0)
+    query_length = frame_length(frame_data, frame_data_length, query_frame_lengths);
+  if ((*search_history & SFF_HIST_NO_RESPONSE_FRAME) == 0)
+    resp_length  = frame_length(frame_data, frame_data_length, response_frame_lengths);
+
+  /* let's check whether any of the lengths are valid...*/
+  /* If any of the guesses coincides with the available data length
+   * we check that length first...
+   */
+  if ((frame_data_length == query_length) || (frame_data_length == resp_length)) {
+    if (   crc_calc(frame_data, frame_data_length - RTU_FRAME_CRC_LENGTH)
+        == crc_read(frame_data, frame_data_length - RTU_FRAME_CRC_LENGTH))
+      return frame_data_length;
+    /* nope, wrong guess...*/
+    if (frame_data_length == query_length)
+      *search_history |= SFF_HIST_NO_QUERY_FRAME;
+    if (frame_data_length == resp_length)
+      *search_history |= SFF_HIST_NO_RESPONSE_FRAME;
+  }
+
+  /* let's shoot for a query frame */
+  if ((*search_history & SFF_HIST_NO_QUERY_FRAME) == 0) {
+    if (query_length >= 0) {
+      if (frame_data_length >= query_length) {
+        /* let's check if we have a valid frame */
+        if (   crc_calc(frame_data, query_length - RTU_FRAME_CRC_LENGTH)
+            == crc_read(frame_data, query_length - RTU_FRAME_CRC_LENGTH))
+          return query_length;
+        else
+          /* We have checked the CRC, and it is not a valid frame! */
+          *search_history |= SFF_HIST_NO_QUERY_FRAME;
+      }
+    }
+  }
+
+  /* let's shoot for a response frame */
+  if ((*search_history & SFF_HIST_NO_RESPONSE_FRAME) == 0) {
+    if (resp_length >= 0) {
+      if (frame_data_length >= resp_length) {
+        /* let's check if we have a valid frame */
+        if (   crc_calc(frame_data, resp_length - RTU_FRAME_CRC_LENGTH)
+            == crc_read(frame_data, resp_length - RTU_FRAME_CRC_LENGTH))
+          return resp_length;
+        else
+          *search_history |= SFF_HIST_NO_RESPONSE_FRAME;
+      }
+    }
+  }
+
+  /* Could not find valid frame... */
+  return -1;
+}
+
+
+
+/************************************/
+/**                                **/
+/**          Read a frame          **/
+/**                                **/
+/************************************/
+
+/* A small auxiliary function, just to make the code easier to read... */
+static inline void next_frame_offset(recv_buf_t *buf, u8 *slave_id) {
+  buf->frame_search_history = 0;
+  lb_data_purge(&(buf->data_buf), 1 /* skip one byte */);
+
+  if (slave_id == NULL)
+    return;
+
+  /* keep ignoring bytes, until we find one == *slave_id,
+   * or no more bytes...
+   */
+  while (lb_data_count(&(buf->data_buf)) != 0) {
+    if (*lb_data(&(buf->data_buf)) == *slave_id)
+      return;
+    lb_data_purge(&(buf->data_buf), 1 /* skip one byte */);
+  }
+}
+
+/* A small auxiliary function, just to make the code easier to read... */
+static inline int return_frame(recv_buf_t *buf,
+                               int frame_length,
+                               u8 **recv_data_ptr) {
+#ifdef DEBUG
+  fprintf(stderr, "\n" );
+  fprintf(stderr, "returning valid frame of %d bytes.\n", frame_length);
+#endif
+    /* set the data pointer */
+  *recv_data_ptr = lb_data(&(buf->data_buf));
+    /* remove the frame bytes off the buffer */
+  lb_data_purge(&(buf->data_buf), frame_length);
+    /* reset the search_history flag */
+  buf->frame_search_history = 0;
+    /* if the buffer becomes empty, then reset boundary flag */
+  if (lb_data_count(&(buf->data_buf)) <= 0)
+    buf->found_frame_boundary = 0;
+    /* return the frame length, excluding CRC */
+  return frame_length - RTU_FRAME_CRC_LENGTH;
+}
+
+/* A function to read a valid frame off the rtu bus.
+ *
+ * NOTES:
+ *        - The returned frame is guaranteed to be a valid frame.
+ *        - The returned length does *not* include the CRC.
+ *        - The returned frame is not guaranteed to have the same
+ *          slave id as that stored in (*slave_id). This value is used
+ *          merely in optimizing the search for wanted valid frames
+ *          after reading an aborted frame. Only in this situation do
+ *          we limit our search for frames with a slvae id == (*slave_id).
+ *          Under normal circumstances, the value in (*slave_id) is
+ *          simply ignored...
+ *          If any valid frame is desired, then slave_id should be NULL.
+ *
+ */
+
+/* NOTE: We cannot relly on the 3.5 character interval between frames to detect
+ *       end of frame. We are reading the bytes from a user process, so in
+ *       essence the bytes we are reading are coming off a cache.
+ *       Any inter-character delays between the arrival of the bytes are
+ *       lost as soon as they were placed in the cache.
+ *
+ *       Our only recourse is to analyse the frame we are reading in real-time,
+ *       and check if it is a valid frame by checking it's CRC.
+ *       To optimise this, we must be able to figure out the length
+ *       of the frame currently being received by analysing the first bytes
+ *       of that frame. Unfortunately, we have three problems with this:
+ *         1) The spec does not specify the format of every possible modbus
+ *            frame. For ex.functions 9, 10, 13, 14, 18 and 19(?).
+ *         2) It is not possible to figure out whether a frame is a query
+ *            or a response by just analysing the frame, and query and response
+ *            frames have different sizes...
+ *         3) A frame may be aborted in the middle! We have no easy way of telling
+ *            if what we are reading is a partial (aborted) frame, followed by a
+ *            correct frame.
+ *       Possible solutions to:
+ *         1) We could try to reverse engineer, but at the moment I have no
+ *            PLCs that will generate the required frames.
+ *            The chosen method is to verify the CRC if we are lucky enough to
+ *            detect the 3.5 frame boundary imediately following one of these
+ *            frames of unknown length.
+ *            If we do not detect any frame boundary, then our only option
+ *            is to consider it an aborted frame.
+ *         2) We aim for the query frame (usually the shortest), and check
+ *            it's CRC. If it matches, we accept, the frame, otherwise we try
+ *            a response frame.
+ *         3) The only way is to consider a frame boundary after each byte,
+ *            (i.e. ignore one bye at a time) and verify if the following bytes
+ *            constitue a valid frame (by checking the CRC).
+ *
+ *       When reading an aborted frame followed by two or more valid frames, if
+ *       we are unlucky and do not detetect any frame boundary using the 3.5
+ *       character interval, then we will most likely be reading in bytes
+ *       beyond the first valid frame. This means we will have to store the extra
+ *       bytes we have already read, so they may be handled the next time the
+ *       read_frame() function is called.
+ */
+ /*
+  * NOTE: The modbus RTU spec is inconsistent on how to handle
+  *       inter-character delays larger than 1.5 characters.
+  *       - On one paragraph it is stated that any delay larger than
+  *         1.5 character times aborts the current frame, and a new
+  *         frame is started.
+  *       - On another paragraph it is stated that a frame must begin
+  *         with a silence of 3.5 character times.
+  *
+  * We will therefore consider that any delay larger than 1.5 character
+  * times terminates a valid frame. All the above references to the 3.5 character
+  * interval should therefore be read as a 1.5 character interval.
+  */
+/* NOTE: This function is only called from one place in the rest of the code,
+ * so we might just as well make it inline...
+ */
+/* RETURNS: number of bytes in received frame
+ *          -1 on read file error
+ *          -2 on timeout
+ */
+static inline int read_frame(nd_entry_t *nd_entry,
+                             u8 **recv_data_ptr,
+                             struct timespec *end_time,
+                             u8 *slave_id)
+{
+  /* temporary variables... */
+  fd_set rfds;
+  struct timeval timeout;
+  int res, read_stat;
+  int frame_length;
+  recv_buf_t *recv_buf = &nd_entry->recv_buf_;
+
+    /* Flag:
+     *       1 => we are reading in an aborted frame, so we must
+     *            start ignoring bytes...
+     */
+  int found_aborted_frame;
+
+  /* assume error... */
+  *recv_data_ptr = NULL;
+
+  /*===================================*
+   * Check for frame in left over data *
+   *===================================*/
+  /* If we have any data left over from previous call to read_frame()
+   * (i.e. this very same function), then we try to interpret that
+   * data, and do not wait for any extra bytes...
+   */
+  frame_length = search_for_frame(lb_data(&recv_buf->data_buf),
+                                  lb_data_count(&recv_buf->data_buf),
+                                  &recv_buf->frame_search_history);
+  if (frame_length > 0)
+    /* We found a valid frame! */
+    return return_frame(recv_buf, frame_length, recv_data_ptr);
+
+  /* If the left over data finished at a frame boundary, and since it
+   * doesn't contain any valid frame, we discard those bytes...
+   */
+  if (recv_buf->found_frame_boundary == 1)
+    recv_buf_reset(recv_buf);
+
+  /*============================*
+   * wait for data availability *
+   *============================*/
+  /* if we can't find a valid frame in the existing data, or no data
+   * was left over, then we need to read more bytes!
+   */
+    FD_ZERO(&rfds);
+    FD_SET(nd_entry->fd, &rfds);
+    {int sel_res = my_select(nd_entry->fd + 1, &rfds, NULL, end_time);
+      if (sel_res < 0)
+        return -1;
+      if (sel_res == 0)
+        return -2;
+    }
+
+  /*==============*
+   * read a frame *
+   *==============*/
+  /* The main loop that reads one frame               */
+  /*  (multiple calls to read() )                     */
+  /* and jumps out as soon as it finds a valid frame. */
+
+  found_aborted_frame = 0;
+  FD_ZERO(&rfds);
+  FD_SET(nd_entry->fd, &rfds);
+  while (1) {
+
+    /*------------------*
+     * read frame bytes *
+     *------------------*/
+     /* Read in as many bytes as possible...
+      * But only if we have not found a frame boundary. Once we find
+      *  a frame boundary, we do not want to read in any more bytes
+      *  and mix them up with the current frame's bytes.
+      */
+    if (recv_buf->found_frame_boundary == 0) {
+      read_stat = read(nd_entry->fd,
+                       lb_free(&recv_buf->data_buf),
+                       lb_free_count(&recv_buf->data_buf));
+      if (read_stat < 0) {
+        if (errno != EINTR)
+          return -1;
+        else
+          read_stat = 0;
+      }
+#ifdef DEBUG
+      {/* display the hex code of each character received */
+        int i;
+        fprintf(stderr, "-");
+        for (i=0; i < read_stat; i++)
+          fprintf(stderr, "<0x%2X>", *(lb_free(&recv_buf->data_buf) + i));
+      }
+#endif
+      lb_data_add(&recv_buf->data_buf, read_stat);
+    }
+
+    /*-----------------------*
+     * check for valid frame *
+     *-----------------------*/
+    frame_length = search_for_frame(lb_data(&recv_buf->data_buf),
+                                    lb_data_count(&recv_buf->data_buf),
+                                    &recv_buf->frame_search_history);
+    if (frame_length > 0)
+      /* We found a valid frame! */
+      return return_frame(recv_buf, frame_length, recv_data_ptr);
+
+    /* if we reach this point, we are sure we do not have valid frame
+     * of known length in the current data with the current offset...
+     */
+
+    /*---------------------------------*
+     * Have we found an aborted frame? *
+     *---------------------------------*/
+    if (lb_data_count(&recv_buf->data_buf) >= MAX_RTU_FRAME_LENGTH)
+      found_aborted_frame = 1;
+
+    /*---------------------------------*
+     * Must we try a new frame_offset? *
+     *---------------------------------*/
+    if (found_aborted_frame == 1) {
+      /* Note that the found_aborted_frame flag is only set if:
+       *   1 - we have previously detected a frame_boundary,
+       *       (i.e. found_frame_boundary is == 1 !!) so we won't be
+       *       reading in more bytes;
+       *   2 - we have read more bytes than the maximum frame length
+       *
+       * Considering we have just failed finding a valid frame, and the above
+       * points (1) and (2), then there is no way we are still going to
+       * find a valid frame in the current data.
+       * We must therefore try a new first byte for the frame...
+       */
+      next_frame_offset(recv_buf, slave_id);
+    }
+
+    /*-----------------------------*
+     * check for data availability *
+     *-----------------------------*/
+    if (recv_buf->found_frame_boundary == 0) {
+      /* We need more bytes!! */
+      /*
+       * if no character at the buffer, then we wait time_15_char_
+       * before accepting end of frame
+       */
+      /* NOTES:
+       *   - On Linux, timeout is modified by select() to reflect
+       *     the amount of time not slept; most other implementations do
+       *     not do this. On those platforms we will simply have to wait
+       *     longer than we wished if select() is by any chance interrupted
+       *     by a signal...
+       */
+      timeout = nd_entry->time_15_char_;
+      while ((res = select(nd_entry->fd+1, &rfds, NULL, NULL, &timeout)) < 0) {
+        if (errno != EINTR)
+          return -1;
+        /* We will be calling select() again.
+         * We need to reset the FD SET !
+         */
+        FD_ZERO(&rfds);
+        FD_SET(nd_entry->fd, &rfds);
+      }
+
+      if (res == 0) {
+        int frame_length = lb_data_count(&recv_buf->data_buf);
+        /* We have detected an end of frame using timing boundaries... */
+        recv_buf->found_frame_boundary = 1; /* => stop trying to read any more bytes! */
+
+        /* Let's check if we happen to have a correct frame... */
+        if ((frame_length <= MAX_RTU_FRAME_LENGTH) &&
+	    (frame_length - RTU_FRAME_CRC_LENGTH > 0)) {
+          if (   crc_calc(lb_data(&recv_buf->data_buf), frame_length - RTU_FRAME_CRC_LENGTH)
+              == crc_read(lb_data(&recv_buf->data_buf), frame_length - RTU_FRAME_CRC_LENGTH)) {
+            /* We have found a valid frame. Let's get out of here! */
+            return return_frame(recv_buf, frame_length, recv_data_ptr);
+          }
+	}
+
+        /* We have detected a frame boundary, but the frame we read
+         * is not valid...
+         *
+         * One of the following reasons must be the cause:
+         *   1 - we are reading a single aborted frame.
+         *   2 - we are reading more than one frame. The first frame,
+         *       followed by any number of valid and/or aborted frames,
+         *       may be one of:
+         *       a - a valid frame whose length is unknown to us,
+         *           i.e. it is not specified in the public Modbus spec.
+         *       b - an aborted frame.
+         *
+         * Due to the complexity of reading 2a as a correct frame, we will
+         * consider it as an aborted frame. (NOTE: it is possible, but
+         * we will ignore it until the need arises... hopefully, never!)
+         *
+         * To put it succintly, what wee now have is an 'aborted' frame
+         * followed by one or more aborted and/or valid frames. To get to
+         * any valid frames, and since we do not know where they begin,
+         * we will have to consider every byte as the possible begining
+         * of a valid frame. For this permutation, we ignore the first byte,
+         * and carry on from there...
+         */
+        found_aborted_frame = 1;
+        lb_data_purge(&recv_buf->data_buf, 1 /* skip one byte */);
+        recv_buf->frame_search_history = 0;
+      }
+    }
+
+    /*-------------------------------*
+     * check for data yet to process *
+     *-------------------------------*/
+    if ((lb_data_count(&recv_buf->data_buf) < MIN_FRAME_LENGTH) &&
+        (recv_buf->found_frame_boundary == 1)) {
+      /* We have no more data to process, and will not read anymore! */
+      recv_buf_reset(recv_buf);
+      /* Return TIMEOUT error */
+      return -2;
+    }
+  } /* while (1)*/
+
+  /* humour the compiler... */
+  return -1;
+}
+
+
+
+
+
+/************************************/
+/**                                **/
+/**    Read a Modbus RTU frame     **/
+/**                                **/
+/************************************/
+
+/* The public function that reads a valid modbus frame.
+ *
+ * The returned frame is guaranteed to be different to the
+ * the frame stored in send_data, and to start with the
+ * same slave address stored in send_data[0].
+ *
+ * If send_data is NULL, send_data_length = 0, or
+ * ignore_echo == 0, then the first valid frame read off
+ * the bus is returned.
+ *
+ * return value: The length (in bytes) of the valid frame,
+ *               -1 on error
+ *               -2 on timeout
+ */
+
+int modbus_rtu_read(int *nd,
+                    u8 **recv_data_ptr,
+                    u16 *transaction_id,
+                    const u8 *send_data,
+                    int send_length,
+                    const struct timespec *recv_timeout) {
+  struct timespec end_time, *ts_ptr;
+  int res, recv_length, iter;
+  u8 *local_recv_data_ptr;
+  u8 *slave_id, local_slave_id;
+  nd_entry_t *nd_entry;
+
+  /* Check input parameters... */
+  if (nd == NULL)
+    return -1;
+
+  if (recv_data_ptr == NULL)
+    recv_data_ptr = &local_recv_data_ptr;
+
+  if ((send_data == NULL) && (send_length != 0))
+    return -1;
+
+  /* check if nd is correct... */
+  if ((nd_entry = nd_table_get_nd(&nd_table_, *nd)) == NULL)
+    return -1;
+
+  /* check if nd is initialzed... */
+  if (nd_entry->fd < 0)
+    return -1;
+
+  slave_id = NULL;
+  if (send_length > L2_FRAME_SLAVEID_OFS) {
+    local_slave_id = send_data[L2_FRAME_SLAVEID_OFS];
+    slave_id = &local_slave_id;
+  }
+
+  /* We will potentially read many frames, and we cannot reset the timeout
+   * for every frame we read. We therefore determine the absolute time_out,
+   * and use this as a parameter for each call to read_frame() instead of
+   * using a relative timeout.
+   *
+   * NOTE: see also the timeout related comment in the read_frame()= function!
+   */
+  /* get the current time... */
+  ts_ptr = NULL;
+  if (recv_timeout != NULL) {
+     ts_ptr = &end_time;
+    *ts_ptr = timespec_add_curtime(*recv_timeout);
+  }
+
+  /* NOTE: When using a half-duplex RS-485 bus, some (most ?) RS232-485
+   *       converters will send back to the RS232 port whatever we write,
+   *       so we will read in whatever we write out onto the bus.
+   *       We will therefore have to compare
+   *       the first frame we read with the one we sent. If they are
+   *       identical it is because we are in fact working on a RS-485
+   *       bus and must therefore read in a second frame which will be
+   *       the true response to our query.
+   *       If the first frame we receive is different to the query we
+   *       just sent, then we are *not* working on a RS-485 bus, and
+   *       that is already the real response to our query.
+   *
+   *       Flushing the input cache immediately after sending the query
+   *       could solve this issue, but we have no guarantee that this
+   *       process would not get swapped out between the write() and
+   *       flush() calls, and we could therefore be flushing the response
+   *       frame!
+   */
+
+  iter = 0;
+  while ((res = recv_length = read_frame(nd_entry, recv_data_ptr, ts_ptr, slave_id)) >= 0) {
+    if (iter < INT_MAX) iter++;
+
+    if ((send_length <= 0) || (nd_entry->ignore_echo == 0))
+      /* any valid frame will do... */
+      return recv_length;
+
+    if ((send_length > L2_FRAME_SLAVEID_OFS + 1) && (iter == 1))
+     /* We have a frame in send_data,
+      * so we must make sure we are not reading in the frame just sent...
+      *
+      * We must only do this for the first frame we read. Subsequent
+      * frames are guaranteed not to be the previously sent frame
+      * since the modbus_rtu_write() resets the recv buffer.
+      * Remember too that valid modbus responses may be exactly the same
+      * as the request frame!!
+      */
+      if (recv_length == send_length)
+        if (memcmp(*recv_data_ptr, send_data, recv_length) == 0)
+          /* recv == send !!! */
+          /* read in another frame.  */
+          continue;
+
+    /* The frame read is either:
+     *  - different to the frame in send_data
+     *  - or there is only the slave id in send_data[0]
+     *  - or both of the above...
+     */
+    if (send_length > L2_FRAME_SLAVEID_OFS)
+      if (recv_length > L2_FRAME_SLAVEID_OFS)
+        /* check that frame is from/to the correct slave... */
+        if ((*recv_data_ptr)[L2_FRAME_SLAVEID_OFS] == send_data[L2_FRAME_SLAVEID_OFS])
+          /* yep, it is... */
+          return recv_length;
+
+    /* The frame we have received is not acceptable...
+     * Let's read a new frame.
+     */
+  } /* while(...) */
+
+  /* error reading response! */
+  /* Return the error returned by read_frame! */
+  return res;
+}
+
+
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****        Initialising and Shutting Down Library        ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+/******************************/
+/**                          **/
+/**   Load Default Values    **/
+/**                          **/
+/******************************/
+
+static void set_defaults(int *baud,
+                         int *parity,
+                         int *data_bits,
+                         int *stop_bits) {
+  /* Set the default values, if required... */
+  if (*baud == 0)
+    *baud = DEF_BAUD_RATE;
+  if (*data_bits == 0)
+    *data_bits = DEF_DATA_BITS;
+  if (*stop_bits == 0) {
+    if (*parity == 0)
+      *stop_bits = DEF_STOP_BITS_NOP; /* no parity */
+    else
+      *stop_bits = DEF_STOP_BITS_PAR; /* parity used */
+  }
+}
+
+
+/******************************/
+/**                          **/
+/**    Initialise Library    **/
+/**                          **/
+/******************************/
+
+int modbus_rtu_init(int nd_count,
+                    optimization_t opt,
+                    int *extra_bytes)
+{
+#ifdef DEBUG
+  fprintf(stderr, "modbus_rtu_init(): called...\n");
+  fprintf(stderr, "creating %d node descriptors\n", nd_count);
+  if (opt == optimize_speed)
+    fprintf(stderr, "optimizing for speed\n");
+  if (opt == optimize_size)
+    fprintf(stderr, "optimizing for size\n");
+#endif
+
+    /* check input parameters...*/
+  if (0 == nd_count) {
+    if (extra_bytes != NULL)
+      // Not the corect value for this layer. 
+      // What we set it to in case this layer is not used!
+      *extra_bytes = 0; 
+    return 0;
+  }
+  if (nd_count <= 0)
+    goto error_exit_0;
+
+  if (extra_bytes == NULL)
+    goto error_exit_0;
+
+  if (crc_init(opt) < 0) {
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "Out of memory: error initializing crc buffers\n");
+#endif
+    goto error_exit_0;
+  }
+
+    /* set the extra_bytes value... */
+    /* Please see note before the modbus_rtu_write() function for a
+     * better understanding of this extremely ugly hack...
+     *
+     * The number of extra bytes that must be allocated to the data buffer
+     * before calling modbus_rtu_write()
+     */
+  *extra_bytes = RTU_FRAME_CRC_LENGTH;
+
+    /* initialise nd table... */
+  if (nd_table_init(&nd_table_, nd_count) < 0)
+    goto error_exit_0;
+
+    /* remember the optimization choice for later reference... */
+  optimization_ = opt;
+
+#ifdef DEBUG
+  fprintf(stderr, "modbus_rtu_init(): returning succesfuly...\n");
+#endif
+  return 0;
+
+error_exit_0:
+  if (extra_bytes != NULL)
+    // Not the corect value for this layer. 
+    // What we set it to in case of error!
+    *extra_bytes = 0; 
+  return -1;
+}
+
+
+
+/******************************/
+/**                          **/
+/**    Open node descriptor  **/
+/**                          **/
+/******************************/
+
+/* Open a node for master or slave operation.
+ * Returns the node descriptor, or -1 on error.
+ *
+ * This function is mapped onto both
+ * modbus_connect() and modbus_listen()
+ */
+int modbus_rtu_connect(node_addr_t node_addr) {
+  int node_descriptor;
+  nd_entry_t *nd_entry;
+
+#ifdef DEBUG
+  fprintf(stderr, "modbus_rtu_connect(): called...\n");
+  fprintf(stderr, "opening %s\n", node_addr.addr.rtu.device);
+  fprintf(stderr, "baud_rate = %d\n", node_addr.addr.rtu.baud);
+  fprintf(stderr, "parity = %d\n", node_addr.addr.rtu.parity);
+  fprintf(stderr, "data_bits = %d\n", node_addr.addr.rtu.data_bits);
+  fprintf(stderr, "stop_bits = %d\n", node_addr.addr.rtu.stop_bits);
+  fprintf(stderr, "ignore_echo = %d\n", node_addr.addr.rtu.ignore_echo);
+#endif
+
+  /* Check for valid address family */
+  if (node_addr.naf != naf_rtu)
+    /* wrong address type... */
+    goto error_exit_0;
+
+  /* find a free node descriptor */
+  if ((node_descriptor = nd_table_get_free_nd(&nd_table_)) < 0)
+    /* if no free nodes to initialize, then we are finished... */
+    goto error_exit_0;
+  if ((nd_entry = nd_table_get_nd(&nd_table_, node_descriptor)) == NULL)
+    /* strange, this should not occur... */
+    goto error_exit_0;
+
+  /* set the default values... */
+  set_defaults(&(node_addr.addr.rtu.baud),
+               &(node_addr.addr.rtu.parity),
+               &(node_addr.addr.rtu.data_bits),
+               &(node_addr.addr.rtu.stop_bits));
+
+#ifdef DEBUG
+  fprintf(stderr, "modbus_rtu_connect(): calling nd_entry_connect()\n");
+#endif
+  if (nd_entry_connect(nd_entry, &node_addr, optimization_) < 0)
+    goto error_exit_0;
+
+#ifdef DEBUG
+  fprintf(stderr, "modbus_rtu_connect(): %s open\n", node_addr.addr.rtu.device);
+  fprintf(stderr, "modbus_rtu_connect(): returning nd=%d\n", node_descriptor);
+#endif
+  return node_descriptor;
+
+  error_exit_0:
+#ifdef DEBUG
+  fprintf(stderr, "modbus_rtu_connect(): error!\n");
+#endif
+    return -1;
+}
+
+
+
+int modbus_rtu_listen(node_addr_t node_addr) {
+  return modbus_rtu_connect(node_addr);
+}
+
+
+
+/******************************/
+/**                          **/
+/**   Close node descriptor  **/
+/**                          **/
+/******************************/
+
+int modbus_rtu_close(int nd) {
+  return nd_table_free_nd(&nd_table_, nd);
+}
+
+
+
+/******************************/
+/**                          **/
+/**    Shutdown Library      **/
+/**                          **/
+/******************************/
+
+int modbus_rtu_done(void) {
+  nd_table_done(&nd_table_);
+  crc_done();
+
+  return 0;
+}
+
+
+
+
+/******************************/
+/**                          **/
+/**                          **/
+/**                          **/
+/******************************/
+int modbus_rtu_silence_init(void) {
+  return 0;
+}
+
+
+
+
+/******************************/
+/**                          **/
+/**                          **/
+/**                          **/
+/******************************/
+
+
+double modbus_rtu_get_min_timeout(int baud,
+                                  int parity,
+                                  int data_bits,
+                                  int stop_bits) {
+  int parity_bits, start_bits, char_bits;
+
+  set_defaults(&baud, &parity, &data_bits, &stop_bits);
+  parity_bits = (parity == 0)?0:1;
+  start_bits  = 1;
+  char_bits   = start_bits + data_bits + parity_bits + stop_bits;
+  return (double)((MAX_RTU_FRAME_LENGTH * char_bits) / baud);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_rtu_private.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2001,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#ifndef MODBUS_RTU_PRIVATE_H
+#define MODBUS_RTU_PRIVATE_H
+
+#include "mb_types.h" /* get the data types */
+#include "mb_util.h"
+
+
+/* serial port default configuration... */
+#define DEF_DATA_BITS 8
+#define DEF_STOP_BITS_PAR 1 /* default stop bits if parity is used     */
+#define DEF_STOP_BITS_NOP 2 /* default stop bits if parity is not used */
+#define DEF_BAUD_RATE 9600
+
+
+/* Send retries of rtu frames... */
+#define RTU_FRAME_SEND_RETRY 1
+  /* NOTES:
+   *  - the above are the retries at the layer1 level,
+   *    higher layers may decide to retry for themselves!
+   */
+
+
+/* Buffer sizes... */
+ /* We use double the maximum frame length for the read buffer,
+  * due to the algorithm used to work around aborted frames.
+  */
+#define RECV_BUFFER_SIZE_SMALL  (MAX_RTU_FRAME_LENGTH + 10)
+#define RECV_BUFFER_SIZE_LARGE  (2 * MAX_RTU_FRAME_LENGTH)
+
+
+/* Frame lengths... */
+
+ /* The number of bytes in each frame format, excluding CRC.
+  *
+  * BYTE_COUNT_3  denotes that third byte of frame contains the number of bytes;
+  *                 - total number of bytes in frame is
+  *                   BYTE_COUNT_3_HEADER + byte_count
+  * BYTE_COUNT_34 denotes that third+fourth bytes of frame contain the number of bytes;
+  *                 - total number of bytes in frame is
+  *                   BYTE_COUNT_34_HEADER + byte_count
+  * BYTE_COUNT_7  denotes that seventh byte of frame contain the number of bytes;
+  *                 - total number of bytes in frame is
+  *                   BYTE_COUNT_7_HEADER + byte_count
+  * BYTE_COUNT_11 denotes that eleventh byte of frame contain the number of bytes;
+  *                 - total number of bytes in frame is
+  *                   BYTE_COUNT_11_HEADER + byte_count
+  * BYTE_COUNT_U  denotes unknown number of bytes;
+  */
+
+#define BYTE_COUNT_3_HEADER   3
+#define BYTE_COUNT_34_HEADER  4
+#define BYTE_COUNT_7_HEADER   7
+#define BYTE_COUNT_11_HEADER  11
+
+#define BYTE_COUNT_3  (-3)
+#define BYTE_COUNT_34 (-34)
+#define BYTE_COUNT_7  (-7)
+#define BYTE_COUNT_11 (-11)
+#define BYTE_COUNT_U  (-128)
+
+
+#define MAX_FUNCTION_CODE 0x18
+
+#define MIN_FRAME_LENGTH       3
+#define EXCEPTION_FRAME_LENGTH 3
+
+static i8 query_frame_lengths[MAX_FUNCTION_CODE+1] = {
+                /* 0x00 */ 0,             /* unused                    */
+                /* 0x01 */ 6,             /* Read Coil Status          */
+                /* 0x02 */ 6,             /* Read Input Status         */
+                /* 0x03 */ 6,             /* Read Holding Registers    */
+                /* 0x04 */ 6,             /* Read Input Registers      */
+                /* 0x05 */ 6,             /* Force Single Coil         */
+                /* 0x06 */ 6,             /* Preset Single Register    */
+                /* 0x07 */ 2,             /* Read Exception Status     */
+                /* 0x08 */ 4,             /* Diagnostics               */
+                /* 0x09 */ BYTE_COUNT_U,  /* Program 484               */
+                /* 0x0A */ BYTE_COUNT_U,  /* Poll 484                  */
+                /* 0x0B */ 2,             /* Fetch Comm. Event Counter */
+                /* 0x0C */ 2,             /* Fetch Comm. Event Log     */
+                /* 0x0D */ BYTE_COUNT_U,  /* Program Controller        */
+                /* 0x0E */ BYTE_COUNT_U,  /* Poll Controller           */
+                /* 0x0F */ BYTE_COUNT_7,  /* Force Multiple Coils      */
+                /* 0x10 */ BYTE_COUNT_7,  /* Preset Multiple Registers */
+                /* 0x11 */ 2,             /* Report Slave ID           */
+                /* 0x12 */ BYTE_COUNT_U,  /* Program 884/M84           */
+                /* 0x13 */ BYTE_COUNT_U,  /* Reset. Comm. Link         */
+                /* 0x14 */ BYTE_COUNT_3,  /* Read General Reference    */
+                /* 0x15 */ BYTE_COUNT_3,  /* Write General Reference   */
+                /* 0x16 */ 8,             /* Mask Write 4X Register    */
+                /* 0x17 */ BYTE_COUNT_11, /* Read/Write 4x Register    */
+                /* 0x18 */ 4              /* Read FIFO Queue           */
+              };
+
+static i8 response_frame_lengths[MAX_FUNCTION_CODE+1] = {
+                /* 0x00 */ 0,             /* unused                    */
+                /* 0x01 */ BYTE_COUNT_3,  /* Read Coil Status          */
+                /* 0x02 */ BYTE_COUNT_3,  /* Read Input Status         */
+                /* 0x03 */ BYTE_COUNT_3,  /* Read Holding Registers    */
+                /* 0x04 */ BYTE_COUNT_3,  /* Read Input Registers      */
+                /* 0x05 */ 6,             /* Force Single Coil         */
+                /* 0x06 */ 6,             /* Preset Single Register    */
+                /* 0x07 */ 3,             /* Read Exception Status     */
+                /* 0x08 */ 6,/*see (1)*/  /* Diagnostics               */
+                /* 0x09 */ BYTE_COUNT_U,  /* Program 484               */
+                /* 0x0A */ BYTE_COUNT_U,  /* Poll 484                  */
+                /* 0x0B */ 6,             /* Fetch Comm. Event Counter */
+                /* 0x0C */ BYTE_COUNT_3,  /* Fetch Comm. Event Log     */
+                /* 0x0D */ BYTE_COUNT_U,  /* Program Controller        */
+                /* 0x0E */ BYTE_COUNT_U,  /* Poll Controller           */
+                /* 0x0F */ 6,             /* Force Multiple Coils      */
+                /* 0x10 */ 6,             /* Preset Multiple Registers */
+                /* 0x11 */ BYTE_COUNT_3,  /* Report Slave ID           */
+                /* 0x12 */ BYTE_COUNT_U,  /* Program 884/M84           */
+                /* 0x13 */ BYTE_COUNT_U,  /* Reset. Comm. Link         */
+                /* 0x14 */ BYTE_COUNT_3,  /* Read General Reference    */
+                /* 0x15 */ BYTE_COUNT_3,  /* Write General Reference   */
+                /* 0x16 */ 8,             /* Mask Write 4X Register    */
+                /* 0x17 */ BYTE_COUNT_3,  /* Read/Write 4x Register    */
+                /* 0x18 */ BYTE_COUNT_34  /* Read FIFO Queue           */
+              };
+
+/* NOTE (1):
+ *    The diagnostic function (0x08) has sub-functions. In particular,
+ *    sub-function 21 (0x15) has two sub-sub-functions. In the very
+ *    particular case of *one* of these sub-sub-functions, the reply
+ *    frame does *not* have a size of 4, but is variable in length
+ *    and includes a byte counter.
+ *    To take this into account in the table would require an extra two
+ *    tables.
+ *    The above length has been hardcoded into the frame_length() function
+ *    (in file modbus_rtu.c)
+ */
+
+
+#define FALSE 0
+#define TRUE 1
+
+
+#endif  /* MODBUS_RTU_PRIVATE_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_slave.c	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,845 @@
+/*
+ * Copyright (c) 2001,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+/* mb_slave.c */
+
+#include <fcntl.h>	/* File control definitions */
+#include <stdio.h>	/* Standard input/output */
+#include <string.h>
+#include <stdlib.h>
+#include <termio.h>	/* POSIX terminal control definitions */
+#include <sys/time.h>	/* Time structures for select() */
+#include <unistd.h>	/* POSIX Symbolic Constants */
+#include <errno.h>	/* Error definitions */
+
+#include <netinet/in.h> /* required for htons() and ntohs() */
+#include "mb_layer1.h"
+#include "mb_slave.h"
+#include "mb_slave_private.h"
+
+/* #define DEBUG */		/* uncomment to see the data sent and received */
+
+
+#define modbus_write             fptr_[layer1_fin].modbus_write           
+#define modbus_read              fptr_[layer1_fin].modbus_read            
+#define modbus_init              fptr_[layer1_fin].modbus_init            
+#define modbus_done              fptr_[layer1_fin].modbus_done            
+#define modbus_connect           fptr_[layer1_fin].modbus_connect         
+#define modbus_listen            fptr_[layer1_fin].modbus_listen          
+#define modbus_close             fptr_[layer1_fin].modbus_close           
+#define modbus_silence_init      fptr_[layer1_fin].modbus_silence_init    
+#define modbus_get_min_timeout   fptr_[layer1_fin].modbus_get_min_timeout 
+
+/* the lower two bits of ttyfd are used to store the index to layer1 function pointers */
+/* layer1_fin index to fptr_[] is in lowest 2 bits of fd */
+#define get_ttyfd()     int layer1_fin = fd & 3; int ttyfd = fd / 4;\
+                        if (fd < 0) {ttyfd = fd; layer1_fin = 0; /* use modbusTCP */}
+
+
+
+
+/******************************************/
+/******************************************/
+/**                                      **/
+/**         Global Variables...          **/
+/**                                      **/
+/******************************************/
+/******************************************/
+/* The layer 1 (RTU, ASCII, TCP) implementations will be adding some 
+ *  header and tail bytes (e.g. CRC) to the packet we build here. Since
+ *  layer1 will re-use the same buffer allocated in this slave layer 
+ *  (so as not to continuosly copy the same info from buffer to buffer),
+ *  we need to allocate more bytes than those strictly required for this
+ *  slave layer. Therefore, the extra_bytes parameter.
+ *
+ *  Note that we add one more extra byte to the response buffer.
+ *  This is because some response packets will not be starting off
+ *  at byte 0, but rather at byte 1 of the buffer. This is in order
+ *  to guarantee that the data that is sent on the buffer is aligned
+ *  on even bytes (the 16 bit words!). This will allow the application
+ *  (layer above the one implemented in this file - i.e. the callback 
+ *  functions) to reference this memory as an u16 *, without producing
+ *  'bus error' messages in some embedded devices that do not allow
+ *   acessing u16 on odd numbered addresses.
+ */
+static int buff_extra_bytes_;
+#define RESP_BUFFER_SIZE       (MAX_L2_FRAME_LENGTH + buff_extra_bytes_ + 1)
+
+/******************************************/
+/******************************************/
+/**                                      **/
+/**       Local Utility functions...     **/
+/**                                      **/
+/******************************************/
+/******************************************/
+
+
+/*
+ * Function to determine next transaction id.
+ *
+ * We use a library wide transaction id, which means that we
+ * use a new transaction id no matter what slave to which we will
+ * be sending the request...
+ */
+static inline u16 next_transaction_id(void) {
+  static u16 next_id = 0;
+  return next_id++;
+}
+
+
+/*
+ * Functions to convert u16 variables
+ * between network and host byte order
+ *
+ * NOTE: Modbus uses MSByte first, just like
+ *       tcp/ip, so we could be tempted to use the htons() and
+ *       ntohs() functions to guarantee code portability.
+ *
+ *       However, on some embedded systems running Linux
+ *       these functions only work if the 16 bit words are 
+ *       stored on even addresses. This is not always the 
+ *       case in our code, so we have to define our own
+ *       conversion functions...
+ */
+
+/* if using gcc, use it to determine byte order... */
+#ifndef __BYTE_ORDER
+#if defined(__GNUC__) 
+  /* We have GCC, which should define __LITTLE_ENDIAN__ */ 
+#  if defined(__LITTLE_ENDIAN__)
+#    define __BYTE_ORDER __LITTLE_ENDIAN
+#  else
+#    define __BYTE_ORDER __BIG_ENDIAN
+#  endif
+#endif /* __GNUC__ */ 
+#endif /* __BYTE_ORDER */
+
+
+/* If we still don't know byte order, try to get it from <sys/param.h> */
+#ifndef __BYTE_ORDER
+#include <sys/param.h>
+#endif
+
+
+#ifndef __BYTE_ORDER
+#  ifdef BYTE_ORDER
+#   if BYTE_ORDER == LITTLE_ENDIAN
+#    define __BYTE_ORDER __LITTLE_ENDIAN
+#   else
+#    if BYTE_ORDER == BIG_ENDIAN
+#      define __BYTE_ORDER __BIG_ENDIAN
+#    endif
+#   endif
+#  endif /* BYTE_ORDER */
+#endif /* __BYTE_ORDER */
+
+
+
+
+
+#ifdef __BYTE_ORDER
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+
+/**************************************************************/
+/* u16 conversion functions to use on little endian platforms */
+/**************************************************************/
+
+static inline u16 mb_hton(u16 w) {
+  register u16 tmp;
+  tmp =  (w & 0x00FF);
+  tmp = ((w & 0xFF00) >> 0x08) | (tmp << 0x08);
+  return(tmp);
+}
+#define mb_ntoh(a) mb_hton(a)
+
+static inline void mb_hton_count(u16 *w, int count) {
+  int i;
+  for (i = 0; i < count; i++) {
+    /* swap the bytes around... 
+     *  a = a ^ b;
+     *  b = a ^ b;
+     *  a = a ^ b;
+     */
+    ((u8 *)(w+i))[0] ^= ((u8 *)(w+i))[1]; 
+    ((u8 *)(w+i))[1] ^= ((u8 *)(w+i))[0]; 
+    ((u8 *)(w+i))[0] ^= ((u8 *)(w+i))[1]; 
+  }
+}
+#define mb_ntoh_count(w, count) mb_hton_count(w, count)
+
+
+
+# else
+#  if __BYTE_ORDER == __BIG_ENDIAN
+/***********************************************************/
+/* u16 conversion functions to use on big endian platforms */
+/***********************************************************/
+
+ /* We do not need to swap the bytes around!  */
+#define mb_ntoh(val) (val)
+#define mb_hton(val) (val)
+#define mb_hton_count(w, count) /* empty ! */
+#define mb_ntoh_count(w, count) /* empty ! */
+
+
+#  else
+
+/********************************************************/
+/* u16 conversion functions to use on generic platforms */
+/********************************************************/
+
+ /* We don't know the byte order, so we revert to the 
+  * standard htons() and ntohs() ... 
+  */
+static inline u16 mb_hton(u16 h_value) 
+  {return htons(h_value); /* return h_value; */}
+
+static inline u16 mb_ntoh(u16 m_value) 
+  {return ntohs(m_value); /* return m_value; */}
+
+static inline void mb_hton_count(u16 *w, int count)
+  {int i; for (i = 0; i < count; i++) {w[i] = mb_hton(w[i]);}}
+
+static inline void mb_ntoh_count(u16 *w, int count)
+  {int i; for (i = 0; i < count; i++) {w[i] = mb_ntoh(w[i]);}}
+
+#  endif
+# endif
+#endif /* __BYTE_ORDER */
+
+
+
+
+/* Safe versions of the conversion functions!
+ *
+ * Note that these functions always work, whatever the endiannes
+ * of the machine that executes it!
+ *
+ * It is also safe because the resulting value may be stored
+ * on an odd address even on machines that do not allow directly
+ * accessing u16 bit words on odd addresses.
+ */
+static inline int mb_hton_safe(u16 from, u16 *to_ptr) {
+  ((u8 *)to_ptr)[1] =  (from & 0x00FF);
+  ((u8 *)to_ptr)[0] = ((from & 0xFF00) >> 0x08);
+  return 0;
+}
+
+#define mb_ntoh_safe(a, b) mb_hton_safe(a, b)
+
+
+/*  return Most Significant Byte of value; */
+static inline u8 msb(u16 value) 
+  {return (value >> 8) & 0xFF;}
+
+/*  return Least Significant Byte of value; */
+static inline u8 lsb(u16 value) 
+  {return value & 0xFF;}
+
+#define u16_v(char_ptr)  (*((u16 *)(&(char_ptr))))
+
+
+
+
+
+
+
+
+
+/***********************************************/
+/***********************************************/
+/**                                           **/
+/**    Handle requests from master/client     **/
+/**                                           **/
+/***********************************************/
+/***********************************************/
+
+
+/* Handle functions 0x01 and 0x02 */
+typedef int (*read_bits_callback_t)(void *arg, u16 start_addr, u16 bit_count,  u8  *data_bytes);
+static int handle_read_bits (u8 *query_packet,
+                             u8 **resp_packet_ptr,
+                             u8 *error_code,
+                             read_bits_callback_t read_bits_callback,
+                             void *callback_arg
+                            ) {
+  u16 start_addr, count;
+  int res;
+  u8 *resp_packet;
+  
+  /* If no callback, handle as if function is not supported... */
+  if (read_bits_callback == NULL) 
+    {*error_code = ERR_ILLEGAL_FUNCTION; return -1;}
+  
+  /* in oprder for the data in this packet to be aligned on even numbered addresses, this 
+   *  response packet will start off at an odd numbered byte...
+   *  We therefore add 1 to the address where the packet starts.
+   */ 
+  (*resp_packet_ptr)++;
+  resp_packet = *resp_packet_ptr;
+  
+    /* NOTE:
+     *  Modbus uses high level addressing starting off from 1, but
+     *  this is sent as 0 on the wire!
+     *  We could expect the user to specify high level addressing 
+     *   starting at 1, and do the conversion to start off at 0 here.
+     *   However, to do this we would then need to use an u32 data type
+     *   to correctly hold the address supplied by the user (which could
+     *   correctly be 65536, which does not fit in an u16), which would
+     *   in turn require us to check whether the address supplied by the user
+     *   is correct (i.e. <= 65536). 
+     *  I decided to go with the other option of using an u16, and 
+     *   requiring the user to use addressing starting off at 0! 
+     */
+  /* start_addr = mb_ntoh(u16_v(query_packet[2])) + 1; */
+  mb_ntoh_safe(u16_v(query_packet[2]), &start_addr);
+  mb_ntoh_safe(u16_v(query_packet[4]), &count); 
+
+  #ifdef DEBUG
+  printf("handle_read_input_bits() called. slave=%d, function=%d, start_addr=%d, count=%d\n", 
+          query_packet[0], query_packet[1], start_addr, count);
+  #endif
+
+  if ((count > MAX_READ_BITS) || (count < 1)) 
+    {*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;}
+  
+  /* Remember, we are using addressing starting off at 0, in the start_addr variable! */
+  /*  This means that he highest acceptable address is 65535, when count=1 .... */
+  /* Note the use of 65536 in the comparison will force automatic upgrade of u16 variables! */
+  /*    => start_addr + count will nver overflow the u16 type!                              */
+  if (start_addr + count > 65536) 
+    {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  
+  /* start building response frame... */
+  resp_packet[0] = query_packet[0]; /* slave */ 
+  resp_packet[1] = query_packet[1]; /* function (either 0x01 or 0x02 ! */
+  resp_packet[2] = (count + 7) / 8; /* number of data bytes = ceil(count/8) */
+  
+  res = read_bits_callback(callback_arg, start_addr, count, &(resp_packet[3]));
+  if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  if (res  <  0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;}
+  
+  return resp_packet[2] + 3; /* packet size is data length + 3 bytes -> slave, function, count */
+}
+
+
+
+/* Handle function 0x01 */
+int handle_read_output_bits  (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks)
+  {return handle_read_bits(query_packet, resp_packet_ptr, error_code, callbacks->read_outbits, callbacks->arg);}
+
+/* Handle function 0x02 */
+int handle_read_input_bits   (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks)
+  {return handle_read_bits(query_packet, resp_packet_ptr, error_code, callbacks->read_inbits, callbacks->arg);}
+
+
+
+
+/* Handle functions 0x03 and 0x04 */
+typedef int (*read_words_callback_t)(void *arg, u16 start_addr, u16 word_count, u16 *data_words);
+static int handle_read_words (u8 *query_packet, 
+                              u8 **resp_packet_ptr, 
+                              u8 *error_code, 
+                              read_words_callback_t read_words_callback,
+                              void *callback_arg
+                             ) {
+  u16 start_addr, count;
+  int res;
+  u8 *resp_packet;
+
+  /* If no callback, handle as if function is not supported... */
+  if (read_words_callback == NULL) 
+    {*error_code = ERR_ILLEGAL_FUNCTION; return -1;}
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  (*resp_packet_ptr)++;
+  resp_packet = *resp_packet_ptr;
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  mb_ntoh_safe(u16_v(query_packet[2]), &start_addr);
+  mb_ntoh_safe(u16_v(query_packet[4]), &count);
+
+  #ifdef DEBUG
+  printf("handle_read_output_words() called. slave=%d, function=%d, start_addr=%d, count=%d\n", 
+         query_packet[0], query_packet[1], start_addr, count);
+  #endif
+
+  if ((count > MAX_READ_REGS) || (count < 1))
+    {*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;}
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  if (start_addr + count > 65536)
+    {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  
+  /* start building response frame... */
+  resp_packet[0] = query_packet[0]; /* slave     */
+  resp_packet[1] = query_packet[1]; /* function code, either 0x03 or 0x04 !!!*/
+  resp_packet[2] = count * 2;       /* number of bytes of data... */
+  
+  res = read_words_callback(callback_arg, start_addr, count, (u16 *)&(resp_packet[3]));
+  if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  if (res  <  0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;}
+  
+  /* convert all data from host to network byte order. */
+  mb_hton_count((u16 *)&(resp_packet[3]), count);
+  
+  return resp_packet[2] + 3; /* packet size is data length + 3 bytes -> slave, function, count */
+}
+
+
+
+
+/* Handle function 0x03 */
+int handle_read_output_words (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) 
+  {return handle_read_words(query_packet, resp_packet_ptr, error_code, callbacks->read_outwords, callbacks->arg);}
+
+/* Handle function 0x04 */
+int handle_read_input_words  (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) 
+  {return handle_read_words(query_packet, resp_packet_ptr, error_code, callbacks->read_inwords, callbacks->arg);}
+
+
+
+/* Handle function 0x05 */
+int handle_write_output_bit  (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) {
+  u16 start_addr;
+  int res;
+  u8 *resp_packet;
+  
+  /* If no callback, handle as if function is not supported... */
+  if (callbacks->write_outbits == NULL)
+    {*error_code = ERR_ILLEGAL_FUNCTION; return -1;}
+  
+  resp_packet = *resp_packet_ptr;
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  mb_ntoh_safe(u16_v(query_packet[2]), &start_addr);
+
+  #ifdef DEBUG
+  printf("handle_write_output_bit() called. slave=%d, function=%d, start_addr=%d\n", 
+         query_packet[0], query_packet[1], start_addr);
+  #endif
+
+  // byte 5 Must be 0x00, byte 4 must be 0x00 or 0xFF !!
+  if ( (query_packet[5] != 0) || 
+      ((query_packet[4] != 0) && (query_packet[4] != 0xFF)))
+    {*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;}
+  
+  /* Address will always be valid, no need to check! */
+  // if (start_addr > 65535) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  
+  /* start building response frame... */
+  resp_packet[0] = query_packet[0]; /* slave */ 
+  resp_packet[1] = query_packet[1]; /* function */ 
+  resp_packet[2] = query_packet[2]; /* start address - hi byte */
+  resp_packet[3] = query_packet[3]; /* start address - lo byte */
+  resp_packet[4] = query_packet[4]; /* value: 0x00 or 0xFF */
+  resp_packet[5] = query_packet[5]; /* value: must be 0x00 */
+  
+  res = (callbacks->write_outbits)(callbacks->arg, start_addr, 1, &(query_packet[4]));
+  if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  if (res  <  0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;}
+  
+  return 6; /* response packet size, including slave id in byte 0 */
+}
+
+
+
+/* Handle function 0x06 */
+int handle_write_output_word (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) {
+  u16 start_addr;
+  int res;
+  u8 *resp_packet;
+  
+  /* If no callback, handle as if function is not supported... */
+  if (callbacks->write_outwords == NULL)
+    {*error_code = ERR_ILLEGAL_FUNCTION; return -1;}
+  
+  resp_packet = *resp_packet_ptr;
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  mb_ntoh_safe(u16_v(query_packet[2]), &start_addr);
+  
+  #ifdef DEBUG
+  printf("handle_write_output_word() called. slave=%d, function=%d, start_addr=%d\n", 
+         query_packet[0], query_packet[1], start_addr);
+  #endif
+
+  /* Address will always be valid, no need to check! */
+  // if (start_addr > 65535) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  
+  /* start building response frame... */
+  resp_packet[0] = query_packet[0]; /* slave           */
+  resp_packet[1] = query_packet[1]; /* function        */ 
+  resp_packet[2] = query_packet[2]; /* start address - hi byte */
+  resp_packet[3] = query_packet[3]; /* start address - lo byte */
+  resp_packet[4] = query_packet[4]; /* value - hi byte */
+  resp_packet[5] = query_packet[5]; /* value - lo byte */
+  
+  /* convert data from network to host byte order */
+  mb_ntoh_count((u16 *)&(query_packet[4]), 1);
+  
+  res = (callbacks->write_outwords)(callbacks->arg, start_addr, 1, (u16 *)&(query_packet[4]));
+  if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  if (res  <  0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;}
+  
+  return 6; /* packet size is 6 -> slave, function, addr(2), value(2) */
+}
+
+
+
+/* Handle function 0x0F */
+int handle_write_output_bits (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) {
+  u16 start_addr, count;
+  int res;
+  u8 *resp_packet;
+  
+  /* If no callback, handle as if function is not supported... */
+  if (callbacks->write_outbits == NULL)
+    {*error_code = ERR_ILLEGAL_FUNCTION; return -1;}
+  
+  resp_packet = *resp_packet_ptr;
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  mb_ntoh_safe(u16_v(query_packet[2]), &start_addr);
+  mb_ntoh_safe(u16_v(query_packet[4]), &count); 
+
+  #ifdef DEBUG
+  printf("handle_write_output_bits() called. slave=%d, function=%d, start_addr=%d, count=%d\n", 
+         query_packet[0], query_packet[1], start_addr, count);
+  #endif
+
+  if ((count > MAX_WRITE_COILS) || (count < 1) || ((count+7)/8 != query_packet[6]) )
+    {*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;}
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  if (start_addr + count > 65536)
+    {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  
+  /* start building response frame... */
+  resp_packet[0] = query_packet[0]; /* slave */ 
+  resp_packet[1] = query_packet[1]; /* function */ 
+  resp_packet[2] = query_packet[2]; /* start address - hi byte */
+  resp_packet[3] = query_packet[3]; /* start address - lo byte */
+  resp_packet[4] = query_packet[4]; /* count - hi byte */
+  resp_packet[5] = query_packet[5]; /* count - lo byte */
+  
+  res = (callbacks->write_outbits)(callbacks->arg, start_addr, count, &(query_packet[7]));
+  if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  if (res  <  0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;}
+  
+  return 6; /* packet size is 6 -> slave, function, addr(2), count(2) */
+}
+
+
+
+
+/* Handle function 0x10 */
+int handle_write_output_words(u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) {
+  u16 start_addr, count;
+  int res;
+  u8 *resp_packet;
+  
+  /* If no callback, handle as if function is not supported... */
+  if (callbacks->write_outwords == NULL)
+    {*error_code = ERR_ILLEGAL_FUNCTION; return -1;}
+  
+  resp_packet = *resp_packet_ptr;
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  mb_ntoh_safe(u16_v(query_packet[2]), &start_addr);
+  mb_ntoh_safe(u16_v(query_packet[4]), &count); 
+  
+  if ((count > MAX_WRITE_REGS) || (count < 1) || (count*2 != query_packet[6]) )
+    {*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;}
+  
+  /* See equivalent comment in handle_read_bits() */ 
+  if (start_addr + count > 65536)
+    {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  
+  /* start building response frame... */
+  resp_packet[0] = query_packet[0]; /* slave           */
+  resp_packet[1] = query_packet[1]; /* function        */ 
+  resp_packet[2] = query_packet[2]; /* start address - hi byte */
+  resp_packet[3] = query_packet[3]; /* start address - lo byte */
+  resp_packet[4] = query_packet[4]; /* count - hi byte */
+  resp_packet[5] = query_packet[5]; /* count - lo byte */
+  
+  /* convert all data from network to host byte order */
+  mb_ntoh_count((u16 *)&(query_packet[7]), count);
+  
+  res = (callbacks->write_outwords)(callbacks->arg, start_addr, count, (u16 *)&(query_packet[7]));
+  if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;}
+  if (res  <  0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;}
+  
+  return 6; /* packet size is 6 -> slave, function, addr(2), count(2) */
+}
+
+
+
+
+
+
+
+
+/***********************************************/
+/***********************************************/
+/**                                           **/
+/**    initialise / shutdown the library      **/
+/**                                           **/
+/***********************************************/
+/***********************************************/
+
+int mb_slave_init__(int extra_bytes) {
+  buff_extra_bytes_ = extra_bytes;
+  return 0;
+}
+
+
+int mb_slave_done__(void) 
+  {return 0;}
+
+
+#if 0
+int mb_slave_init(int nd_count) {
+  int extra_bytes;
+
+  #ifdef DEBUG
+  fprintf( stderr, "mb_slave_init()\n");
+  fprintf( stderr, "creating %d nodes\n", nd_count);
+  #endif
+
+  /* initialise layer 1 library */
+  if (modbus_init(nd_count, DEF_OPTIMIZATION, &extra_bytes) < 0)
+    goto error_exit_0;
+
+  /* initialise this library */
+  if (mb_slave_init__(extra_bytes) < 0)
+    goto error_exit_1;
+
+  return 0;
+
+error_exit_1:
+  modbus_done();
+error_exit_0:
+  return -1;
+}
+
+
+int mb_slave_done(void) {
+  mb_slave_done__(void)
+  return modbus_done();
+}
+#endif
+
+
+
+/***********************************************/
+/***********************************************/
+/**                                           **/
+/**        open/close slave connection        **/
+/**                                           **/
+/***********************************************/
+/***********************************************/
+
+/* Create a new slave/server */
+/* NOTE: We use the lower 2 bits of the returned node id to identify which 
+ *       layer1 implementation to use. 
+ *           0 -> TCP 
+ *           1 -> RTU 
+ *           2 -> ASCII 
+ *           4 -> unused 
+ *       The node id used by the layer1 is shifted left 2 bits
+ *       before returning the node id to the caller!
+ */
+int mb_slave_new(node_addr_t node_addr) {
+  int res = -1;
+  #ifdef DEBUG
+  fprintf( stderr, "mb_slave_connect()\n");
+  #endif
+
+  /* call layer 1 library */
+  switch(node_addr.naf) {
+    case naf_tcp:  
+      res = modbus_tcp_listen(node_addr);
+      if (res >= 0) res = res*4 + 0 /* offset into fptr_ with TCP functions */;
+      return res;
+    case naf_rtu:  
+      res = modbus_rtu_listen(node_addr);
+      if (res >= 0) res = res*4 + 1 /* offset into fptr_ with RTU functions */;
+      return res;
+    case naf_ascii:  
+      res = modbus_ascii_listen(node_addr);
+      if (res >= 0) res = res*4 + 2 /* offset into fptr_ with ASCII functions */;
+      return res;
+  }
+
+  return -1;
+}
+
+
+
+
+int mb_slave_close(int fd) {
+  #ifdef DEBUG
+  fprintf( stderr, "mb_slave_close(): nd = %d\n", fd);
+  #endif
+  get_ttyfd(); /* declare the ttyfd variable!! */
+  /* call layer 1 library */
+  /* will call one of modbus_tcp_close(), modbus_rtu_close(), modbus_ascii_close() */
+  return modbus_close(ttyfd);
+}
+
+
+
+
+
+/***********************************************/
+/***********************************************/
+/**                                           **/
+/**               Run the slave               **/
+/**                                           **/
+/***********************************************/
+/***********************************************/
+
+/* Execute infinite loop waiting and replying to requests coming from clients/master
+ * This function enters an infinite loop wating for new connection requests, 
+ * and for modbus requests over previoulsy open connections...
+ *
+ * The frames are read from:
+ *   -  the node descriptor nd, if nd >= 0
+ *       When using TCP, if the referenced node nd was created to listen for new connections
+ *       [mb_slave_listen()], then this function will also reply to Modbus data requests arriving
+ *       on other nodes that were created as a consequence of accepting connections requests to
+ *       the referenced node nd.
+ *       All other nodes are ignored!
+ *       
+ *   -  any valid and initialised TCP node descriptor, if nd = -1
+ *      In this case, will also accept connection requests arriving from a previously
+ *       created node to listen for new connection requests [mb_slave_listen() ].
+ *      NOTE: (only avaliable if using TCP)
+ * 
+ * slaveid identifies the address (RTU and ASCII) or slaveid (TCP) that we implement.
+ *     Any requests that we receive sent with a slaveid different
+ *     than the one specified, and also different to 0, will be silently ignored!
+ *     Whatever the slaveid specified, we always reply to requests
+ *     to slaveid 0 (the modbus broadcast address).
+ *     Calling this function with a slaveid of 0 means to ignore this 
+ *     parameter and to reply to all requests (whatever the slaveid
+ *     used in the request). This should mostly be used by TCP servers... 
+ */
+
+int mb_slave_run(int fd, mb_slave_callback_t callback_functions, u8 slaveid) {
+  int byte_count;
+  u16 transaction_id;
+  int nd;
+  u8 function, error_code = 0;
+  int resp_length;
+  u8 *query_packet = NULL;
+  u8 *resp_packet;
+  u8  resp_buffer_[RESP_BUFFER_SIZE];
+  u8  slave;
+  
+  get_ttyfd(); /* declare the ttyfd variable!! */
+
+  #ifdef DEBUG  
+  fprintf(stderr,"[%lu] mb_slave_run(): Called... fd=%d, ttyfd=%d\n", pthread_self(), fd, ttyfd);
+  #endif
+
+  while(1) {
+    nd = ttyfd;
+    /* will call one of modbus_tcp_read(), modbus_rtu_read(), modbus_ascii_read() */
+    do {
+        byte_count = modbus_read(&nd,              /* node descriptor          */
+                                 &query_packet,    /* u8 **recv_data_ptr,      */
+                                 &transaction_id,  /* u16 *transaction_id,     */
+                                 NULL,             /* const u8 *send_data,     */
+                                 0,                /* int send_length,         */
+                                 NULL  /* wait indefenitely */ /* const struct timespec *recv_timeout); */
+                                );
+    } while (byte_count <= 2);
+
+    #ifdef DEBUG
+    {/* display the hex code of each character received */
+      int i;
+      printf("[%lu] mb_slave_run() received %d bytes (ptr=%p): \n", pthread_self(), byte_count, query_packet);
+      for (i=0; i < byte_count; i++)
+        printf("<0x%2X>", query_packet[i]);
+      printf("\n");
+    }
+    #endif
+
+    slave    = query_packet[0];
+    function = query_packet[1];
+    
+    /* We only reply if:
+     *       - request was sent to broadcast address   (slave   == 0)
+     *  OR   - we were asked to reply to every request (slaveid == 0)
+     *  OR   - request matches the slaveid we were asked to accept (slave == slaveid)
+     * 
+     * Otherwise, silently ignore the received request!!!
+     */
+    if ((slaveid == 0) || (slave == 0) || (slave == slaveid)) {
+      resp_packet = resp_buffer_;
+      
+      switch(function) {
+        case 0x01: resp_length = handle_read_output_bits  (query_packet, &resp_packet, &error_code, &callback_functions); break;
+        case 0x02: resp_length = handle_read_input_bits   (query_packet, &resp_packet, &error_code, &callback_functions); break;
+        case 0x03: resp_length = handle_read_output_words (query_packet, &resp_packet, &error_code, &callback_functions); break;
+        case 0x04: resp_length = handle_read_input_words  (query_packet, &resp_packet, &error_code, &callback_functions); break;
+        case 0x05: resp_length = handle_write_output_bit  (query_packet, &resp_packet, &error_code, &callback_functions); break;
+        case 0x06: resp_length = handle_write_output_word (query_packet, &resp_packet, &error_code, &callback_functions); break;
+        case 0x0F: resp_length = handle_write_output_bits (query_packet, &resp_packet, &error_code, &callback_functions); break;
+        case 0x10: resp_length = handle_write_output_words(query_packet, &resp_packet, &error_code, &callback_functions); break;
+        /* return exception code 0x01 -> function not supported! */
+        default :  resp_length = -1; error_code = 0x01; break; 
+      }; /* switch(function) */
+      
+      if (resp_length < 0) {
+        /* return error... */
+        /* build exception response frame... */
+        resp_packet = resp_buffer_;
+        resp_packet[0] = query_packet[0]; /* slave */ 
+        resp_packet[1] = query_packet[1] | 0x80; /* function code with error bit activated! */ 
+        resp_packet[2] = error_code; 	
+        resp_length = 3;
+      }
+      modbus_write(nd, resp_packet, resp_length, transaction_id, NULL /*transmit_timeout*/);
+    }; /* if not ignore request */
+  }; /* while(1) */
+  
+  /* humour the compiler... */	
+  return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_slave.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2001,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+/* mb_slave.h */
+
+
+#ifndef MODBUS_SLAVE_H
+#define MODBUS_SLAVE_H
+
+#include <time.h> /* struct timespec data structure */
+
+#include "mb_types.h" /* get the data types */
+#include "mb_addr.h"  /* get definition of common variable types and error codes */
+
+
+
+
+/* Initialise the Modbus Library to work as Slave/Server only */
+int mb_slave_init(int nd_count);
+/* Shut down the Modbus Library */
+int mb_slave_done(void);
+
+
+
+
+/* Create a new slave/server...
+ * 
+ * This function creates a new node used to:
+ *   - accept connection requests (TCP version)
+ *   - receive frames from masters (RTU and ASCII versions)
+ * 
+ * The type of address (naf_tcp, naf_rtu, naf_ascii) will specify the lower
+ * layer of modbus that will be used by the newly opened node.
+ */
+int mb_slave_new(node_addr_t node_addr);
+/* close a node crreated by mb_slave_new() */
+int mb_slave_close(int nd);
+
+
+
+
+/***********************************************/
+/***********************************************/
+/**                                           **/
+/**               Run the slave               **/
+/**                                           **/
+/***********************************************/
+/***********************************************/
+
+
+/* The following functions must return:
+ *  -2  on attempt to read invalid address 
+ *  -1  on all other errors... 
+ *   0  on success.
+ *
+ *  Start_addr may start from 0, to 65535!
+ *  In other words, we use 0 based addressing!  
+ */ 
+typedef struct {
+  int (*read_inbits)   (void *arg, u16 start_addr, u16 bit_count,  u8  *data_bytes); /* bits are packed into bytes... */
+  int (*read_outbits)  (void *arg, u16 start_addr, u16 bit_count,  u8  *data_bytes); /* bits are packed into bytes... */
+  int (*write_outbits) (void *arg, u16 start_addr, u16 bit_count,  u8  *data_bytes); /* bits are packed into bytes... */
+  int (*read_inwords)  (void *arg, u16 start_addr, u16 word_count, u16 *data_words);
+  int (*read_outwords) (void *arg, u16 start_addr, u16 word_count, u16 *data_words);
+  int (*write_outwords)(void *arg, u16 start_addr, u16 word_count, u16 *data_words);
+  void *arg;
+ } mb_slave_callback_t;
+
+/* Execute the Slave and process requests coming from masters...
+ * This function enters an infinite loop wating for new connection requests, 
+ * and for modbus requests over previoulsy open connections...
+ *
+ * The frames are read from:
+ *   -  the node descriptor nd, if nd >= 0
+ *       When using TCP, if the referenced node nd was created to listen for new connections
+ *       [mb_slave_listen()], then this function will also reply to Modbus data requests arriving
+ *       on other nodes that were created as a consequence of accepting connections requests to
+ *       the referenced node nd.
+ *       All other nodes are ignored!
+ *       
+ *   -  any valid and initialised TCP node descriptor, if nd = -1
+ *      In this case, will also accept connection requests arriving from a previously
+ *       created node to listen for new connection requests [mb_slave_listen() ].
+ *      NOTE: (only avaliable if using TCP)
+ * 
+ * slaveid identifies the address (RTU and ASCII) or slaveid (TCP) that we implement.
+ *     Any requests that we receive sent with a slaveid different
+ *     than the one specified, and also different to 0, will be silently ignored!
+ *     Whatever the slaveid specified, we always reply to requests
+ *     to slaveid 0 (the modbus broadcast address).
+ *     Calling this function with a slaveid of 0 means to ignore this 
+ *     parameter and to reply to all requests (whatever the slaveid
+ *     used in the request). This should mostly be used by TCP servers... 
+ */
+int mb_slave_run(int nd, mb_slave_callback_t callback_functions, u8 slaveid);
+
+
+
+
+#endif  /* MODBUS_SLAVE_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_slave_and_master.c	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#include <fcntl.h>	/* File control definitions */
+#include <stdio.h>	/* Standard input/output */
+#include <string.h>
+#include <stdlib.h>
+#include <termio.h>	/* POSIX terminal control definitions */
+#include <sys/time.h>	/* Time structures for select() */
+#include <unistd.h>	/* POSIX Symbolic Constants */
+#include <errno.h>	/* Error definitions */
+
+#include "mb_layer1.h"
+#include "mb_slave_private.h"
+#include "mb_master_private.h"
+#include "mb_slave.h"
+#include "mb_master.h"
+
+//#define DEBUG 		/* uncomment to see the data sent and received */
+
+
+#ifndef TRUE
+#define TRUE  1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+
+
+layer1_funct_ptr_t fptr_[4] = {
+  { /* WARNING: TCP functions MUST be the first, as we have this hardcoded in the code! */
+    /*          more specifically, in the get_ttyfd() macro in mb_slave.c               */ 
+    /*                             in the mb_slave_new() function in mb_slave.c         */
+    /*                             in the mb_master_connect() function in mb_master.c   */    
+    &modbus_tcp_write          
+   ,&modbus_tcp_read           
+   ,&modbus_tcp_init           
+   ,&modbus_tcp_done           
+   ,&modbus_tcp_connect        
+   ,&modbus_tcp_listen         
+   ,&modbus_tcp_close          
+   ,&modbus_tcp_silence_init   
+   ,&modbus_tcp_get_min_timeout
+  },{
+    &modbus_rtu_write           
+   ,&modbus_rtu_read            
+   ,&modbus_rtu_init            
+   ,&modbus_rtu_done            
+   ,&modbus_rtu_connect         
+   ,&modbus_rtu_listen          
+   ,&modbus_rtu_close           
+   ,&modbus_rtu_silence_init    
+   ,&modbus_rtu_get_min_timeout 
+  },{
+    &modbus_ascii_write          
+   ,&modbus_ascii_read           
+   ,&modbus_ascii_init           
+   ,&modbus_ascii_done           
+   ,&modbus_ascii_connect        
+   ,&modbus_ascii_listen         
+   ,&modbus_ascii_close          
+   ,&modbus_ascii_silence_init   
+   ,&modbus_ascii_get_min_timeout
+  },{
+    NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
+  }
+};
+
+
+
+
+
+
+/************************************************************************
+
+	initialise / shutdown the library
+
+	These functions sets up/shut down the library state
+        (allocate memory for buffers, initialise data strcutures, etc)
+
+**************************************************************************/
+#define max(a,b) (((a)>(b))?(a):(b))
+
+int mb_slave_and_master_init(int nd_count_tcp, int nd_count_rtu, int nd_count_ascii) {
+        int extra_bytes, extra_bytes_tcp, extra_bytes_rtu, extra_bytes_ascii;
+
+#ifdef DEBUG
+	fprintf( stderr, "mb_slave_and_master_init()\n");
+	fprintf( stderr, "creating %d nodes\n", nd_count);
+#endif
+
+            /* initialise layer 1 library */
+	if (modbus_tcp_init  (nd_count_tcp,   DEF_OPTIMIZATION, &extra_bytes_tcp  ) < 0)
+		goto error_exit_0;
+	if (modbus_rtu_init  (nd_count_rtu,   DEF_OPTIMIZATION, &extra_bytes_rtu  ) < 0)
+		goto error_exit_1;
+	if (modbus_ascii_init(nd_count_ascii, DEF_OPTIMIZATION, &extra_bytes_ascii) < 0)
+		goto error_exit_2;
+	extra_bytes= max(extra_bytes_tcp, extra_bytes_rtu);
+	extra_bytes= max(extra_bytes    , extra_bytes_ascii);
+
+            /* initialise master and slave libraries... */
+	if (mb_slave_init__(extra_bytes) < 0)
+		goto error_exit_3;
+	if (mb_master_init__(extra_bytes) < 0)
+		goto error_exit_4;
+	return 0;
+
+/*
+error_exit_3:
+	modbus_master_done();
+*/
+error_exit_4:
+	mb_slave_done__();
+error_exit_3:
+	modbus_ascii_done();
+error_exit_2:
+	modbus_rtu_done();
+error_exit_1:
+	modbus_tcp_done();
+error_exit_0:
+	return -1;
+}
+
+
+
+
+int mb_slave_and_master_done(void) {
+	int res = 0;
+	res |= mb_slave_done__  ();
+	res |= mb_master_done__ ();
+	res |= modbus_ascii_done();
+	res |= modbus_rtu_done  ();
+	res |= modbus_tcp_done  ();
+	return res;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_slave_and_master.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+
+#include "mb_slave.h"
+#include "mb_master.h"
+
+
+/************************************************************************
+
+	initialise / shutdown the library
+
+	These functions sets up/shut down the library state
+        (allocate memory for buffers, initialise data strcutures, etc)
+
+**************************************************************************/
+
+
+int mb_slave_and_master_init(int nd_count_tcp, int nd_count_rtu, int nd_count_ascii);
+
+int mb_slave_and_master_done(void);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_slave_private.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#ifndef MODBUS_SLAVE_PRIVATE_H
+#define MODBUS_SLAVE_PRIVATE_H
+
+#include "mb_slave.h"
+#include "mb_util.h"
+
+
+#define DEF_LAYER2_SEND_RETRIES 1
+
+#define DEF_IGNORE_ECHO         0
+
+#define DEF_OPTIMIZATION        optimize_speed
+
+
+
+
+int mb_slave_init__(int extra_bytes);
+int mb_slave_done__(void);
+
+
+#endif  /* MODBUS_SLAVE_PRIVATE_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_tcp.c	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,1705 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+
+
+#include <fcntl.h>      /* File control definitions */
+#include <stdio.h>      /* Standard input/output */
+#include <string.h>
+#include <stdlib.h>
+#include <termio.h>     /* POSIX terminal control definitions */
+#include <sys/time.h>   /* Time structures for select() */
+#include <unistd.h>     /* POSIX Symbolic Constants */
+#include <assert.h>
+#include <errno.h>      /* Error definitions */
+#include <time.h>       /* clock_gettime()   */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>  /* required for htons() and ntohs() */
+#include <netinet/tcp.h> /* TCP level socket options */
+#include <netinet/ip.h>  /* IP  level socket options */
+
+#include <pthread.h>
+#include <sched.h>       /* sched_yield() */
+
+
+
+#include "sin_util.h"   /* internet socket utility functions... */
+#include "mb_layer1.h"  /* The public interface this file implements... */
+#include "mb_tcp_private.h"
+
+
+
+/************************************/
+/**                                **/
+/** Include common code...         **/
+/**                                **/
+/************************************/
+
+#include "mb_time_util.h"
+
+
+//#define ERRMSG
+#define ERRMSG_HEAD "Modbus/TCP: "
+
+
+// #define DEBUG       /* uncomment to see the data sent and received */
+
+
+#ifdef DEBUG
+#ifndef ERRMSG
+#define ERRMSG
+#endif
+#endif
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****                Forward Declarations                  ****/
+/****                    and Defaults                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+  /* A Node Descriptor metadata,
+   *   Due to the fact that modbus TCP is connection oriented,
+   *   and that if the client detects an error the connection
+   *   must be shut down and re-established automatically,
+   *   the modbus TCP layer needs to keep the address of the remote server.
+   *
+   * We do this by implementing a node descriptor table, in which each
+   *   entry will have the remote address, and the file descriptor
+   *   of the socket currently in use.
+   *
+   * We do not pass the file descriptor up to the next higher layer. We
+   *   send them the node descriptor instead...
+   */
+#define MB_MASTER_NODE 12
+#define MB_LISTEN_NODE 14
+#define MB_SLAVE_NODE  16
+#define MB_FREE_NODE   18
+typedef sa_family_t nd_type_t;
+
+typedef struct {
+    int    fd;                 /* socket descriptor == file descriptor */
+                               /* NOTE:
+                                *   Modbus TCP says that on error, we should close
+                                *   a connection and retry with a new connection.
+                                *   Since it takes time for a socket to close
+                                *   a connection if the remote server is down,
+                                *   we close the connection on the socket, close the
+                                *   socket itself, and create a new one for the new
+                                *   connection. There will be times when the node will
+                                *   not have any valid socket, and it will have to
+                                *   be created on the fly.
+                                *   When the node does not have a valid socket,
+                                *   fd will be set to -1
+                                */
+    int    node_type;          /*   What kind of use we are giving to this node...
+                                *   If node_type == MB_MASTER_NODE
+                                *      The node descriptor was initialised by the
+                                *      modbus_connect() function.
+                                *      The node descriptor is being used by a master
+                                *      device, and the addr contains the address of the slave.
+                                *      Remember that in this case fd may be >= 0 while
+                                *      we have a valid connection, or it may be < 0 when
+                                *      the connection needs to be reset.
+                                *   If node_type == MB_LISTEN_NODE
+                                *      The node descriptor was initialised by the
+                                *      modbus_listen() function.
+                                *      The node is merely used to accept() new connection
+                                *      requests. The new slave connections will use another
+                                *      node to transfer data.
+                                *      In this case fd must be >= 0.
+                                *      fd < 0 is an ilegal state and should never occur.
+                                *   If node_type == MB_SLAVE_NODE
+                                *      The node descriptor was initialised when a new
+                                *      connection request arrived on a MB_LISTEN type node.
+                                *      The node descriptor is being used by a slave device,
+                                *      and is currently being used to connect to a master.
+                                *      In this case fd must be >= 0.
+                                *      fd < 0 is an ilegal state and should never occur.
+                                *   If node_type == FREE_ND
+                                *      The node descriptor is currently not being used.
+                                *      In this case fd is set to -1, but is really irrelevant.
+                                */
+    struct sockaddr_in addr;   /* The internet adress we are using.
+                                *   If node_type == MB_MASTER_NODE
+                                *      addr will be the address of the remote slave
+                                *   If node_type == MB_LISTEN_NODE
+                                *      addr will be the address of the local listening port and network interface
+                                *   If node_type == MB_SLAVE_NODE
+                                *      addr will be the address of the local port and network interface
+                                *       of the connection to the specific client.
+                                */
+    int listen_node;           /* When a slave accepts a connection through a MB_LISTEN_NODE, it will
+                                * will use an empty node for the new connection, and configure this new node
+                                * to use the type MB_SLAVE_NODE.
+                                * The listen_node entry is only used by nodes of type MB_SLAVE_NODE.
+                                * In this case, listen_node will be the node of type MB_LISTEN_NODE through
+                                * which the connection request came through...
+                                */ 
+    int close_on_silence;      /* A flag used only by Master Nodes.
+                                * When (close_on_silence > 0), then the connection to the
+                                * slave device will be shut down whenever the
+                                * modbus_tcp_silence_init() function is called.
+                                * Remember that the connection will be automatically
+                                * re-established the next time the user wishes to communicate
+                                * with the same slave (using this same node descripto).
+                                * If the user wishes to comply with the sugestion
+                                * in the OpenModbus Spec, (s)he should set this flag
+                                * if a silence interval longer than 1 second is expected.
+                                */
+    int print_connect_error;   /* flag to guarantee we only print an error the first time we
+                                * attempt to connect to a emote server.
+                                * Stops us from generting a cascade of errors while the slave
+                                * is down.
+                                * Flag will get reset every time we successfully
+                                * establish a connection, so a message is once again generated 
+                                * on the next error.
+                                */
+    u8 *recv_buf;              /* This node's receive buffer
+                                * The library supports multiple simultaneous connections,
+                                * and may need to receive multiple frames through mutiple nodes concurrently.
+                                * To make the library thread-safe, we use one buffer for each node.
+                                */
+} nd_entry_t;
+
+
+/* please make sure to lock the node table mutex before calling this function */
+static int nd_entry_init(nd_entry_t *nde) {
+  nde->addr.sin_family = AF_INET  ;
+  nde->node_type = MB_FREE_NODE;
+  nde->fd = -1; /* not currently connected... */
+  /* initialise recv buffer */
+  nde->recv_buf = malloc(sizeof(u8) * RECV_BUFFER_SIZE);
+  if (nde->recv_buf == NULL)
+    return -1;
+  return 0;
+}
+
+/* please make sure to lock the node table mutex before calling this function */
+static int nd_entry_done(nd_entry_t *nde) {
+  free(nde->recv_buf);
+  return 0;
+}
+
+
+
+typedef struct {
+      /* the array of node descriptors, and current size... */
+    nd_entry_t      *node;           /* array of node entries. if NULL => node table not initialized */
+    int             node_count;      /* total number of nodes in the node[] array */
+    int             free_node_count; /* number of free nodes in the node[] array */
+    pthread_mutex_t mutex;
+} nd_table_t;
+
+
+
+static int nd_table_done(nd_table_t *ndt) {
+  int count;
+
+  if (ndt->node == NULL) 
+    return 0;
+
+  /* lock the mutex */
+  while (pthread_mutex_lock(&ndt->mutex) != 0) sched_yield();
+  
+  /* initialise the state of each node in the array... */
+  for (count = 0; count < ndt->node_count; count++) {
+    nd_entry_done(&ndt->node[count]);
+  } /* for() */
+
+  free(ndt->node);
+  pthread_mutex_unlock (&ndt->mutex);
+  pthread_mutex_destroy(&ndt->mutex);
+  *ndt = (nd_table_t){.node=NULL, .node_count=0, .free_node_count=0};
+  
+  return 0;
+}
+
+
+
+
+#if 1
+/* nd_table_init()
+ *   Version 1 of the nd_table_init() function. 
+ *   If called more than once, 2nd and any subsequent calls will
+ *   be interpreted as a request to confirm that it was already correctly 
+ *   initialized with the requested number of nodes.
+ */
+static int nd_table_init(nd_table_t *ndt, int nd_count) {
+  int count;
+
+  if (ndt->node != NULL) {
+    /* this function has already been called, and the node table is already initialised */
+    return (ndt->node_count == nd_count)?0:-1;
+  }
+  
+  /* initialise the node table mutex... */
+  pthread_mutex_init(&ndt->mutex, NULL);
+  if (pthread_mutex_lock(&ndt->mutex) != 0) {
+#ifdef DEBUG
+    perror("pthread_mutex_lock()");
+    fprintf(stderr, "[%lu] Unable to lock newly crated mutex while creating new node table!\n", pthread_self());
+#endif
+    pthread_mutex_destroy(&ndt->mutex);
+    return -1;
+  }
+  
+  /* initialise the node descriptor metadata array... */
+  ndt->node = malloc(sizeof(nd_entry_t) * nd_count);
+  if (ndt->node == NULL) {
+#ifdef DEBUG
+    perror("malloc()");
+    fprintf(stderr, "[%lu] Out of memory: error initializing node address buffer\n", pthread_self());
+#endif
+#ifdef ERRMSG
+    perror("malloc()");
+    fprintf(stderr, ERRMSG_HEAD "Out of memory. Error initializing node address buffer\n");
+#endif
+    pthread_mutex_unlock (&ndt->mutex);
+    pthread_mutex_destroy(&ndt->mutex);
+    return -1;
+  }
+
+  /* initialise the state of each node in the array... */
+  for (count = 0; count < nd_count; count++) {
+    if (nd_entry_init(&ndt->node[count]) < 0) {
+      pthread_mutex_unlock(&ndt->mutex);
+      nd_table_done(ndt);
+      return -1;
+    }
+    ndt->node_count = count+1;
+    ndt->free_node_count = count+1;
+  } /* for() */
+
+  ndt->node_count = nd_count;
+  ndt->free_node_count = nd_count;
+
+  pthread_mutex_unlock(&ndt->mutex);
+  return nd_count; /* number of succesfully created nodes! */
+}
+
+
+#else
+/* nd_table_init()
+ *   Version 2 of the nd_table_init() function. 
+ *   If called more than once, 2nd and any subsequent calls will
+ *   be interpreted as a request to reserve an extra new_nd_count
+ *   number of nodes. This will be done using realloc().
+ */
+static int nd_table_init(nd_table_t *ndt, int new_nd_count) {
+  int count;
+
+  if (ndt->node == NULL) {
+    /* Node table nt yet initialized => we must initialise the node table mutex... */
+    pthread_mutex_init(&ndt->mutex, NULL);
+  }
+  
+  /* lock the mutex */
+  while (pthread_mutex_lock(&ndt->mutex) != 0) sched_yield();
+  
+  /* initialise the node descriptor metadata array... */
+  ndt->node = realloc(ndt->node, sizeof(nd_entry_t) * (ndt->node_count + new_nd_count));
+  if (ndt->node == NULL) {
+#ifdef DEBUG
+    perror("malloc()");
+    fprintf(stderr, "[%lu] Out of memory: error initializing node address buffer\n", pthread_self());
+#endif
+#ifdef ERRMSG
+    perror("malloc()");
+    fprintf(stderr, ERRMSG_HEAD "Out of memory. Error initializing node address buffer\n");
+#endif
+    pthread_mutex_unlock (&ndt->mutex);
+    pthread_mutex_destroy(&ndt->mutex);
+    return -1;
+  }
+
+  /* initialise the state of each newly added node in the array... */
+  for (count = ndt->node_count; count < ndt->node_count + new_nd_count; count++) {
+    if (nd_entry_init(&ndt->node[count]) < 0) {
+      pthread_mutex_unlock(&ndt->mutex);
+      return -1;
+    }
+  } /* for() */
+  ndt->node_count      += new_nd_count;
+  ndt->free_node_count += new_nd_count;
+
+  pthread_mutex_unlock(&ndt->mutex);
+  return new_nd_count; /* number of succesfully created nodes! */
+}
+#endif
+
+
+static int nd_table_get_free_node(nd_table_t *ndt, nd_type_t nd_type) {
+  int count;
+
+  /* lock the mutex */
+  while (pthread_mutex_lock(&ndt->mutex) != 0) sched_yield();
+
+  /* check for free nodes... */
+  if (ndt->free_node_count <= 0) {
+    /* no free nodes... */
+    pthread_mutex_unlock(&ndt->mutex);
+    return -1;
+  }
+
+  /* Decrement the free node counter...*/
+  ndt->free_node_count--;
+
+  /* search for a free node... */
+  for (count = 0; count < ndt->node_count; count++) {
+    if(ndt->node[count].node_type == MB_FREE_NODE) {
+      /* found one!! Allocate it to the new type! */
+      ndt->node[count].node_type = nd_type;
+      pthread_mutex_unlock(&ndt->mutex);
+      return count;
+    }
+  } /* for() */
+
+  /* Strange... We should have free nodes, but we didn't finda any! */
+  /* Let's try to get into a consistent state, and return an error! */
+  ndt->free_node_count = 0;
+  pthread_mutex_unlock(&ndt->mutex);
+  return -1;
+}
+
+
+
+static void nd_table_close_node(nd_table_t *ndt, int nd) {
+
+  /* lock the mutex */
+  while (pthread_mutex_lock(&ndt->mutex) != 0) sched_yield();
+
+  if(ndt->node[nd].node_type == MB_FREE_NODE) {
+    /* Node already free... */
+    pthread_mutex_unlock(&ndt->mutex);
+    return;
+  }
+
+  /* Increment the free node counter...*/
+  ndt->free_node_count++;
+  /* Mark the node as being free. */
+  ndt->node[nd].node_type = MB_FREE_NODE;
+
+  pthread_mutex_unlock(&ndt->mutex);
+  return;
+}
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****                Global Library State                  ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+ /* The node descriptor table... */
+ /* NOTE: The node_table_ Must be initialized correctly here! */
+static nd_table_t nd_table_ = {.node=NULL, .node_count=0, .free_node_count=0};
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****              Local Utility functions...              ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+#define min(a,b) ((a<b)?a:b)
+#define max(a,b) ((a>b)?a:b)
+
+/************************************/
+/**                                **/
+/**  Configure socket for Modbus   **/
+/**                                **/
+/************************************/
+
+
+static int configure_socket(int socket_id) {
+
+  /* configure the socket */
+    /* Set it to be non-blocking. This is safe because we always use select() before reading from it! 
+     * It is also required for the connect() call. The default timeout in the TCP stack is much too long
+     * (typically blocks for 128 s ??) when the connect does not succedd imediately!
+     */
+  if (fcntl(socket_id, F_SETFL, O_NONBLOCK) < 0) {
+#ifdef ERRMSG
+    perror("fcntl()");
+    fprintf(stderr, ERRMSG_HEAD "Error configuring socket 'non-blocking' option.\n");
+#endif
+    return -1;
+  }
+
+  /* configure the socket */
+    /* set the TCP no delay flag. */
+  {int bool_opt = 1;
+  if (setsockopt(socket_id, SOL_TCP, TCP_NODELAY,
+                 (const void *)&bool_opt, sizeof(bool_opt))
+      < 0) {
+#ifdef ERRMSG
+    perror("setsockopt()");
+    fprintf(stderr, ERRMSG_HEAD "Error configuring socket 'TCP no delay' option.\n");
+#endif
+    return -1;
+  }
+  }
+
+    /* set the IP low delay option. */
+  {int priority_opt = IPTOS_LOWDELAY;
+  if (setsockopt(socket_id, SOL_IP, IP_TOS,
+                 (const void *)&priority_opt, sizeof(priority_opt))
+      < 0) {
+#ifdef ERRMSG
+    perror("setsockopt()");
+    fprintf(stderr, ERRMSG_HEAD "Error configuring socket 'IP low delay' option.\n");
+#endif
+    return -1;
+  }
+  }
+
+#if 0
+    /* send buffer */
+    /* NOTE: For slave devices, that may be receiving multiple
+     *       requests before they have a chance to reply to the first,
+     *       it probably is a good idea to have a large receive buffer.
+     *       So it is best to leave it with the default configuration, as it is
+     *       larger than the largest Modbus TCP frame.
+     *
+     *       For the send buffer, a smaller buffer should suffice.
+     *       However, it probably does not make sense to
+     *       waste time asking for a smaller buffer, since the larger
+     *       default buffer has already been allocated (the socket has already
+     *       been created!)
+     *
+     *       We might just as well leave out the configuration of the socket
+     *       buffer size...
+     */
+#define SOCK_BUF_SIZE 300 /* The size proposed in the Modbus TCP spec. */
+  {int sock_buf_size;
+  sock_buf_size = SOCK_BUF_SIZE;
+  if (setsockopt(socket_id, SOL_SOCKET, SO_SNDBUF,
+                 (const void *)&sock_buf_size, sizeof(sock_buf_size))
+      < 0)
+    return -1;
+    /* recv buffer */
+  sock_buf_size = SOCK_BUF_SIZE;
+  if (setsockopt(socket_id, SOL_SOCKET, SO_RCVBUF,
+             (const void *)&sock_buf_size, sizeof(sock_buf_size))
+      < 0)
+    return -1;
+  }
+#endif
+
+  return 0;
+}
+
+
+/************************************/
+/**                                **/
+/** Connect socket to remote host  **/
+/**                                **/
+/************************************/
+
+/* This function will create a new socket, and connect it to a remote host... */
+static inline int open_connection(int nd, const struct timespec *timeout) {
+  int socket_id, con_res;
+
+#ifdef DEBUG
+        printf("[%lu] open_connection(): called, nd = %d\n", pthread_self(), nd);
+#endif
+
+  if (nd_table_.node[nd].fd >= 0)
+    /* nd already connected) */
+    return nd_table_.node[nd].fd;
+
+  if (nd_table_.node[nd].addr.sin_family != AF_INET)
+    /* invalid remote address, or invalid nd */
+    return -1;
+
+  /* lets try to connect... */
+    /* create the socket */
+  if ((socket_id = socket(PF_INET, DEF_TYPE, 0 /* protocol_num */)) < 0) {
+#ifdef DEBUG
+    perror("socket()");
+    fprintf(stderr, "[%lu] Error creating socket\n", pthread_self());
+#endif
+#ifdef ERRMSG
+    perror("socket()");
+    fprintf(stderr, ERRMSG_HEAD "Error creating socket\n");
+#endif
+    return -1;
+  }
+
+  /* configure the socket - includes setting non-blocking option! */
+  if (configure_socket(socket_id) < 0) {
+    close(socket_id);
+    return -1;
+  };
+ 
+  /* establish the connection to remote host */
+  con_res = connect(socket_id,
+                    (struct sockaddr *)&(nd_table_.node[nd].addr),
+                    sizeof(nd_table_.node[nd].addr));
+
+  /* The following condition is not strictly necessary 
+   * (we could let the code fall through)
+   * but it does make the code easier to read/understand...
+   */
+  if (con_res >= 0)
+    goto success_exit; /* connected succesfully on first try! */
+    
+  if (con_res < 0) {
+    if ((errno != EINPROGRESS) && (errno != EALREADY))
+      goto error_exit; /* error in connection request! */
+
+    /* connection request is ongoing */
+    /* EINPROGRESS -> first call to connect, EALREADY -> subsequent calls to connect */
+    /* Must wait for connect to complete at most 'timeout' seconds */
+    {fd_set fdset;
+     int res, so_error;
+     socklen_t len;
+     struct timespec end_time, *et_ptr;
+     
+     et_ptr = NULL;
+     if (timeout != NULL) {
+        et_ptr = &end_time;
+       *et_ptr = timespec_add_curtime(*timeout);
+     }
+      
+     FD_ZERO(&fdset);
+     FD_SET(socket_id, &fdset);
+     
+     res = my_select(socket_id+1, NULL, &fdset, et_ptr);
+     if (res  < 0) goto error_exit; /* error on call to select */
+     if (res == 0) goto error_exit; /* timeout */
+     /* (res  > 0) -> connection attemt completed. May have been success or failure! */
+     
+     len = sizeof(so_error);
+     res = getsockopt(socket_id, SOL_SOCKET, SO_ERROR, &so_error, &len);
+     if (res  < 0)      goto error_exit; /* error on call to getsockopt */
+     if (so_error != 0) goto error_exit; /* error on connection attempt */
+     goto success_exit; /* succesfully completed connection attempt! */
+                        /* goto sucess_exit is not strcitly necessary - we could let the code fall through! */
+    }
+  }
+
+success_exit:
+  nd_table_.node[nd].fd = socket_id;
+  /* Succesfully established connection => print a message next time we have error. */
+  nd_table_.node[nd].print_connect_error = 1;  
+
+#ifdef DEBUG
+  printf("[%lu] open_connection(): returning...\n", pthread_self());
+#endif
+  return socket_id;
+
+error_exit:
+#ifdef ERRMSG
+    if (nd_table_.node[nd].print_connect_error > 0) {
+      perror("connect()");
+      fprintf(stderr, ERRMSG_HEAD "Error establishing socket connection.\n");
+      /* do not print more error messages for this node... */
+      nd_table_.node[nd].print_connect_error = 0;
+    }
+#endif
+    close(socket_id);
+    return -1;
+}
+
+
+/* This function will accept a new connection request, and attribute it to a new node... */
+static inline int accept_connection(int nd) {
+  int socket_id, new_nd;
+
+#ifdef DEBUG
+        printf("[%lu] accept_connection(): called, nd = %d\n", pthread_self(), nd);
+#endif
+
+  /* NOTE: We MUST accccept8) all connection requests, even if no new node is available.
+   *       => We first accept the connection request, and only later look for a node.
+   *          If no node is free/available for this new connections request, the 
+   *          connection will be accepted and immediately closed.
+   *       Reason:
+   *       When the library is used for a Modbus/TCP server and no free node is 
+   *        available, if we do not accept() all newly arrived connection requests
+   *        we would enter an infinite loop calling
+   *           - select() (in modbus_tcp_read()) 
+   *           - and accept_connection().
+   *        Note that select() will continue to return immediately if the 
+   *        connection request is not accept()ted!
+   */
+  /* lets accept new connection request... */
+  if ((socket_id = accept(nd_table_.node[nd].fd, NULL, NULL)) < 0) {
+#ifdef ERRMSG
+    perror("accept()");
+    fprintf(stderr, ERRMSG_HEAD "Error while waiting for connection request from new client\n");
+#endif
+    /* error establishing new connection... */
+    return -1;
+  }
+
+  /* find a free node */ 
+  if ((new_nd = nd_table_get_free_node(&nd_table_, MB_SLAVE_NODE)) < 0) {
+    /* no available free nodes for the new connection... */
+    close(socket_id);    
+    return -1;
+  }
+
+  /* configure the socket - includes setting the non-blocking option! */
+  if (configure_socket(socket_id) < 0) {
+    nd_table_close_node(&nd_table_, new_nd);  /* first free up the un-used node. */
+    close(socket_id);
+    return -1;
+  }
+
+  /* set up the node entry and update the fd sets */
+  nd_table_.node[new_nd].fd = socket_id;
+  nd_table_.node[new_nd].listen_node = nd;
+
+#ifdef DEBUG
+        printf("[%lu] accept_connection(): returning new_nd = %d\n", pthread_self(), new_nd);
+#endif
+  return new_nd;
+}
+
+
+static inline void close_connection(int nd) {
+  if (nd_table_.node[nd].fd >= 0) {
+    /* disconnect the tcp connection */
+    shutdown(nd_table_.node[nd].fd, SHUT_RDWR);
+#ifdef ERRMSG
+    int res =
+#endif
+    close(nd_table_.node[nd].fd);
+#ifdef ERRMSG
+    if (res < 0) {
+      perror("close()");
+      fprintf(stderr, ERRMSG_HEAD "Error closing socket\n");
+    }
+#endif
+    nd_table_.node[nd].fd = -1;
+  }
+
+  if (nd_table_.node[nd].node_type == MB_SLAVE_NODE) {
+    /* If it is a slave node, we will not be receiving any more data over this disconnected node,
+     * (MB_SLAVE_NODE do not get re-connected!), so we free the node...
+     */
+    nd_table_close_node(&nd_table_, nd);
+  }
+}
+
+
+
+/************************************/
+/**                                **/
+/**     Data format conversion     **/
+/**                                **/
+/************************************/
+
+/*
+ * Functions to convert u16 variables
+ * between network and host byte order
+ *
+ * NOTE: Modbus uses MSByte first, just like
+ *       tcp/ip, so we use the htons() and
+ *       ntoh() functions to guarantee
+ *       code portability.
+ */
+
+static inline u16 mb_hton(u16 h_value) {
+/*  return h_value; */
+  return htons(h_value);
+}
+
+static inline u16 mb_ntoh(u16 m_value) {
+/*  return m_value; */
+  return ntohs(m_value);
+}
+
+static inline u8 msb(u16 value) {
+/*  return Most Significant Byte of value; */
+  return (value >> 8) & 0xFF;
+}
+
+static inline u8 lsb(u16 value) {
+/*  return Least Significant Byte of value; */
+  return value & 0xFF;
+}
+
+#define u16_v(char_ptr)  (*((u16 *)(&(char_ptr))))
+
+
+/************************************/
+/**                                **/
+/**   Build/Check a frame header   **/
+/**                                **/
+/************************************/
+
+/* A modbus TCP frame header has 6 bytes...
+ *   header[0-1] -> transaction id
+ *   header[2-3] -> must be 0
+ *   header[4-5] -> frame data length (must be <= 255)
+ */
+#if TCP_HEADER_LENGTH < 6
+#error This code assumes a header size of 6 bytes, but TCP_HEADER_LENGTH < 6
+#endif
+
+static inline void build_header(u8 *header,
+                                u16 transaction_id,
+                                u16 byte_count)
+{
+  u16_v(header[0]) = mb_hton(transaction_id);
+  header[2] = 0;
+  header[3] = 0;
+  u16_v(header[4]) = mb_hton(byte_count);
+}
+
+
+static inline int check_header(u8  *header,
+                               u16 *transaction_id,
+                               u16 *byte_count)
+{
+  if ((header[2] != 0) || (header[3] != 0))
+    return -1;
+
+  *transaction_id = mb_ntoh(*(u16 *)(header + 0));
+  *byte_count     = mb_ntoh(*(u16 *)(header + 4));
+
+  if (*byte_count > MAX_L2_FRAME_LENGTH)
+    return -1;
+
+  return 0;
+}
+
+
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****              Sending of Modbus TCP Frames            ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+// pthread_mutex_t sendmsg_mutex = PTHREAD_MUTEX_INITIALIZER; 
+
+/* NOTE: this function MUST be thread safe!! */
+int modbus_tcp_write(int nd,  /* node descriptor */
+                     u8 *data,
+                     size_t data_length,
+                     u16 transaction_id,
+                     const struct timespec *transmit_timeout
+                     )
+{
+#define data_vector_size 2
+
+  u8            header[TCP_HEADER_LENGTH];
+  struct iovec  data_vector[data_vector_size] = {
+                         {(void *)header, TCP_HEADER_LENGTH},
+                         {NULL, 0}};
+  struct msghdr msg = {NULL, 0, data_vector, data_vector_size, NULL, 0, 0};
+  int res, bytes_sent;
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_write(): called...  nd=%d\n", pthread_self(), nd);
+#endif
+
+  if ((nd >= nd_table_.node_count) || (nd < 0))
+    /* invalid node descriptor... */
+    return -1;
+
+#ifdef DEBUG
+//  printf("[%lu] locking mutex...\n", pthread_self());
+#endif
+//  while (pthread_mutex_lock(&sendmsg_mutex) != 0);
+
+  /*************************
+  * prepare the header...  *
+  *************************/
+  build_header(header, transaction_id, data_length);
+#ifdef DEBUG
+/* Print the hex value of each character that is about to be
+ * sent over the bus.
+ */
+  { int i;
+    printf("modbus_tcp_write(): sending data...\n");
+    for(i = 0; i < TCP_HEADER_LENGTH; i++)
+      printf("[0x%2X]", header[i]);
+    for(i = 0; i < data_length; i++)
+      printf("[0x%2X]", data[i]);
+    printf("\n");
+  }
+#endif
+
+  /******************************************
+   * do we need to re-establish connection? *
+   ******************************************/
+  if (open_connection(nd, transmit_timeout) < 0) {
+#ifdef DEBUG
+    fprintf(stderr, "[%lu] modbus_tcp_write(): could not establish connection...\n", pthread_self());
+#endif
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "could not establish connection...\n");
+#endif
+    return -1;
+  }
+
+  /**********************
+   * write to output... *
+   **********************/
+   /* TWO ALTERNATIVE IMPLEMENTATIONS !!! */
+#if 0
+    /* write header */
+  bytes_sent = 0;
+  while (1) {
+    res = write(nd_table_.node[nd].fd, header+bytes_sent, TCP_HEADER_LENGTH-bytes_sent);
+    if (res < 0) {
+      if ((errno != EAGAIN ) && (errno != EINTR )) {
+        /* error sending message... */
+        close_connection(nd);
+        return -1;
+      } else {
+        continue;
+      }
+    } else {
+      /* res >= 0 */
+      bytes_sent += res;
+      if (bytes_sent >= TCP_HEADER_LENGTH) {
+        break;
+      }
+	}
+  }
+
+      /* write data */
+  bytes_sent = 0;
+  while (1) {
+    res = write(nd_table_.node[nd].fd, data+bytes_sent, data_length-bytes_sent);
+    if (res < 0) {
+      if ((errno != EAGAIN ) && (errno != EINTR )) {
+        /* error sending message... */
+        close_connection(nd);
+        return -1;
+      } else {
+        continue;
+      }
+    } else {
+      /* res >= 0 */
+      bytes_sent += res;
+      if (bytes_sent >= data_length) {
+        /* query succesfully sent! */
+#ifdef DEBUG
+        printf("[%lu] modbus_tcp_write(): sent %d bytes\n", pthread_self(), TCP_HEADER_LENGTH+data_length);
+#endif
+        return data_length;
+      }
+	}
+  }
+
+   /**********************
+   * write to output... *
+   **********************/
+#else
+  /* We are optimising for the most likely case, and in doing that
+   * we are making the least likely case have worse behaviour!
+   * Read on for an explanation...
+   *
+   * - The optimised behaviour for the most likely case:
+   * We have set the NO_DELAY flag on the socket, so the IP datagram
+   * is not delayed and is therefore sent as soon as any data is written to
+   * the socket.
+   * In order to send the whole message in a single IP datagram, we have to
+   * write both the the header and the data with a single call to write()
+   * In order to not to have to copy the data around just to add the
+   * message header, we use sendmsg() instead of write()!
+   *
+   * - The worse behaviour for the least likely case:
+   * If for some reason only part of the data is sent with the first call to
+   * write(), a datagram is sent right away, and the subsequent data will
+   * be sent in another datagram. :-(
+   */
+   /* NOTE: since snedmsg() is not thread safe, we use a mutex to protect access to this function... */
+
+  data_vector[data_vector_size - 1].iov_base = data;
+  data_vector[data_vector_size - 1].iov_len  = data_length;
+  data_vector[                   0].iov_base = header;
+  data_vector[                   0].iov_len  = TCP_HEADER_LENGTH;
+  bytes_sent = 0;
+  while (1) {
+    int sendmsg_errno;
+     /* Please see the comment just above the main loop!! */
+    res = sendmsg(nd_table_.node[nd].fd, &msg, 0);
+    sendmsg_errno = errno;
+    if (res < 0) {
+      if ((sendmsg_errno != EAGAIN ) && (sendmsg_errno != EINTR )) {
+        /* error sending message... */
+        close_connection(nd);
+        return -1;
+      } else {
+        continue;
+      }
+    } else {
+      /* res >= 0 */
+      bytes_sent += res;
+      if (bytes_sent >= data_length + TCP_HEADER_LENGTH) {
+        /* query succesfully sent! */
+#ifdef DEBUG
+        printf("[%lu] modbus_tcp_write(): sent %d bytes\n", pthread_self(), bytes_sent);
+#endif
+//        pthread_mutex_unlock(&sendmsg_mutex);
+#ifdef DEBUG
+//        printf("[%lu] unlocked  mutex...\n", pthread_self());
+#endif
+        return data_length;
+      }
+
+      /* adjust the data_vector... */
+      if (res < data_vector[0].iov_len) {
+        u8* tmp = data_vector[0].iov_base;
+        tmp += res; 
+        data_vector[0].iov_len -= res;
+        data_vector[0].iov_base = tmp;
+      } else {
+        u8* tmp = data_vector[1].iov_base;
+        tmp += res; 
+        res -= data_vector[0].iov_len;
+        data_vector[0].iov_len  = 0;
+        data_vector[1].iov_len -= res;
+        data_vector[1].iov_base = tmp;
+      }
+    }
+  } /* while (1) */
+#endif
+
+  /* humour the compiler... */
+//  pthread_mutex_unlock(&sendmsg_mutex);
+#ifdef DEBUG
+//  printf("[%lu] unlocked  mutex...\n", pthread_self());
+#endif
+  return -1;
+}
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****              Receiving Modbus TCP Frames             ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+/* A helper function to modbus_tcp_read()
+ *
+ * WARNING: The semantics of this function are not what you would expect!
+ *
+ *          if (data_already_available != 0)
+ *          It assumes that select() has already been called before
+ *          this function got called, and we are therefore guaranteed
+ *          to have at least one byte to read off the socket (the fd).
+ *
+ *          if (data_already_available == 0)
+ *          it starts off by calling select()!
+ *
+ *
+ * NOTE: Ususal select semantics for (a: end_time == NULL) and
+ *       (b: *end_time == 0) also apply.
+ *
+ *       (a) Indefinite timeout
+ *       (b) Try once, and and quit if no data available.
+ */
+/* RETURNS: number of bytes read
+ *          -1 read error!
+ *          -2 timeout
+ */
+static int read_bytes(int fd,
+                      u8 *data,
+                      int max_data_count,
+                      const struct timespec *end_time,
+                      int data_already_available)
+{
+  fd_set rfds;
+  int res, data_count;
+
+  data_count = 0;
+
+  while (data_count < max_data_count) {
+    /*============================*
+     * wait for data availability *
+     *============================*/
+    if (data_already_available == 0) {
+      int sel_res;
+      FD_ZERO(&rfds);
+      FD_SET(fd, &rfds);
+      sel_res = my_select(fd + 1, &rfds, NULL, end_time);
+      if (sel_res < 0)
+        return -1;
+      if (sel_res == 0)
+        /* timeout! */
+        return -2;
+    }
+
+    /*============================*
+     * read the available data... *
+     *============================*/
+    res = read(fd, data + data_count, max_data_count - data_count);
+    if (res == 0) {
+      /* We are guaranteed to have data to read off the fd since we called
+       * select(), but read() returned 0 bytes.
+       * This means that the remote process has closed down the connection,
+       * so we return 0.
+       */
+      return 0;
+    }
+
+    if (res < 0) {
+      if (errno != EINTR)
+        return -1;
+      else
+        res = 0;
+    }
+#ifdef DEBUG
+    {/* display the hex code of each character received */
+      int i;
+      for (i=0; i < res; i++)
+        printf("<0x%2X>", *(data + data_count + i));
+    }
+#endif
+    data_count += res;
+    data_already_available = 0;
+  } /* while ()*/
+
+  /* data read succesfully... */
+  return data_count;
+}
+
+
+
+/***************************************/
+/**                                   **/
+/**    Read a Modbus TCP frame        **/
+/**    off a single identified node.  **/
+/**                                   **/
+/***************************************/
+
+/* This private function will read a Modbus TCP frame off a single identified node
+ * that we know before hand that has data ready to be read off it. The data may or may not be
+ * a valid Modbus TCP frame. It is up to this function to figure that out.
+ */
+/* NOTES:
+ *  - We re-use the recv_buf_ to load the frame header, so we have to make
+ *    sure that the buffer is large enough to take it...
+ */
+ /* RETURNS: number of bytes read
+  *          -1 on read from file/node error
+  *          -2 on timeout
+  */
+#if RECV_BUFFER_SIZE < TCP_HEADER_LENGTH
+#error The receive buffer is smaller than the frame header length.
+#endif
+
+static int modbus_tcp_read_frame(int nd,
+                                 u16 *transaction_id,
+                                 struct timespec *ts_ptr) {
+  int fd, res;
+  u16 frame_length;
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_read_frame(): reading off nd=%d\n", pthread_self(), nd);
+#endif
+  /*=========================*
+   * read a Modbus TCP frame *
+   *=========================*/
+  /* assume error... */
+  fd = nd_table_.node[nd].fd;
+
+  /*-------------*
+   * read header *
+   *-------------*/
+  if ((res = read_bytes(fd, nd_table_.node[nd].recv_buf, TCP_HEADER_LENGTH, ts_ptr, 1)) != TCP_HEADER_LENGTH) { 
+#ifdef DEBUG
+    printf("[%lu] modbus_tcp_read_frame(): frame with insuficient bytes for a valid header...\n", pthread_self());
+#endif
+    if (res < 0) return res;
+    return -1;
+  }
+
+  /* let's check for header consistency... */
+  if (check_header(nd_table_.node[nd].recv_buf, transaction_id, &frame_length) < 0) {
+#ifdef DEBUG
+    printf("[%lu] modbus_tcp_read_frame(): frame with non valid header...\n", pthread_self());
+#endif
+    return -1;
+  }
+
+  /*-----------*
+   * read data *
+   *-----------*/
+  if ((res = read_bytes(fd, nd_table_.node[nd].recv_buf, frame_length, ts_ptr, 0)) != frame_length) { 
+#ifdef DEBUG
+    printf("[%lu] modbus_tcp_read_frame(): frame with non valid frame length...\n", pthread_self());
+#endif
+    if (res < 0) return res;
+    return -1;
+  }
+
+  /* frame received succesfully... */
+#ifdef DEBUG
+  printf("\n");
+#endif
+  return frame_length;
+}
+
+
+
+
+/***************************************/
+/**                                   **/
+/**    Read a Modbus TCP frame        **/
+/**    OR Accept connection requests  **/
+/**    off possibly multiple node...  **/
+/**                                   **/
+/***************************************/
+
+/* The public function that reads a valid modbus frame.
+ * The frame is read from...:
+ *   -  if (nd >= 0) and (nd is of type MB_MASTER_NODE or MB_SLAVE_NODE)
+ *          The frame is read from the node descriptor nd 
+ *   -  if (nd >= 0) and (nd is of type MB_LISTEN_NODE)
+ *          The frame is read from the all node descriptors of type MB_SLAVE_NODE that were
+ *          opened as a consequence of a connection request to the nd slave.
+ *          In this case, new connection requests to nd will also be accepted! 
+ *   -  if (nd == -1)
+ *          The frame is read from any valid and initialised node descriptor.
+ *          In this case, new connection requests to any nd of type MB_LISTEN_NODE will also be accepted! 
+ *          In this case, the node where the data is eventually read from is returned in *nd.
+ *
+ * The send_data and send_length parameters are ignored...
+ *  (However, these parameters must stay in order to keep the function 
+ *   interface identical to the ASCII and RTU versons!)
+ *
+ * return value: The length (in bytes) of the valid frame,
+ *               -1 on error
+ *
+ * NOTE: Ususal select semantics for (a: recv_timeout == NULL) and
+ *       (b: *recv_timeout == 0) also apply.
+ *
+ *       (a) Indefinite timeout
+ *       (b) Try once, and and quit if no data available.
+ */
+
+ /* RETURNS: number of bytes read
+  *          -1 on read from file/node error
+  *          -2 on timeout
+  */
+int modbus_tcp_read(int *nd,                /* node descriptor */
+                    u8 **recv_data_ptr,
+                    u16 *transaction_id,
+                    const u8 *send_data,   /* ignored ! */
+                    int send_length,       /* ignored ! */
+                    const struct timespec *recv_timeout) {
+
+  struct timespec end_time, *ts_ptr;
+  u8 *local_recv_data_ptr;
+  u16 local_transaction_id = 0;
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_read(): called...  nd=%d\n", pthread_self(), *nd);
+#endif
+
+  if (nd == NULL)
+    return -1;
+
+  if (*nd >= nd_table_.node_count)
+    /* invalid *nd                      */
+    /* remember that *nd < 0 is valid!! */
+    return -1;
+
+  if (recv_data_ptr == NULL)
+    recv_data_ptr = &local_recv_data_ptr;
+  if (transaction_id == NULL)
+    transaction_id = &local_transaction_id;
+
+  /* We will potentially call read() multiple times to read in a single frame.
+   * We therefore determine the absolute time_out, and use this as a parameter
+   * for each call to read_bytes() instead of using a relative timeout.
+   *
+   * NOTE: see also the timeout related comment in the read_bytes() function!
+   */
+  ts_ptr = NULL;
+  if (recv_timeout != NULL) {
+     ts_ptr = &end_time;
+    *ts_ptr = timespec_add_curtime(*recv_timeout);
+  }
+
+  /* If we must read off a single node... */
+  if (*nd >= 0)
+    /* but the node does not have a valid fd */
+    if ((nd_table_.node[*nd].node_type == MB_FREE_NODE) ||
+        (nd_table_.node[*nd].fd < 0))
+      /* then we return an error... */
+      return -1;
+
+  /* We will loop forever...
+   * We jump out of the loop and return from the function as soon as:
+   *  - we receive a valid modbus message;
+   *    OR
+   *  - we time out.
+   * 
+   *  NOTE: This loop will close connections through which we receive invalid frames.
+   *        This means that the set of nodes through which we may receive data may change with each
+   *        loop iteration.  => We need to re-calculate the fds in each loop iteration! 
+   */
+
+  while (1) {
+    int nd_count, fd_high;
+    fd_set rfds;
+
+    /* We prepare our fd sets here so we can later call select() */
+    FD_ZERO(&rfds);
+    fd_high = -1;
+
+    for (nd_count = 0; nd_count < nd_table_.node_count; nd_count++) {
+      if (nd_table_.node[nd_count].node_type != MB_FREE_NODE)
+      {
+        if ((*nd < 0)  // we select from all nodes 
+            || (*nd == nd_count)  // we select from this specific node
+              // we are listening on a MB_LISTEN_NODE, so we must also receive requests sent to slave nodes
+              // whose connection requests arrived through this MB_LISTEN_NDODE 
+            || ((nd_table_.node[nd_count].node_type == MB_SLAVE_NODE) && (nd_table_.node[nd_count].listen_node == *nd))) 
+        {
+          /* check if valid fd */
+          if (nd_table_.node[nd_count].fd >= 0) {
+            /* Add the descriptor to the fd set... */
+            FD_SET(nd_table_.node[nd_count].fd, &rfds);
+            fd_high = max(fd_high, nd_table_.node[nd_count].fd);
+          }
+        }
+      }
+    } /* for(;;) */
+
+#ifdef DEBUG
+    printf("[%lu] modbus_tcp_read(): while(1) looping. fd_high = %d, nd=%d\n", pthread_self(), fd_high, *nd);
+#endif
+
+    if (fd_high == -1)
+      /* we will not be reading from any node! */
+      return -1;
+
+    /* We now call select and wait for activity on the nodes we are listening to */
+    { int sel_res = my_select(fd_high + 1, &rfds, NULL, ts_ptr);
+      if (sel_res < 0)
+        return -1;
+      if (sel_res == 0)
+        /* timeout! */
+        return -2;
+    }
+
+    /* figure out which nd is ready to be read... */
+    for (nd_count = 0; nd_count < nd_table_.node_count; nd_count++) {
+      if ((nd_table_.node[nd_count].node_type != MB_FREE_NODE) &&
+          (nd_table_.node[nd_count].fd >= 0)) {
+        if (FD_ISSET(nd_table_.node[nd_count].fd, &rfds)) {
+          /* Found the node descriptor... */
+#ifdef DEBUG
+          printf("[%lu] modbus_tcp_read(): my_select() returned due to activity on node nd=%d\n", pthread_self(), nd_count);
+#endif
+          if (nd_table_.node[nd_count].node_type == MB_LISTEN_NODE) {
+            /* We must accept a new connection...
+             * No need to check for errors.
+             * If one occurs, there is nothing we can do...
+             */
+            accept_connection(nd_count);
+          } else {
+            /* it is a MB_SLAVE_NODE or a MB_MASTER_NODE */ 
+            /* We will read a frame off this nd */
+            int res;
+            res = modbus_tcp_read_frame(nd_count, transaction_id, ts_ptr);
+            if (res > 0) {
+              *nd = nd_count;
+              *recv_data_ptr = nd_table_.node[nd_count].recv_buf;
+              return res;
+            } 
+            if (res < 0) {
+                /* We had an error reading the frame...
+                 * We handle it by closing the connection, as specified by
+                 * the modbus TCP protocol!
+                 *
+                 * NOTE: The error may have been a timeout, which means this function should return immediately.
+                 *       However, in this case we let the execution loop once again
+                 *       in the while(1) loop. My_select() will be called again
+                 *       and the timeout detected. The timeout error code (-2)
+                 *       will then be returned correctly!
+                 */
+#ifdef DEBUG
+              printf("[%lu] modbus_tcp_read(): error reading frame. Closing connection...\n", pthread_self());
+#endif
+              /* We close the socket... */
+              close_connection(nd_count);
+            }
+          }
+          /* we have found the node descriptor, so let's jump out of the for(;;) loop */
+          break;
+        }
+      }
+    } /* for(;;) */
+
+    /* We were unsuccesfull reading a frame, so we try again... */
+  } /* while (1) */
+
+  /* humour the compiler... */
+  return -1;
+}
+
+
+
+
+
+/**************************************************************/
+/**************************************************************/
+/****                                                      ****/
+/****                                                      ****/
+/****        Initialising and Shutting Down Library        ****/
+/****                                                      ****/
+/****                                                      ****/
+/**************************************************************/
+/**************************************************************/
+
+
+/* Ugly hack...
+ *  Beremiz will be calling modbus_tcp_init() multiple times (through modbus_init() )
+ *    (once for each plugin instance)
+ *  It will also be calling modbus_tcp_done() the same number of times
+ *  We only want to really shutdown the library the last time it is called.
+ *  We therefore keep a counter of how many times modbus_tcp_init() is called,
+ *  and decrement it in modbus_tcp_done()
+ */
+int modbus_tcp_init_counter = 0;
+
+/******************************/
+/**                          **/
+/**   Load Default Values    **/
+/**                          **/
+/******************************/
+
+static void set_defaults(const char **service) {
+  /* Set the default values, if required... */
+  if (*service == NULL)
+    *service = DEF_SERVICE;
+}
+
+
+/******************************/
+/**                          **/
+/**    Initialise Library    **/
+/**                          **/
+/******************************/
+/* returns the number of nodes succesfully initialised...
+ * returns -1 on error.
+ */
+int modbus_tcp_init(int nd_count,
+                    optimization_t opt /* ignored... */,
+                    int *extra_bytes) {
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_init(): called...\n", pthread_self());
+  printf("[%lu] creating %d nodes:\n", pthread_self(), nd_count);
+#endif
+
+  modbus_tcp_init_counter++;
+  
+    /* set the extra_bytes value... */
+    /* Please see note before the modbus_rtu_write() function for a
+     * better understanding of this extremely ugly hack... This will be
+     * in the mb_rtu.c file!!
+     *
+     * The number of extra bytes that must be allocated to the data buffer
+     * before calling modbus_tcp_write()
+     */
+  if (extra_bytes != NULL)
+    *extra_bytes = 0;
+
+  if (0 == nd_count)
+    /* no need to initialise this layer! */
+    return 0;
+  if (nd_count <= 0)
+    /* invalid node count... */
+    goto error_exit_1;
+
+  /* initialise the node table... */
+  if (nd_table_init(&nd_table_, nd_count) < 0)
+    goto error_exit_1;
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_init(): %d node(s) opened succesfully\n", pthread_self(), nd_count);
+#endif
+  return nd_count; /* number of succesfully created nodes! */
+
+/*
+error_exit_2:
+  nd_table_done(&nd_table_);
+*/
+error_exit_1:
+  if (extra_bytes != NULL)
+    *extra_bytes = 0;
+  return -1;
+}
+
+
+
+
+
+
+/******************************/
+/**                          **/
+/**    Open a Master Node    **/
+/**                          **/
+/******************************/
+int modbus_tcp_connect(node_addr_t node_addr) {
+  int node_descriptor;
+  struct sockaddr_in tmp_addr;
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_connect(): called...\n", pthread_self());
+  printf("[%lu]        %s:%s\n", pthread_self(),
+         node_addr.addr.tcp.host,
+         node_addr.addr.tcp.service);
+#endif
+
+  /* Check for valid address family */
+  if (node_addr.naf != naf_tcp)
+    /* wrong address type... */
+    return -1;
+
+  /* set the default values... */
+  set_defaults(&(node_addr.addr.tcp.service));
+
+  /* Check the parameters we were passed... */
+  if(sin_initaddr(&tmp_addr,
+                  node_addr.addr.tcp.host,    0,
+                  node_addr.addr.tcp.service, 0,
+                  DEF_PROTOCOL)
+       < 0) {
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "Error parsing/resolving address %s:%s\n",
+                   node_addr.addr.tcp.host,
+                   node_addr.addr.tcp.service);
+#endif
+    return -1;
+  }
+
+  /* find a free node descriptor */
+  if ((node_descriptor = nd_table_get_free_node(&nd_table_, MB_MASTER_NODE)) < 0)
+    /* if no free nodes to initialize, then we are finished... */
+    return -1;
+
+  nd_table_.node[node_descriptor].addr = tmp_addr;
+  nd_table_.node[node_descriptor].fd   = -1; /* not currently connected... */
+  nd_table_.node[node_descriptor].close_on_silence = node_addr.addr.tcp.close_on_silence;
+
+  if (nd_table_.node[node_descriptor].close_on_silence < 0)
+    nd_table_.node[node_descriptor].close_on_silence = DEF_CLOSE_ON_SILENCE;
+  
+  /* WE have never tried to connect, so print an error the next time we try to connect */
+  nd_table_.node[node_descriptor].print_connect_error = 1;
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_connect(): returning nd=%d\n", pthread_self(), node_descriptor);
+#endif
+  return node_descriptor;
+}
+
+
+
+/******************************/
+/**                          **/
+/**    Open a Slave Node     **/
+/**                          **/
+/******************************/
+
+int modbus_tcp_listen(node_addr_t node_addr) {
+  int fd, nd;
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_listen(): called...\n", pthread_self());
+  printf("[%lu]        %s:%s\n", pthread_self(),
+         node_addr.addr.tcp.host,
+         node_addr.addr.tcp.service);
+#endif
+
+  /* Check for valid address family */
+  if (node_addr.naf != naf_tcp)
+    /* wrong address type... */
+    goto error_exit_0;
+
+  /* set the default values... */
+  set_defaults(&(node_addr.addr.tcp.service));
+
+  /* create a socket and bind it to the appropriate port... */
+  fd = sin_bindsock(node_addr.addr.tcp.host,
+                    node_addr.addr.tcp.service,
+                    DEF_PROTOCOL);
+  if (fd < 0) {
+#ifdef ERRMSG
+    fprintf(stderr, ERRMSG_HEAD "Could not bind to socket %s:%s\n", 
+                    ((node_addr.addr.tcp.host==NULL)?"#ANY#":node_addr.addr.tcp.host),
+                    node_addr.addr.tcp.service);
+#endif
+    goto error_exit_0;
+  }
+  if (listen(fd, DEF_MAX_PENDING_CONNECTION_REQUESTS) < 0)
+    goto error_exit_0;
+
+  /* find a free node descriptor */
+  if ((nd = nd_table_get_free_node(&nd_table_, MB_LISTEN_NODE)) < 0) {
+    /* if no free nodes to initialize, then we are finished... */
+    goto error_exit_1;
+  }
+
+  /* nd_table_.node[nd].addr = tmp_addr; */ /* does not apply for MB_LISTEN_NODE */
+  nd_table_.node[nd].fd = fd; /* not currently connected... */
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_listen(): returning nd=%d\n", pthread_self(), nd);
+#endif
+  return nd;
+
+error_exit_1:
+  close(fd);
+error_exit_0:
+  return -1;
+}
+
+
+
+/******************************/
+/**                          **/
+/**       Close a node       **/
+/**                          **/
+/******************************/
+
+int modbus_tcp_close(int nd) {
+#ifdef DEBUG
+  fprintf(stderr, "[%lu] modbus_tcp_close(): called... nd=%d\n", pthread_self(), nd);
+#endif
+
+  if ((nd < 0) || (nd >= nd_table_.node_count)) {
+    /* invalid nd */
+#ifdef DEBUG
+    fprintf(stderr, "[%lu] modbus_tcp_close(): invalid node %d. Should be < %d\n", pthread_self(), nd, nd_table_.node_count);
+#endif
+    return -1;
+  }
+
+  if (nd_table_.node[nd].node_type == MB_FREE_NODE)
+    /* already free node */
+    return 0;
+
+  close_connection(nd);
+
+  nd_table_close_node(&nd_table_, nd);
+
+  return 0;
+}
+
+
+
+/**********************************/
+/**                              **/
+/**  Close all open connections  **/
+/**                              **/
+/**********************************/
+
+int modbus_tcp_silence_init(void) {
+  int nd;
+
+#ifdef DEBUG
+  printf("[%lu] modbus_tcp_silence_init(): called...\n", pthread_self());
+#endif
+
+  /* close all master connections that remain open... */
+  for (nd = 0; nd < nd_table_.node_count; nd++)
+    if (nd_table_.node[nd].node_type == MB_MASTER_NODE)
+      if (nd_table_.node[nd].close_on_silence > 0)
+        /* node is is being used for a master device,
+         * and wishes to be closed...   ...so we close it!
+         */
+         close_connection(nd);
+
+  return 0;
+}
+
+
+
+/******************************/
+/**                          **/
+/**   Shutdown the Library   **/
+/**                          **/
+/******************************/
+
+int modbus_tcp_done(void) {
+  int i;
+  
+  modbus_tcp_init_counter--;
+  if (modbus_tcp_init_counter != 0) return 0; /* ignore this request */
+  
+    /* close all the connections... */
+  for (i = 0; i < nd_table_.node_count; i++)
+    modbus_tcp_close(i);
+
+  /* Free memory... */
+  nd_table_done(&nd_table_);
+
+  return 0;
+}
+
+
+
+
+double modbus_tcp_get_min_timeout(int baud,
+                                  int parity,
+                                  int data_bits,
+                                  int stop_bits) {
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_tcp_private.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#ifndef MODBUS_TCP_PRIVATE_H
+#define MODBUS_TCP_PRIVATE_H
+
+
+#include "mb_util.h"
+
+
+/* tcp port default configuration... */
+#define DEF_SERVICE  "502"        /* port used by modbus */
+#define DEF_PROTOCOL "tcp"        /* protocol used by modbus tcp */
+#define DEF_TYPE     SOCK_STREAM  /* Quality of service required of the socket... */
+#define DEF_MAX_PENDING_CONNECTION_REQUESTS 5
+                                  /* maximum number of pending connection requests
+                                   * that have not yet been accept()'ed
+                                   */
+#define DEF_CLOSE_ON_SILENCE 1    /* Used only by master nodes.
+                                   * Flag indicating whether, by default, the connection
+                                   * to the slave device should be closed whenever the
+                                   * modbus_tcp_silence_init() function is called.
+                                   *
+                                   * 0  -> do not close connection
+                                   * >0 -> close connection
+                                   *
+                                   * The spec sugests that connections that will not
+                                   * be used for longer than 1 second should be closed.
+                                   * Even though we expect most connections to have
+                                   * silence intervals much shorted than 1 second, we
+                                   * decide to use the default of shuting down the
+                                   * connections because it is safer, and most other
+                                   * implementations seem to do the same.
+                                   * If we do not close we risk using up all the possible
+                                   * connections that the slave can simultaneouly handle,
+                                   * effectively locking out every other master that
+                                   * wishes to communicate with that same slave.
+                                   */
+
+ /* Since the receive buffer is also re-used to store the frame header,
+  * we set it to the larger of the two.
+  */
+#if     TCP_HEADER_LENGTH > MAX_L2_FRAME_LENGTH
+#define RECV_BUFFER_SIZE    TCP_HEADER_LENGTH
+#else
+#define RECV_BUFFER_SIZE    MAX_L2_FRAME_LENGTH
+#endif
+
+
+
+
+#endif  /* MODBUS_TCP_PRIVATE_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_time_util.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+ /* Time handling functions used by the modbus protocols... */
+
+
+#ifndef __MODBUS_TIME_UTIL_H
+#define __MODBUS_TIME_UTIL_H
+
+
+/************************************/
+/**                                **/
+/**     Time format conversion     **/
+/**                                **/
+/************************************/
+
+/* Function to load a struct timeval correctly from a double. */
+static inline struct timeval d_to_timeval(double time) {
+  struct timeval tmp;
+
+  tmp.tv_sec  = time;
+  tmp.tv_usec = 1e6*(time - tmp.tv_sec);
+  return tmp;
+}
+
+
+/* Function to load a struct timespec correctly from a double. */
+static inline struct timespec d_to_timespec(double time) {
+  struct timespec tmp;
+
+  tmp.tv_sec  = time;
+  tmp.tv_nsec = 1e9*(time - tmp.tv_sec);
+  return tmp;
+}
+
+
+
+/* Function to ... */
+static inline struct timespec timespec_dif(struct timespec ts1, struct timespec ts2) {
+  struct timespec ts;
+
+  ts.tv_sec  = ts1.tv_sec  - ts2.tv_sec;
+  if(ts1.tv_nsec > ts2.tv_nsec) {
+    ts.tv_nsec = ts1.tv_nsec - ts2.tv_nsec;
+  } else {
+    ts.tv_nsec = 1000000000 + ts1.tv_nsec - ts2.tv_nsec;
+    ts.tv_sec--;
+  }
+
+  if (ts.tv_sec < 0)
+    ts.tv_sec = ts.tv_nsec = 0;
+
+  return ts;
+}
+
+/* Function to ... */
+static inline struct timespec timespec_add(struct timespec ts1, struct timespec ts2) {
+  struct timespec ts;
+
+  ts.tv_sec  = ts1.tv_sec  + ts2.tv_sec;
+  ts.tv_nsec = ts1.tv_nsec + ts2.tv_nsec;
+  ts.tv_sec += ts.tv_nsec / 1000000000;
+  ts.tv_nsec = ts.tv_nsec % 1000000000;
+  return ts;
+}
+
+/* Function to convert a struct timespec to a struct timeval. */
+static inline struct timeval timespec_to_timeval(struct timespec ts) {
+  struct timeval tv;
+
+  tv.tv_sec  = ts.tv_sec;
+  tv.tv_usec = ts.tv_nsec/1000;
+  return tv;
+}
+
+
+/*
+ * NOTE: clock_gettime() is rather expensive, between 7000 and 7500 clock
+ *       cycles (measured with rdtsc on an Intel Pentium)
+ *       gettimeofday() is half as expensive (3000 to 3500 clock cycles),
+ *       but is not POSIX compliant... :-(
+ *       Nevertheless this is peanuts (20 us on a 350 MHz cpu) compared to
+ *       the timescales required to read a modbus frame over a serial bus
+ *       (aprox. 10 ms for a 10 byte frame on a 9600 baud bus!)
+ */
+static inline struct timespec timespec_add_curtime(struct timespec ts) {
+  struct timespec  res     = {.tv_sec = 0, .tv_nsec = 0};
+  
+  /* is ts = 0 also return 0 !! */
+  if ((ts.tv_sec != 0) || (ts.tv_nsec != 0))
+    if (clock_gettime(CLOCK_MONOTONIC, &res) >= 0)
+      res = timespec_add(res, ts);
+  return res;
+}
+  
+/************************************/
+/**                                **/
+/** select() with absolute timeout **/
+/**                                **/
+/************************************/
+
+
+
+
+/* My private version of select using an absolute timeout, instead of the
+ * usual relative timeout.
+ *
+ * NOTE: Ususal select semantics for (a: end_time == NULL) and
+ *       (b: *end_time == 0) also apply.
+ *
+ *       (a) Indefinite timeout
+ *       (b) Try once, and and quit if no data available.
+ */
+/* Returns: -1 on error
+ *           0 on timeout
+ *          >0 on success
+ */
+static int my_select(int fd, fd_set *rfds, fd_set *wfds, const struct timespec *end_time) {
+
+  int res;
+  struct timespec cur_time;
+  struct timeval timeout, *tv_ptr;
+  fd_set tmp_rfds, *tmp_rfds_ptr, tmp_wfds, *tmp_wfds_ptr;
+  
+  tmp_rfds_ptr = NULL;
+  tmp_wfds_ptr = NULL;
+  if (rfds != NULL) tmp_rfds_ptr = &tmp_rfds;
+  if (wfds != NULL) tmp_wfds_ptr = &tmp_wfds;
+
+  /*============================*
+   * wait for data availability *
+   *============================*/
+  do {
+    if (rfds != NULL) tmp_rfds = *rfds;
+    if (wfds != NULL) tmp_wfds = *wfds;
+      /* NOTE: To do the timeout correctly we would have to revert to timers
+       *       and asociated signals. That is not very thread friendly, and is
+       *       probably too much of a hassle trying to figure out which signal
+       *       to use. What if we don't have any free signals?
+       *
+       *       The following solution is not correct, as it includes a race
+       *       condition. The following five lines of code should really
+       *       be atomic!
+       *
+       * NOTE: see also the timeout related comment in the
+       *       modbus_tcp_read() function!
+       */
+    if (end_time == NULL) {
+      tv_ptr = NULL;
+    } else {
+      tv_ptr = &timeout;
+      if ((end_time->tv_sec == 0) && (end_time->tv_nsec == 0)) {
+        timeout.tv_sec = timeout.tv_usec = 0;
+      } else {
+        /* ATOMIC - start */
+        if (clock_gettime(CLOCK_MONOTONIC, &cur_time) < 0)
+          return -1;
+        timeout = timespec_to_timeval(timespec_dif(*end_time, cur_time));
+      }
+    }
+
+    res = select(fd, tmp_rfds_ptr, tmp_wfds_ptr, NULL, tv_ptr);
+  /* ATOMIC - end */
+
+#ifdef DEBUG
+  {int i;
+   if (tmp_rfds_ptr != NULL)
+     for (i = 0; i < fd; i++)
+       if (FD_ISSET(i, tmp_rfds_ptr))
+         fprintf(stderr,"fd=%d is ready for reading\n", i);
+   if (tmp_wfds_ptr != NULL)
+     for (i = 0; i < fd; i++)
+       if (FD_ISSET(i, tmp_wfds_ptr))
+         fprintf(stderr,"fd=%d is ready for writing\n", i);
+  }
+#endif
+    if (res == 0) {
+#ifdef DEBUG
+      printf("Comms time out\n");
+#endif
+      return 0;
+    }
+    if ((res < 0) && (errno != EINTR)) {
+      return -1;
+    }
+  } while (res <= 0);
+
+  if (rfds != NULL) *rfds = tmp_rfds;
+  if (wfds != NULL) *wfds = tmp_wfds;
+  return res;
+}
+
+
+
+
+
+
+#endif  /* __MODBUS_TIME_UTIL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_types.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#ifndef __MB_TYPES_H
+#define __MB_TYPES_H
+
+#ifndef __PLC_TYPES_H
+ /* if we have already included the MatPLC's type definitions, we don't need to delare the types ourselves... */
+
+
+/* Tell the stdint.h file we want the limits of the data types defined. */
+/* If being compiled by a C++ compiler, this is required!               */
+/* If being compiled by a C   compiler, this is ignored!                */
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+
+
+/* We use the _leastX_t versions of the data types as these are guaranteed
+ * to be the exact size we want.
+ * The int8_t, etc..., may have been defined to be the same as the 
+ * _fastX_t version, which may take up more space than what is really wanted
+ * in order as to speed up memory access.
+ */
+typedef uint_least64_t      u64; /* 64-bit unsigned integer */
+typedef  int_least64_t      i64; /* 64-bit signed integer   */
+
+typedef uint_least32_t      u32; /* 32-bit unsigned integer */
+typedef  int_least32_t      i32; /* 32-bit signed integer   */
+
+typedef uint_least16_t      u16; /* 16-bit unsigned integer */
+typedef  int_least16_t      i16; /* 16-bit signed integer   */
+
+typedef uint_least8_t       u8;  /*  8-bit unsigned integer */
+typedef  int_least8_t       i8;  /*  8-bit signed integer   */
+
+
+
+
+#define u64_MAX UINT_LEAST64_MAX
+#define u64_MIN UINT_LEAST64_MIN
+#define i64_MAX  INT_LEAST64_MAX
+#define i64_MIN  INT_LEAST64_MIN
+
+#define u32_MAX UINT_LEAST32_MAX
+#define u32_MIN UINT_LEAST32_MIN
+#define i32_MAX  INT_LEAST32_MAX
+#define i32_MIN  INT_LEAST32_MIN
+
+#define u16_MAX UINT_LEAST16_MAX
+#define u16_MIN UINT_LEAST16_MIN
+#define i16_MAX  INT_LEAST16_MAX
+#define i16_MIN  INT_LEAST16_MIN
+
+#define u8_MAX UINT_LEAST8_MAX
+#define u8_MIN UINT_LEAST8_MIN
+#define i8_MAX  INT_LEAST8_MAX
+#define i8_MIN  INT_LEAST8_MIN
+
+
+#endif /* __PLC_TYPES_H */
+
+#endif /* __MB_TYPES_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mb_util.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+
+#ifndef MB_UTIL_H
+#define MB_UTIL_H
+
+/* This file has constants related to the modbus protocol */
+/*
+ * Some of these constants are specific to the layer two protocols
+ * (i.e. master and slave), while others are specific of the
+ * layer one protocols (i.e. rtu, ascii, tcp).
+ *
+ * a) Unfortunately, due to the nature of the modbus protocol, that does not
+ * include a frame size field in the layer 1 frame (see note 1), and the
+ * fact that we are implementing it at the user level, the implementation
+ * of some layer 1 protocols need to know the content of the layer 2 protocol
+ * in order to determine the size of the frame.
+ *
+ * b) The layer two message formats are in fact the same, just reversing the role
+ * being played (master or slave).
+ *
+ * Bothe a) and b) mean we need the same modbus protocol constants in several files.
+ * It ends up making more sense to put them all together in a single file, which
+ * makes updating easier, even though we are trying to strictly seperate the layer 1
+ * and layer 2 protocols.
+ *
+ *
+ *
+ * Notes:
+ *  (1) There is no layer 1 field with the frame size, nevertheless this
+ *      size can be determined indirectly due to timing restrictions on the rtu
+ *      protocol. Unfortunately, due to the fact that we are implementing
+ *      it at the user level, we are not guaranteed to detect these timings
+ *      correctly, and therefore have to rely on layer 2 protocol info to
+ *      determine the frame size.
+ *      For the ascii protocol, the frame size is determined indirectly by
+ *      a frame header and tail, so we do not use layer 2 protocol info.
+ */
+
+
+ /* Layer 2 Frame Structure...                */
+ /* Valid for both master and slave protocols */
+#define L2_FRAME_HEADER_LENGTH    6
+#define L2_FRAME_BYTECOUNT_LENGTH 1
+#define L2_FRAME_DATABYTES_LENGTH 255
+#define MAX_L2_FRAME_LENGTH (L2_FRAME_HEADER_LENGTH + L2_FRAME_BYTECOUNT_LENGTH +    \
+                             L2_FRAME_DATABYTES_LENGTH)
+
+#define L2_FRAME_SLAVEID_OFS    0
+#define L2_FRAME_FUNCTION_OFS   1
+
+ /* Layer 1 - Ascii Frame sizes... */
+#define L2_TO_ASC_CODING        2 /* number of ascii bytes used to code a Layer 2 frame byte */
+#define ASC_FRAME_HEADER_LENGTH 1
+#define ASC_FRAME_HEADER        ':'
+#define ASC_FRAME_TAIL_LENGTH   2
+#define ASC_FRAME_TAIL_0        '\13' /* 'CR' */
+#define ASC_FRAME_TAIL_1        '\10' /* 'LF' */
+#define ASC_FRAME_LRC_LENGTH    2
+
+ /* Layer 1 - RTU Frame sizes... */
+#define RTU_FRAME_CRC_LENGTH    2
+
+ /* Layer 1 - TCP Frame sizes... */
+#define TCP_HEADER_LENGTH       6
+
+ /* Global Frame sizes */
+#define MAX_RTU_FRAME_LENGTH MAX_L2_FRAME_LENGTH + RTU_FRAME_CRC_LENGTH
+#define MAX_ASC_FRAME_LENGTH ((MAX_L2_FRAME_LENGTH * L2_TO_ASC_CODING) +             \
+                              ASC_FRAME_HEADER_LENGTH + ASC_FRAME_TAIL_LENGTH +      \
+                              ASC_FRAME_LRC_LENGTH)
+                              
+
+/* Modbus Exception codes */
+#define ERR_ILLEGAL_FUNCTION                        0x01
+#define ERR_ILLEGAL_DATA_ADDRESS                    0x02 
+#define ERR_ILLEGAL_DATA_VALUE                      0x03 
+#define ERR_SLAVE_DEVICE_FAILURE                    0x04 
+#define ERR_ACKNOWLEDGE                             0x05 
+#define ERR_SLAVE_DEVICE_BUSY                       0x06 
+#define ERR_NEGATIVE_ACKNOWLEDGE                    0x07 
+#define ERR_MEMORY_PARITY_ERROR                     0x08 
+#define ERR_GATEWAY_PATH_UNAVAILABLE                0x0A 
+#define ERR_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND 0x0B 
+
+
+
+#endif  /* MB_UTIL_H */
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sin_util.c	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+/* sin_util.c */
+
+#include "sin_util.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>   // gethostbyname(), ...
+#include <errno.h>   // errno
+#include <stdlib.h>  // strtoll()
+#include <ctype.h>   // isspace()
+#include <string.h>  // memcpy(), memset()
+#include <strings.h> // strcasecmp()
+#include <stdio.h>   // perror()
+
+#ifndef INADDR_NONE
+#define INADDR_NONE       0xffffffff
+#endif
+
+/* Last time I (msousa) checked, this was not being defined in QNX */
+#ifndef socklen_t
+typedef unsigned int socklen_t;
+#endif
+
+
+static inline int str_is_whitespace(const char *str) {
+  const char *s;
+  for (s = str; *s; s++) if (!isspace(*s)) return 0; // non whitespace char found
+  return 1; // all whitespace, or empty ""
+}
+
+/* Convert a string to a number, allowing for leading and trailing whitespace */
+/* Number may be in decimal, hexadecimal (leading '0x') or octal (leading '0') format */
+static long long str_to_portnum(const char *str) {
+  long long port = 0;
+  char *errstr;
+  #define PORT_MIN 0
+  #define PORT_MAX 65535
+  errno = 0; // strtoll() does not set errno to 0 on success!!
+  port = strtoll(str, &errstr, 0 /* accept base 8, 10 and 16 */);
+  if (str   == errstr)                        return -1; // not a number
+  if (errno == ERANGE)                        return -2; // out of range
+  if (!str_is_whitespace(errstr))             return -1; // not a number (has trailing characters)
+  if ((port < PORT_MIN) || (port > PORT_MAX)) return -2; // out of range  
+  return (port);
+}
+
+
+
+static int gettypebyname(const char *protocol) {
+   if (strcasecmp(protocol, "tcp") == 0) return SOCK_STREAM;
+   if (strcasecmp(protocol, "udp") == 0) return SOCK_DGRAM;
+   if (strcasecmp(protocol, "raw") == 0) return SOCK_RAW;
+   return SOCK_PACKET; // a deprecated service type, we use here as error.
+}
+
+
+static int getportbyname(in_port_t *port, const char *service, const char *protocol) {
+   struct servent *se;
+   int32_t tmp;   
+   // if service is NULL, "" or string of whitespace, then set port to 0, and return -1.
+   // Used when binding to a random port on the local host...
+   if (    port == NULL)                                 {                              return -1;}
+   if ( service == NULL)                                 {*port = 0;                    return -1;}
+   if (str_is_whitespace(service))                       {*port = 0;                    return -1;}
+   if ((se  = getservbyname(service, protocol)) != NULL) {*port = se->s_port;           return  0;}
+   if ((tmp = str_to_portnum(service)) >= 0)             {*port = htons((uint16_t)tmp); return  0;}
+   return -2;
+}
+
+
+static int getipbyname(struct in_addr *ip_addr, const char *host) {
+   struct hostent *he;
+   // if host is NULL, "", or "*", then set ip_addr to INADDR_ANY, and return -1.
+   // Used when binding to all interfaces on the local host...
+   if ( host == NULL)                                 {ip_addr->s_addr = INADDR_ANY; return -1;}
+   if (str_is_whitespace(host))                       {ip_addr->s_addr = INADDR_ANY; return -1;}
+   if (strcmp(host, "*") == 0)                        {ip_addr->s_addr = INADDR_ANY; return -1;}
+   if ((he = gethostbyname(host)) != NULL) {memcpy((char *)ip_addr, he->h_addr, he->h_length); return 0;}
+   if ((ip_addr->s_addr = inet_addr(host)) != INADDR_NONE) {return 0;}
+   return -2;
+}
+
+
+
+
+
+int sin_initaddr(struct sockaddr_in *sin,
+                  const char *host,    int allow_null_host, // 1 => allow host NULL, "" or "*" -> INADDR_ANY
+                  const char *service, int allow_null_serv, // 1 => allow serivce NULL or ""   -> port = 0
+                  const char *protocol) {
+  int he = allow_null_host?-1:0;
+  int se = allow_null_serv?-1:0;
+
+  memset((void *)sin, 0, sizeof(sin));
+  sin->sin_family = AF_INET;
+  
+  if (getportbyname(&(sin->sin_port), service, protocol) < se) return -1; 
+  if (getipbyname  (&(sin->sin_addr), host)              < he) return -1;
+  return 0;
+}
+
+
+
+/* Create a socket for the IP protocol family, and connect to remote host. */
+int sin_connsock(const char *host, const char *service, const char *protocol) {
+  struct sockaddr_in sin;
+  int s, type;
+
+  if (sin_initaddr(&sin, host, 0, service, 0, protocol) < 0) return -1;
+  if ((type = gettypebyname(protocol)) == SOCK_PACKET)       return -1;
+  /* create the socket */
+  if ((s = socket(PF_INET, type, 0)) < 0)                   {perror("socket()");  return -1;}   
+  /* connect the socket */
+  if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {perror("connect()"); return -1;}
+
+  return(s);
+}
+
+
+
+
+/* Create a socket for the IP protocol family, and bind to local host's port. */
+int sin_bindsock(const char *host, const char *service, const char *protocol) {
+  struct sockaddr_in sin;
+  int s, type;
+
+  // TODO allow random port... Needs new input parameter to function interface!
+  if (sin_initaddr(&sin, host, 1, service, 0, protocol) < 0) return -1;
+  if ((type = gettypebyname(protocol)) == SOCK_PACKET)       return -1;
+  /* create the socket */
+  if ((s = socket(PF_INET, type, 0)) < 0)                   {perror("socket()");  return -1;}   
+  /* bind the socket */
+  if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0)   {perror("bind()");    return -1;}
+  
+  return(s);
+}
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sin_util.h	Sun Mar 05 00:05:46 2017 +0000
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus 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 3 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 Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+#ifndef SIN_UTIL_H
+#define SIN_UTIL_H
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+
+/* Create a socket for the IP protocol family, and connect to remote host. */
+int sin_connsock(const char *host, const char *service, const char *protocol);
+
+/* Create a socket for the IP protocol family, and bind to local host's port. */
+int sin_bindsock(const char *host, const char *service, const char *protocol);
+
+/* Initialize a in_addr structure */
+int sin_initaddr(struct sockaddr_in *sin,
+                 const char *host,    int allow_null_host, // 1 => allow host NULL, "" or "*" -> INADDR_ANY
+                 const char *service, int allow_null_serv, // 1 => allow serivce NULL or ""   -> port = 0
+                 const char *protocol);
+#endif