From 6360152627c9a4e0a1f420a0868f7c7eff2917cb Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 22 Jan 2010 15:59:10 +0100 Subject: [PATCH] Add brcm_iscsi_uio The bnx2i driver requires ARP and DHCP support from userspace. This patch adds the required daemons for this. Signed-off-by: Hannes Reinecke diff --git a/brcm_iscsi_uio/COPYING b/brcm_iscsi_uio/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/brcm_iscsi_uio/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/brcm_iscsi_uio/ChangeLog b/brcm_iscsi_uio/ChangeLog new file mode 100644 index 0000000..7e94f1d --- /dev/null +++ b/brcm_iscsi_uio/ChangeLog @@ -0,0 +1,7 @@ +Version 0.4.1 (July 20, 2009) + * Fix from Mike Christie to determine page size from getpagesize() + rather then the constant PAGE_SIZE. PAGE_SIZE is not defined om + ia64 and ppc. + * Update documentation to indicate IPv6 is not supported + * Fix code to catch the message from the CNIC that the network + interface is going down. diff --git a/brcm_iscsi_uio/INSTALL b/brcm_iscsi_uio/INSTALL new file mode 100644 index 0000000..8b82ade --- /dev/null +++ b/brcm_iscsi_uio/INSTALL @@ -0,0 +1,291 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007, 2008 Free Software Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *Note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/brcm_iscsi_uio/Makefile.am b/brcm_iscsi_uio/Makefile.am new file mode 100644 index 0000000..6be8eea --- /dev/null +++ b/brcm_iscsi_uio/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS= src + +EXTRA_DIST = build_date + +build_date: + echo 'char *build_date ="'`date`'";' > build_date.c + echo 'char *build_date; '> build_date.h diff --git a/brcm_iscsi_uio/README b/brcm_iscsi_uio/README new file mode 100644 index 0000000..bd13e29 --- /dev/null +++ b/brcm_iscsi_uio/README @@ -0,0 +1,100 @@ +Broadcom iSCSI Userspace Tools +Version 0.5.2 +Dec 10, 2009 +------------------------------------------------------ + +This tools is to be used in conjunction with the Broadcom NetXtreme II Linux +driver (Kernel module name: 'bnx2' and 'bnx2x'), Broadcom CNIC driver, +and the Broadcom iSCSI driver (Kernel module name: 'bnx2i'). +This user space driver is used in conjunction with the following +Broadcom Network Controllers: + bnx2: BCM5708, BCM5709 devices + bnx2x: BCM57710, BCM57711, BCM57711E devices + +This utility will provide the ARP and DHCP functionality for the iSCSI offload. +The communication to the driver is done via Userspace I/O (Kernel module name +'uio'). + +There are two components to this application: + +1. 'brcm_iscsiuio' - This is the daemon which aids in creating iSCSI offloaded + connections. + +Dependencies: +======================================= + +Linux Kernel Dependencies: +1. Broadcom CNIC driver (cnic) +1. Broadcom iSCSI offload driver (bnx2i) +2. Userspace I/O driver (uio) + +Directory Structure of this Package: +======================================= + + + | + +-doc (documentation directory: man pages) + | + +-src + | + +- uip - the uIP stack + | + +- unix - brcm_iscsiuio source + + + +Compiling / Installing +======================================= + +1. Please untar the tarball. +2. Run the configure script. This will create the Makefiles and proper + header files needed for the build. +3. Run 'make'. This will create 2 binaries, 'brcm_iscsiuio' and + 'admin_client' +4. Run 'make install' to place the binaries in their installed location. + +iscsid IFACE Configuration File: +======================================= +The network interface configuration files are driven by the iscsid iface +files. The configuration data is parsed by iscsid and passed to the uIP +stack when the connection is established. + +One can use the following iscsiadm commands to create/set the configuration +using the iface files: + +To create the iface file: + iscsiadm -m iface -I --op=new + +To update the iface file: + + To use a static IPv4 address: + iscsiadm -m iface -I --op=update --name=iface.ipaddress --value= + + To use a DHCP address: + iscsiadm -m iface -I --op=update --name=iface.ipaddress --value=0.0.0.0 + + The following values are required. + + To specify the bnx2i as the transport: + iscsiadm -m iface -I --op=update --name=iface.transport_name --value=bnx2i + + To specify the network interface name: + iscsiadm -m iface -I --op=update --name=iface.net_ifacename --value= + + Now all the settings should be set so that one could connect to their + desired iSCSI target. + iscsiadm -m node -p -T -I --login + +Limitations: +======================================= +* RX iSCSI ring: + * default ring size is 3 entries + * default buffer size is 0x400 bytes +* TX iSCSI ring: + * default ring size of 1 entry + * default buffer size is 0x400 bytes + +Any packets larger then the buffer size will not be sent/received by the +hardware and will be dropped. + +There is no IPv6 support. diff --git a/brcm_iscsi_uio/RELEASE.TXT b/brcm_iscsi_uio/RELEASE.TXT new file mode 100644 index 0000000..38947af --- /dev/null +++ b/brcm_iscsi_uio/RELEASE.TXT @@ -0,0 +1,3968 @@ + Release Notes + Broadcom uIP Linux Driver + Version 0.5.2 + 12/10/2009 + + Broadcom Corporation + 5300 California Avenue, + Irvine, CA 92617 + + Copyright (c) 2004 - 2009 Broadcom Corporation + All rights reserved + +uIP v0.5.2 (Dec 10, 2009) +======================================================= + Fixes + ----- + 1. Problem: Switching between 10G and 1G iSCSI offloaded + devices caused login connectivity problems + + Cause: The NIC devices within uIP were not cleanup + properly. + + Change: The NIC structure is not re-initialized and the + NIC thread is destroyed when the host network + interface is brought down. + + Impact: None. + + +uIP v0.5.1 (Dec 9, 2009) +======================================================= + Fixes + ----- + 1. Problem: 10G devices behind PCI bridges would not collect + + Cause: PCI bus:slot.func string was parsed incorrectly + because the bridge string was used + + Change: Parse the proper PCI bus:slot.func string. + + Impact: None. + + +uIP v0.5.0b (Nov 24, 2009) +======================================================= + Initial release + + Enhancements + ------------ + + 1. Change: Add Broadcom 10G iSCSI offload support + + Impact: Linux + + +bnx2 v1.9.41b (Nov 19, 2009) cnic 1.9.11b (Nov 19, 2009) +======================================================= + Fixes + ----- + 1. Problem: (CQ44617) Compile error on SLES10 and RHEL4 kernels. + + Cause: bool not defined on older kernels. + + Change: Added compatibility code. + + Impact: None. + + 2. Problem: (CQ42096) iSCSI sessions do not recover after running + selftest/ifdown/etc with max sessions connected. + + Cause: cnic_lock mutex has circular dependency, causing deadlock + when iscsid tries to cleanup connections. + + Change: Merged in upstream cnic patches to avoid circular + dependency. + + Impact: None. + + Enhancements + ------------ + Change: (CQ43088) Changed the firmware version string format. + + Impact: None. + +bnx2 v1.9.40b (Nov 18, 2009) cnic 1.9.10b (Nov 12, 2009) +======================================================= + Fixes + ----- + 1. Problem: (CQ44528) Kernel runs out of memory after load/unload bnx2. + + Cause: RX SKB buffers were not freed. + + Change: Fixed the regression introduced by VMWare development. + + Impact: None. + + 2. Problem: (CQ44399) iLAB 5.10 connection establishment test failure. + + Cause: Firmware SYN-ACK handling issue. + + Change: Updated firmware to 5.0.0.j6 in bnx2_fw.h and 5.0.0.j9 in + bnx2_fw2.h. + + Impact: iSCSI. + + Enhancements + ------------ + Change: (CQ43088) Added VPD-R V0 firmware version string to ethtool -i. + + Impact: None. + +bnx2 v1.9.39b (Nov 6, 2009) cnic 1.9.10b (Nov 12, 2009) +======================================================= + Fixes + ----- + 1. Problem: (CQ44470) Kernel panics when bnx2/bnx2x is unloaded with + active iSCSI sessions. + + Cause: NETDEV_UNREGISTER will cause bcm570x_* transport to be + destroyed while sessions may still be active. + + Change: Use module referencing to prevent bnx2/bnx2x from unloading. + This is a simple interim solution to revert back to the + old behavior. In T6.0, we will migrate to the upstream + driver architecture and network drivers will be able to be + unloaded again. + + Impact: None. + + Enhancements + ------------ + Change: Added a short register dump during NETDEV_WATCHDOG. + + Impact: None. + +bnx2 v1.9.38b (Nov 6, 2009) cnic 1.9.9b (Nov 6, 2009) +===================================================== + Fixes + ----- + 1. Problem: (CQ44446) cnic panics during 10G iSCSI initialization. + + Cause: Recent interface changes introduced this regression. + cnic_service_bnx2x() panics when accessing uninitialized + status block pointer. + + Change: Added state checking in cnic_service_bnx2x(). + + Impact: None. + + 2. Problem: (CQ44439) bnx2 does not compile on older 2.6 kernels. + + Cause: mutex not defined on older kernels. + + Change: Use rtnl_lock() to protect bnx2_reset_task(). + + Impact: None. + +bnx2 v1.9.37b (Nov 4, 2009) cnic 1.9.8b (Nov 3, 2009) +===================================================== + Enhancements + ------------ + + 1. Change: Updated 5708/5706 firmware to 5.0.0.j5 for better small packet + performance. + + Impact: 5706/5708. + + Fixes + ----- + 1. Problem: (CQ43280) Cannot unload cnic after bnx2/bnx2x is loaded. + + Cause: module referencing. + + Change: Changed module referencing scheme. + + Impact: None. + +bnx2 v1.9.36b (Oct 22, 2009) +======================================================= + Enhancements + ------------ + + 1. Change: Expand on NetQueue module parameters. + + Impact: bnx2 VMware NetQ. + +bnx2 v1.9.35b (Oct 22, 2009) +======================================================= + Fixes + ----- + 1. Problem: If only 1 queue is used on VMware ESX, iSCSI + offload would fail. + + Cause: The inboxed VMware driver would only use + 1 MSI-X vector. Because of this the driver + would not setup MSI-X + + Change: Always setup MSI-X reguardless of the + number of MSI-X vectors used. + + Impact: 5709 iSCSI on VMware + +bnx2 v1.9.34b (Oct 20, 2009) +======================================================= + Fixes + ----- + 1. Change: Updated xinan firmware to 5.0.0.j8 + Nop timeout tuning (VMware PR467194) + Added a new data fw heartbeat to work with + the new bootcode and also handle older + bootcode. + + Impact: 5709. + +bnx2 v1.9.33b (Oct 8, 2009) +======================================================= + Enhancements + ------------ + + 1. Change: Make NetQueue module parameters more explict. + + Impact: bnx2 VMware NetQ. + +bnx2 v1.9.32b (Oct 8, 2009) cnic v1.9.7c (Oct 8, 2009) +======================================================= + Fixes + ----- + 1. Problem: CNIC unable to handle an unsolicited + RESET COMPLETE which would result in a hung + session. + + Cause: The CNIC driver couldn't handle unexpected SYN. + + Change: The CNIC driver will now handle an unsolicited + RESET COMPLETE and cleanup properly. + + Impact: cnic + + Enhancements + ------------ + + 1. Change: Allow the default NetQueue options to be + controlled through the macro, + BNX2_ENABLE_NETQUEUE. + + Impact: bnx2 VMware NetQ. + +bnx2 v1.9.31b (Oct 6, 2009) +======================================================= + Fixes + ----- + 1. Problem: PSOD would occur when alloc()'ing NetQueue's + + Cause: With the removal of the global 'disable_netq' + parameters, the NetQueue CID's weren't being + mapped in some cases + + Change: Always map the NetQueue CID's + + Impact: bnx2 VMware ESX NetQueue + +bnx2 v1.9.30b (Oct 5, 2009) +======================================================= + Fixes + ----- + 1. Problem: PSOD would occur because of TX timeouts + + Cause: The VMware netdev watchdog timer was ran before + the reset task got a chance to run on a loaded + system. + + Change: Elongated the TX timeout value for VMware ESX + only to allow for the reset task to run. + + Impact: bnx2 VMware ESX + + + Enhancements + ------------ + + 1. Change: Updated vmkernel module parameter, 'force_netq', + to allow NetQueue setting to be adjusted per + function rather then globally for all functions. + + Impact: bnx2 VMware ESX + + +bnx2 v1.9.29b (Oct 1, 2009) +======================================================= + Fixes + ----- + 1. Change: Updated xinan firmware to 5.0.0.j7 and + teton firmware to 5.0.0.j4 which will fix + L2 hang due to iSCSI PDU's padding byte + extended to another TCP segment + + Impact: 5706/5708/5709. + + +bnx2 v1.9.28b (Sep 25, 2009) +======================================================= + Fixes + ----- + 1. Change: Updated firmware to 5.0.0.j6 which will add + the corresponding flow control trip point. + + Impact: 5709. + + +bnx2 v1.9.27b (Sep 24, 2009) cnic v1.9.7b (Sep 24, 2009) +======================================================= + Fixes + ----- + 1. Problem: For the cnic users would see in the kernel logs: + + "kcq abs(hw_prod(0) - sw_prod(65534)) > + MAX_KCQ_IDX(2047)" + + Cause: The function was not taking into account the + wrapping of the software and hardware producer + indexes. + + Change: Recaculate the difference between the index with + wrapping taken into account. + + Impact: cnic + + + Enhancements + ------------ + + 1. Change: Updated firmware to 5.0.0.j5 which increased the + rxpq depth from 32 to 256 to enhance the performance + on systems without flow control. + + Impact: 5709. + + +bnx2 v1.9.26c (Sep 17, 2009) +======================================================= + Enhancements + ------------ + 1. Change: Cleanup the VMware NetQueue code. Eliminate unneeded + RX flush routine, fix the number of queues to be the + same on 1500 and 9000 MTU sized packets. + + Impact: bnx2 VMware NetQ. + +bnx2 v1.9.26b (Sep 14, 2009) +======================================================= + Fixes + ----- + 1. Problem: VMware PSOD on MTU change + + Cause: MSI-X window was remapped to the beginning of the PCI + config space. The driver would disable the bar space + because the MSI-X window was remapped incorrectly. This + would eventually cause a PSOD. + + Change: Before the chip reset, the driver would disable MSI-X, + after the chip reset, MSI-X is re-enabled. + + Impact: VMware NetQueue + + +bnx2 v1.9.25b (Aug 21, 2009) cnic v1.9.6 (Aug 21, 2009) +======================================================= + Fixes + ----- + 1. Problem: (CQ 43182/43252) Compile error on RHEL4 + + Cause: struct mutex not defined on older kernels. + + Change: Wrap cnic_lock mutex structure and code with #ifdef BCM_CNIC. + + Impact: None. + + 2. Problem: Unloading cnic after ifdown may crash. + + Cause: cnic unregisters with bnx2/bnx2x twice. + + Change: Fixed logic to unregister only once. + + Impact: None. + +bnx2 v1.9.24b (Aug 13, 2009) cnic v1.9.6 (Aug 13, 2009) +======================================================= + Fixes + ----- + 1. Problem: (CQ 42778) Kernel occasionally panics when enabling jumbo + frames. + + Cause: If the driver is unable to allocate memory for the buffers, + the device is not properly shutdown. + + Change: Added logic to shutdown device if unable to allocate buffers + and complete the setup of the device. + + Impact: bnx2, changing MTU and ring sizes. + + Enhancements + ------------ + 1. Change: Updated firmware to 5.0.0.j3 for better small packet rx + performance. Firmware also fixes header digest error during + I/O. + + Impact: All bnx2 devices. + +bnx2 v1.9.23b (July 29, 2009) cnic v1.9.5 (July 29, 2009) +========================================================== + + Fixes + ----- + 1. Problem: (CQ 42723) KCQ not resetting to 0 problem + + Cause: RCU deadlock during shutdown + + Change: Move the bnx2_register_cnic() call out of the + above sequence. Instead, register and + unregister during NETDEV_UP/NETDEV_DOWN events. + + Change all slow path RCU read locks to mutex. + In cnic, we also use reference counting to + protect some slow paths. + + Impact: bnx2 iSCSI, bnx2x iSCSI + + +bnx2 v1.9.22b (July 23, 2009) cnic v1.9.4 (July 23, 2009) +========================================================== + + Enhancements + ------------ + 1. Change: If a TX timeout occurs, there could be the possiblity that the + chip is hung. By printing chip state into the kernel ring buffer, + it will be easier to determine this. + + Impact: bnx2 iSCSI. + + 2. Change: The bnx2 firmware should always be matched with the proper bnx2 + driver. The bnx2 firmware version string are now printed. + It will be easier to identify the running firmware. + + Impact: bnx2 + + 3. Change: When a TX timeout occurs and the chip is reset, the ability to + debug the chip further is lost. This is because the chip state + information is lost during the reset. The users have a choice + to prevent the chip reset. + + Impact: bnx2 + + 4. Change: Add a check to see if there was a KCQ overflow was + in the bnx2x firmware. Now this check has been moved to the + CNIC so that the driver could control the cleanup/recovery. + + Impact: bnx2 iSCSI. + + +bnx2 v1.9.21b (July 16, 2009) +========================================================== + Enhancements + ------------ + 1. Change: Updated firmware to 5.0.0.j2 to fix remote reset problem + + Impact: bnx2 iSCSI. + + +bnx2 v1.9.20b (July 09, 2009), cnic v1.9.3 (July 09, 2009) +========================================================== + Fixes + ----- + 1. Problem: (CQ42534) cnic.c does not compile on older kernels. + + Cause: Some iscsi_proto constants not defined in + + Change: Added the constant definitions if not already defined. + + Impact: None. + + Enhancements + ------------ + 1. Change: Updated firmware to 5.0.0.j to fix TMF releated iSCSI issues. + + Impact: bnx2 iSCSI. + +bnx2 v1.9.19b (July 01, 2009), cnic v1.9.2 (July 01, 2009) +========================================================== + Fixes + ----- + 1. Problem: (CQ42399) 10G iSCSI fails when initiator and target MTUs + don't match. + + Cause: cnic driver sets the MSS without knowing the actual MSS + option and timestamp option during TCP connection. + + Change: Changed driver to set MSS to 0xffff so that firmware can + calculate MSS on its own. + + Impact: bnx2x iSCSI. + + 2. Problem: 1G iSCSI fails when initiator and target MTUs don't + match and timestamp is enabled. + + Cause: Firmware was not adjusting for the 12-byte timestamp option + if MSS was reduced by MSS option. + + Change Updated firmware to 5.0.0.i + + Impact: bnx2 iSCSI. + + 3. Problem: (CQ42353) Unable to disable TCP timestamp on 10G iSCSI. + + Cause: Timestamp was hardcoded to be enabled for 10G. + + Change: Update global timestamp option during each TCP connection. + + Impact: bnx2x iSCSI. + + 4. Problem: (CQ42329) bnx2x MC assert during IPv6 login/logout. + + Cause: EQ filled with more than 128 L4 kcqes without MSI-X interrupt + for 2 seconds. The driver can only buffer 128 L4 kcqes. + + Change: Increased L4 kcqe buffer to 256 entries as a temporary solution. + Need to understand the delayed interrupt issue. + + Impact: None. + +bnx2 v1.9.18b (June 25, 2009), cnic v1.9.1 (June 24, 2009) +========================================================== + Enhancements + ------------ + 1. Change: Added FTQDiscard counter to the list of counters being + reported. During small packet test, there may be FTQ + overflow and this counter is very useful. + + Impact: None. + + 2. Change: Updated firmware to 5.0.0.h to improve small packet L2 + performance and to enhance iSCSI TMF handling. + + Impact: 5706/5708/5709. + +bnx2 v1.9.17b (June 24, 2009), cnic v1.9.1 (June 24, 2009) +========================================== + Fixes + ----- + 1. Problem: Leaking NetQueues if the firmware did not respond in time + + Cause: System might be clogged and there is not enough time to + process the KCQ + + Change: Updated driver/firmware to allow the calling of the free RX + queue request before a RX alloc request is issued + + Impact: 5709 + + 2. Problem: An offloaded iSCSI connection would get stuck + + Cause: Issing an offload request might fail, and this was not handled + properly. + + Change: Updated driver/firmware to return the proper return code and + handle offload connection failures. + + Impact: 5706/5708/5709 + + 3. Problem: tasklet_init() is being called on a task that is scheduled + + Cause: When the CNIC calls register_device(), that task might already + be schedled. + + Change: tasklet_disable() is insufficient because the tasklet remains + scheduled and stays on the tasklet list, instead tasklet_kill() + is now used. + + Impact: 5706/5708/5709 + + + +bnx2 v1.9.16b (June 10, 2009), cnic v1.9.0 (June 10, 2009) +========================================================== + Enhancements + ------------ + 1. Change: (CQ38228) Use symbol_get() for ipv6 symbols so that the + cnic driver will load without necessarily loading ipv6 + modules. + + Impact: iSCSI over IPv6. + + 2. Change: Updated 5706/08/09 MIPS firmware to 5.0.0.g. + + Impact: 5706/08/09. + + 3. Change: Backported some dma_mapping_error() checking. + + Impact: None. + +bnx2 v1.9.15b (June 1, 2009), cnic v1.8.6 (June 1, 2009) +======================================================== + Enhancements + ------------ + 1. Change: Upstream bnx2 tx path optimizations. Please see ChangeLog. + + Impact: bnx2 tx path. + + 2. Change: In the cnic driver, save the last allocated TCP port during + unregistration so that previously used TCP ports will not + be immediately re-used. + + Impact: iSCSI. + + 3. Change: VMWare TCP port allocation changes. + + Impact: VMWare iSCSI. + +bnx2 v1.9.14b (May 21, 2009) +========================================== + Fixes + ----- + 1. Problem: NOPOUT timeouts on 570x iSCSI connections. + + Cause: Bugs in TCP timestamp implementation. + + Change: Updated MIPS firmware to 5.0.0.f. + + Impact: 5706/5708/5709. + +bnx2 v1.9.13b (May 18, 2009) +========================================== + Fixes + ----- + 1. Problem: Potentially exceeding the NetQueue limit recommendations + by VMware which could stress system resources + + Cause: When system resources are strained. + + Change: There are now limits on the number of NetQueues which the + driver will request: + When the MTU is 1500 the max number of queues per port is 8 + When the MTU is 9000 the max number of queues per port is 4 + + Impact: NetQueue enabled version of VMware ESX + + 2. Problem: Getting conflicting NetQueue and iSCSI completions + + Cause: When MSI-X is not used on a 5709, NetQueue is still enabled + + Change: NetQueue is not disabled is MSI-X is not used. + + Impact: NetQueue enabled version of VMware ESX + + +bnx2 v1.9.12b (May 13, 2009) +========================================== + Fixes + ----- + 1. Problem: Potentially returning a negative number of NetQueues + + Cause: When calculating the number of rings, we use the calculation from + the number of msi-x vectors returned, The number of NetQueues + returned could potentially be negitive if only 1 vector was + returned. This is because the driver was subtracting 2 + instead of subtracting 1 (for the default queue) from the total + number of rings. + + Change: Return the proper number of queues not including the default + queue. + + Impact: NetQueue enabled version of VMware ESX + +bnx2 v1.9.11b (May 12, 2009), cnic v1.8.5 (May 13, 2009) +======================================================== + Fixes + ----- + 1. Problem: TCP Delay ACK not working on bnx2x iSCSI + + Cause: Disabled in cnic driver. + + Change: Fixed csk->tcp_flags definitions and enabled the proper storm + bit for delay ACK. + + Impact: bnx2x iSCSI. + +bnx2 v1.9.11b (May 12, 2009) +========================================== + Fixes + ----- + 1. Problem: iSCSI timestamp TCP option not working. + + Cause: Disabled in firmware. + + Change: Updated to 5.0.0e MIPS firmware. + + Impact: 5709. + +bnx2 v1.9.10b, cnic v1.8.4 (May 11, 2009) +========================================== + Fixes + ----- + 1. Problem: NetQ and iSCSI do not work in Vmware environment. + + Cause: 1. cnic driver is overwriting CP and COM mailbox bit values newly + defined to support multiple kcqs. + + 2. Firmware is completing some iSCSI kcqes on the NetQ's kcq. + + Change: Fixed cnic driver to only set and clear bit 0 of the CP + and COM mailbox. + + Firmware has been updated to 5.0.0d. This firmware also + supports TCP timestamp option for iSCSI connections. + + Impact: 5706/5708/5709 iSCSI, NetQ. + +bnx2 v1.9.9b (May 1, 2009) +========================================== + Fixes + ----- + 1. Problem: There is link, but can't pass traffic on ESXi + + Cause: Bits in the RX context are being trampled during the + renegotiation of the link. + + Change: RX context offset 0x6 should only be initialized once + during init and not be updated during run-time. Offset + 4 contains the BIDX that's mapped to the mailbox. + + Impact: VMware with NetQueue + +bnx2 v1.9.8b, cnic v1.8.2 (April 27, 2009) +========================================== + Enhancements + ------------ + 1. Change: Updated cnic driver to support 1.50.x HSI as well as the older + 1.48.107 HSI. A compile flag will be used to select one or the + other. + + Impact: iSCSI on bnx2x. + + 2. Change: Spec file and Makefile changes detect the proper module + install directory. + + Impact: installation. + +bnx2 v1.9.7b, cnic v1.8.1 (April 22, 2009) +========================================== + Fixes + ----- + 1. Problem: Compile error with redfinition of struct kwqe. + + Cause: Both the CNIC and bnx2 defined this structure. + + Change: Because the bnx2 definition is L2 specific, it was renamed to + struct l2_kwqe. + + Impact: None. + + 2. Problem: Add context memory DMA alignment for 57710. + + Cause: For the 57710 context memory needs to be 16k aligned. + Without this alignment you will see errors when trying to + establish more then 5 iSCSI connections on VMware ESX 4.0 U1. + + Change: Properly aligned the context memory by allocating a bigger + chunk of memory and then finding the proper alignment. + This reduces the number of possible connections in half + but now we can properly establish more then 5 connections + on VMware. + + Impact: iSCSI. + + +bnx2 v1.9.6b (April 17, 2009) +============================= + Enhancements + ------------ + 1. Change: Added NetQueue support for VMware. + + Impact: 5709. + +bnx2 v1.9.5b (April 17, 2009) +============================= + Enhancements + ------------ + 1. Change: Updated to 5.0.0.a firmware with NetQ support and iSCSI max + connection license information. RV2P firmware unchanged: + 4.6.16 for 5706, 4.6.15 for 5709. + + Impact: All chips. + + 2. Change: Set USE_INT_PARAM on 5709. This will switch the chip in + use "interrupt mode" coalescing after MSI/MSI-X. + + Impact: 5709. + + 3. Change: Included 5706 when doing statistics block DMA workaround. + + Impact: 5706. + +bnx2 v1.8.8b (March 12, 2009) +============================= + Enhancements + ------------ + 1. Change: Updated to latest 4.6.17 CP firmware for 5709. + + Impact: iSCSI licensing on 5709. + +bnx2 v1.8.7b, cnic v1.7.8 (March 10, 2009) +========================================== + Enhancements + ------------ + 1. Change: Reverted 5706/5708 firmware to 4.6.16. Low latency interrupt + mode does not work on 5706/5708. + + Impact: None. + + 2. Change: VMWare changes to expose max. number of iSCSI connections in + the cnic_device structure. + + Impact: VMware iSCSI. + + Fixes + ----- + 1. Problem: (CQ39867) Compile error on RHEL5.3 Xen kernel. + + Cause: msi related symbols undefined because CONFIG_PCI_MSI is not + enabled on Xen kernels. + + Change: Added CONFIG_PCI_MSI around the msi related code. + + Impact: None. + + 2. Problem: (CQ39818) iSCSI targets do not reconnect after running self test. + + Cause: Interrupt is disabled by bnx2 before notifying CNIC to clean up + and stop. As a result, bnx2i will not receive interrupts when + closing the connections if sharing INTA/MSI with network. + + Change: Disable interrupt after notifying CNIC to stop. + + Impact: iSCSI. + +bnx2 v1.8.6b, cnic v1.7.7 (March 2, 2009) +========================================= + Enhancements + ------------ + 1. Change: Updated to new 5706 firmware (4.6.17) and 5709 firmware (4.6.16). + New interrupt coalescing mode for improved latency and iSCSI + cache line fix on 5709. + + Impact: All bnx2 chips. + + 2. Change: CNIC iSCSI changes for VMWare. + + Impact: VMWare only. + +bnx2 v1.8.5b, cnic v1.7.6 (Feb 9, 2009) +======================================= + Fixes + ----- + 1. Problem: (CQ39380) bnx2x iSCSI license not read correctly. + + Cause: Using wrong variable to check the license. + + Change: Fixed the logic. + + Impact: bnx2x iSCSI. + + 2. Problem: Driver accesing MMIO registers during VLAN operations when + the device is in D3hot state in older kernels. + + Cause: VLAN registration functions do not check for device state. + + Change: Added if_running check before MMIO access in VLAN registration + functions. + + Impact: VLAN operations on all bnx2 chips. + + Enhancements + ----------- + 1. Problem: Updated 5706/5708 firmware to 4.6.16. + + Impact: 5706/5708. + +bnx2 v1.8.4b, cnic v1.7.5 (Jan 30, 2009) +======================================== + Fixes + ----- + 1. Problem: (CQ39281) Management firmware stops passing traffic when + bnx2 driver is loaded with jumbo frames enabled. + + Cause: Firmware bug. + + Change: Updated to 4.6.15 firmware. + + Impact: 5706/5708/5709. + + 2. Problem: iSCSI does not work on 57711 in single function mode. + + Cause: Driver only allows iSCSI on 57711 if iSCSI is enabled. + + Change: iSCSI config. not required in single function mode. + + Impact: bnx2x iSCSI. + + Enhancements + ------------ + 1. Change: (CQ38576) Add 5709 to bnx2.4 man page. + + Impact: None. + +bnx2 v1.8.3b, cnic v1.7.4 (Jan 23, 2009) +======================================== + Fixes + ----- + 1. Problem: (CQ38610, CQ39000, CQ39015, CQ39071, CQ39133) Various Jumbo + frames related data corruption issues. + + Cause: Bug in RV2P firmware causing jumbo frames to be DMA'ed without + the frame header control structure occasionally. + + Change: New firmware. + + Impact: 5706/5708/5709. + +bnx2 v1.8.2c, cnic v1.7.4 (Dec 30, 2008) +======================================== + Fixes + ----- + 1. Problem: (CQ38955) Xen kernel panic when unloading bnx2 with jumbo MTU. + + Cause: Bug in bnx2_free_rx_mem(). The jumbo page ring memory was + freed incorrectly using the wrong local variable as array + index. + + Change: Fixed bug by using the correct variable as array index. + + Impact: Jumbo frames. + + 2. Problem: (CQ38965) iSCSI connection timeout on bnx2x. + + Cause: During CFC Delete RAMROD completion handling, the wrong (older) + iSCSI connection ID can sometimes be retrieved using the + harware CID. The CID in the mapping table was not cleared + after the CID was freed. + + Fix: Adding clearing of the CID in the data structure after a CID + is freed. + + Impact: bnx2x iSCSI. + + Enhancements + ------------ + 1. Change: (CQ38744) Reduced R2TQ memory usage to match new firmware + interface (firmware 4.8.9). This reduces the amount of + kernel memory substantially for each iSCSI connection on + bnx2x. + + Impact: bnx2x iSCSI. + +bnx2 v1.8.2b, cnic v1.7.3 (Dec 17, 2008) +======================================== + Enhancements + ------------ + 1. Change: Added iSCSI configuration check on 57711E devices before + allowing iSCSI connections. + + Impact: bnx2x iSCSI. + +bnx2 v1.8.2b, cnic v1.7.2 (Nov 25, 2008) +======================================== + Fixes + ----- + 1. Problem: bnx2x iSCSI does not work on functions 2 or above. + + Cause: This was caused by multiple problems: + a) Status block ID specified by bnx2x was wrong. + b) MSI-X vector assigned to CNIC was wrong. + c) New method to ack. status block using HC register was not + implemented. + d) Status block ID in CSTORM status block is always 0, and + as a result, the status block ID used to ack. the status + block was wrong. + + All these problems only affect functions 2 and above. + + Change: All problems fixed in bnx2x and cnic. + + Impact: bnx2x iSCSI. + +bnx2 v1.8.2b, cnic v1.7.1 (Nov 15, 2008) +======================================== + Enhancements + ------------ + 1. Change: Added CNIC support for Multi function bnx2x iSCSI. + + Impact: bnx2x iSCSI. + +bnx2 v1.8.2b, cnic v1.6.2 (Nov 12, 2008) +======================================== + Enhancements + ------------ + 1. Change: Updated to latest 4.6.13 5709 firmware to fix context corruption + with flow control enabled. + + Impact: 5709. + + 2. Change: Added support for 5716S PCI ID. + + Impact: None. + + Fixes + ----- + 1. Problem: (CQ35904) Netdump does not work. + + Cause: Problem introduced by MSI-X multiqueue. IRQ handlers were + changed to take bnx2_napi parameter instead of netdev parameter. + + Change: Fixed by passing proper parameter to IRQ handler from netpoll. + All IRQ handlers are called when using multiple MSI-X vectors. + + Impact: 5709 netpoll. + +bnx2 v1.8.1f, cnic v1.6.1 (Nov 5, 2008) +======================================== + Fixes + ----- + 1. Problem: All TSO packets corrupted on 5706/5708. + + Cause: Firmware bug zeroing the wrong field in IP header. + + Change: Updated to latest 4.6.11 5706/5708 firmware and 4.6.12 5709 + Firmware. + + Impact: 5706/5708/5709. + + 2. Problem: bnx2x chip asserts when trying to make iSCSI connection + to an unknown IP address. + + Cause: When connection fails, the driver sends a command to an + unitialized CID. + + Change: Added logic to only send deletion commands to initialized + CIDs. + + Impact: bnx2x iSCSI. + + Enhancements + ------------ + 1. Change: Added support for one single VLAN to a bnx2x iSCSI interface. + + Impact: bnx2x iSCSI. + +bnx2 v1.8.1e, cnic v1.6.0 (Oct 27, 2008) +======================================== + 1. Change: Updated to latest 5709 firmware to fix iSCSI data integrity + problem. + + Impact: 5709. + +bnx2 v1.8.1d, cnic v1.6.0 (Oct 23, 2008) +======================================== + Enhancements + ------------ + 1. Change: Updated to latest bnx2 firmware to fix iSCSI data integrity + problem. + + Impact: 5706/5708/5709. + + 2. Change: Updated to the latest bnx2x HSI files. + + Impact: bnx2x iSCSI. + +bnx2 v1.8.1c, cnic v1.5.9 (Oct 13, 2008) +======================================== + Fixes + ----- + 1. Problem: (CQ37905) Unable to compile on SLES10 SP2. + + Cause: DIV_ROUND_UP macro not defined in this kernel. + + Change: Added it for compatibility. + + Impact: None. + + Enhancements + ------------ + 1. Change: Updated to latest 5709 firmware that fixes an NCSI problem. + + Impact: 5709. + + 2. Change: Handle TCP reset in the middle of TCP close more appropriately. + + Impact: iSCSI. + + 3. Change: Handle iSCSI license checking in the same way as 5708/5709. + + Impact: iSCSI on bnx2x. + +bnx2 v1.8.1b, cnic v1.5.8 (Oct 13, 2008) +======================================== + Fixes + ----- + 1. Problem: bnx2x iSCSI CIDs may be allocated outside the range of allocated + host memory. + + Cause: CID range bigger than the amount of allocated host context + memory. + + Change: Changed to match the CID range with memory range. + + Impact: bnx2x iSCSI. + + Enhancements + ------------ + 1. Change: Sync'ed with the latest upstream driver bnx2 1.8.1. + + Impact: None. + + 2. Change: Implemented iSCSI connection license enforcement for bnx2x + devices. + + Impact: bnx2x iSCSI. + +bnx2 v1.8.0j, cnic v1.5.7 (Oct 6, 2008) +======================================== + Fixes + ----- + 1. Problem: (CQ37610) Chip hangs when changing MTU with active iSCSI + connections. + + Cause: Both cnic and bnx2 drivers setting up the chip at the same + time. + + Change: Added check so that the register_cnic function will fail + if the bnx2 driver is in reset. + + Impact: iSCSI. + + 2. Problem: (CQ37762) Able to unload bnx2/bnx2x drivers while there are + active iSCSI connections. + + Cause: CNIC driver does not have a reference count on the BNX2/BNX2X + drivers. + + Change: This became a problem after the register_cnic function was + no longer exported. To solve the problem, the CNIC driver + will now get an explicit reference during register_cnic. + + Impact: iSCSI. + + 3. Problem: (CQ37429) MMIO access while the chip is in D3hot state. + + Cause: The driver does not check if_running state consistently during + ethtool operations. + + Change: Added proper checks to all ethtool and multicast change + functions. + + Impact: ethtool operations and multicast operations. + + Enhancements + ------------ + 1. Change: Simplified ipv4 TSO logic in the driver. This was made + possible with newer 5708 firmware. + + Impact: 5708 TSO and other traffic. + + 2. Change: ethtool -p now blinks forever. This is the standard behavior + in most other net drivers. + + Impact: ethtool -p. + +bnx2 v1.8.0i, cnic v1.5.5 (Sep 30, 2008) +======================================== + Fixes + ----- + 1. Problem: (CQ36787) iSCSI login/logout commands fail on 5709 after a + while. + + Cause: Not handling the KWQ completion properly on 5709 in INTA/MSI + mode. + + Change: Changed to handle KWQ completions by checking the + cmd_consumer_index instead of the rx_quick_index15. + + Impact: 5709 ISCSI in INTA/MSI mode. + + 2. Problem: (CQ37497) CNIC driver does not compile on powerpc. + + Cause: csum_ipv6_magic() not defined. + + Change: Added #include + + Impact: None. + +v1.8.0g (Sep 24, 2008) +======================== + Fixes + ----- + 1. Problem: (CQ37511) System locks up when loading cnic driver on 2.6.16 + kernel. + + Cause: symbol_put_addr() in 2.6.16 tries to acquire the same lock twice. + + Change: Workaround by using __symbol_put() which does not have the same + problem. + + Impact: None. + +v1.8.0f (Sep 23, 2008) +======================== + Fixes + ----- + 1. Problem: (CQ37405) Kernel panic when performing ifup/ifdown with iSCSI + connections on bnx2x devices. + + Cause: Sending searcher delete to bnx2x devices when connection was + not established (due to Spanning Tree or other reasons). + + Change: Check PG offload state before sending searcher delete. + + Impact: iSCSI on bnx2x. + +v1.8.0e (Sep 22, 2008) +======================== + Enhancement + ----------- + 1. Change: Updated to latest driver firmware 4.6.6 for 5709. + + Impact: 5709. + + 2. Change: Updated cnic so that it will not crash even if using older + bnx2/bnx2x drivers without CNIC support. The net driver + can also be loaded after cnic and it will still work without + crashing. + + Impact: iSCSI. + + 3. Change: (CQ 37381) Updated driver to check Vaux PRESET before allowing + WoL. This is needed to support 5709 quad-port boards. + + Impact: WoL on all devices. + +v1.8.0d (Sep 15, 2008) +======================== + Enhancements + ------------ + 1. Change: Updated CNIC iSCSI setup for bnx2x devices. + + Impact: iSCSI on bnx2x devices. + +v1.8.0c (Aug 25, 2008) +======================== + Fixes + ----- + 1. Problem: Does not compile on older kernels. + + Cause: "__maybe_unused" undefined on older kernels. "num_online_cpus()" + undefined on 2.4 kernels. + + Change: Added compatibility code to workaround. + + Impact: None. + +v1.8.0b (Aug 25, 2008) +======================== + Enhancement + ----------- + 1. Change: Updated to latest driver firmware 4.6.2 for 5709. + + Impact: 5709. + +v1.7.9c (Aug 5, 2008) +======================== + Fixes + ----- + 1. Problem: bnx2 does not compile on older kernels. + + Cause: is missing on 2.6.19 and older kernels. + + Change: Added compatibility code to check for kernel version before + including the file. + + Impact: none. + +v1.7.9b (Aug 5, 2008) +======================== + Enhancements + ------------ + 1. Change: Added multi-tx ring for 5709. This fetaure is available on + 2.6.27 kernel when using MSI-X. + + Impact: 5709 + + 2. Change: Added support for 57710 iSCSI in the CNIC driver. + + Impact: iSCSI. + +v1.7.7c (July 14, 2008) +======================== + Fixes + ----- + 1. Problem: Source RPM fails to build. + + Cause: New file missing in tarball. + + Change: Added missing file. + + Impact: None. + +v1.7.7b (July 9, 2008) +======================== + Enhancements + ------------ + 1. Change: Added probe function so that the CNIC driver can get the + pci_device pointer earlier before registration. + + Impact: iSCSI. + + 2. Change: Added support for 5716. + + Impact: None. + +v1.7.6c (June 6, 2008) +======================== + Enhancements + ------------ + 1. Change: Added multi-ring RX support on 5709 using MSI-X. This feature + is only available on 2.6.24 and newer kernels running on SMP + machines. + + Impact: 5709. + + 2. Change: Added secondary unicast address support. We now support up + to 5 unicast addresses. This feature is only available on + 2.6.23 and newer kernels. + + Impact: All chips. + +v1.7.6b.1 (Aug 14, 2008) +======================== + Fixes + ----- + 1. Problem: (CQ36808) VLAN tagged packets received by host stack without + the VLAN tag. + + Cause: If no VLANs are set up using vconfig, and if the driver + configures the chip to strip the VLAN tag, the problem can + happen. With IPMI or UMP enabled, the driver is likely to + configure the chip to strip the VLAN tag. + + Change: Modified the driver to re-insert the VLAN tag when necessary. + I.e. when IPMI/UMP is enabled and vconfig has not setup any + VLANs. + + Impact: All chips receiving VLAN traffic. + +v1.7.6b (May 29, 2008) +======================== + Fixes + ----- + 1. Problem: IPv4 TSO generates bad packet when the TSO packet size + is 1-MSS. + + Cause: Probably firmware does not expect this boundary condition. + + Change: Detect this 1-MSS condition in driver and skip the TSO logic. + 1-MSS TSO packet will be treated like a regular TX checksum + offload packet. + + Impact: TSO on all chips. + + 2. Problem: Cannot disable VLAN stripping even when management firmware + is disabled. + + Cause: The new logic to check firmware VLAN stripping capability + was buggy. + + Change: Fixed the code so that VLAN stripping can always be disabled + if management firmware is disabled. + + Impact: VLAN/management firmware. + +v1.7.5d (May 13, 2008) +======================== + Enhancements + ------------ + 1. Change: Incorporated latest firmware 4.4.18. Bug fix is in + RV2P. + + Impact: All chips. + +v1.7.5c (May 7, 2008) +======================== + Enhancements + ------------ + 1. Change: Incorporated latest firmware 4.4.17. Bug fix is in + RV2P. + + Impact: All chips. + +v1.7.5b (May 5, 2008) +======================== + Fixes + ----- + 1. Problem: (CQ35080) Hard lock when running ethtool -s on the device + that is in the down state. + + Cause: I/O to the device to set link speed in D3-hot state can cause + lockup because the chip does not respond to I/O. + + Change: ethtool -s will just store the new settings without configuring + the new speed. When the device is later brought up, the new + speed will be set as part of chip init. + + Impact: All chips. + + 2. Problem: (CQ35075 CQ35042) Various compile errors on SuSE SLES 9 and SLES + 10 distro kernels. + + Cause: ip_hdr() and __le32/__le32 backported to the distro kernels. + + Change: Adjusted compatibility defines. + + Impact: None. + + Enhancements + ------------ + 1. Change: Incorporated latest 5709 firmware 4.4.16. Timer context + scanning is reduced with this firmware to 500us. With this, + the temporary enhancement in 1.7.4c #2 has been removed. + + Impact: 5709 iSCSI. + +v1.7.4e (April 26, 2008) +======================== + Fixes + ----- + 1. Problem: (CQ34841) 5709 does not link up on some remote PHY systems. + + Cause: No initial link interrupt. + + Change: Added code to poll for link when remote PHY is present. + + Impact: Remote PHY systems. + + 2. Problem: (CQ34767) Kernel panic on 5709 when using jumbo frames + with NCSI traffic. + + Cause: RV2P bug causing RX packets to overwrite kernel memory. + + Change: Updated to latest RV2P code for 5709 and 5708. + + Impact: 5706/5708/5709. + +v1.7.4d (April 17, 2008) +======================== + Fixes + ----- + 1. Problem: IPv6 iSCSI does not work. + + Cause: Added code in the last version 1.7.3c to check for CONFIG_IPV6 + before enabling the IPv6 CNIC support. This failed to check + for CONFIG_IPV6_MODULE if IPv6 is configured as module in the + kernel configuration. + + Change: Changed the checking to CONFIG_IPV6 or CONFIG_IPV6_MODULE. + + Impact: None. + +v1.7.4c (April 16, 2008) +======================== + Fixes + ----- + 1. Problem: (CQ34814, CQ33984) Driver does not compile on 2.4 kernels. + + Cause: __le32 and __be32 not defined in 2.4 kernels. + + Change: Defined these as u32 on 2.4 kernels. + + Impact: 2.4 kernels. + + Enhancements + ------------ + 1. Change: Incorporated latest 5709 4.4.12 firmware to fix various iSCSI + problems. + + Impact: 5709. + + 2. Change: Slowed down the timer block reload time. It was noticed that + after the CNIC driver was unloaded, the timer context memory + was reloaded in a tight loop by the chip. This issue needs + to be properly addressed in the firmware at a future date. + + Impact: iSCSI. + +v1.7.4b (April 11, 2008) +======================= + Enhancements + ------------ + 1. Change: Incorporated latest 5709 4.4.11 firmware to fix various iSCSI + problems. + + Impact: 5709. + +v1.7.3k (April 9, 2008) +======================= + Enhancements + ------------ + 1. Change: Incorporated latest 5709 4.4.10 firmware to fix various iSCSI + problems. CID ranges are now read directly from shared memory. + + Impact: 5709. + +v1.7.3j (April 7, 2008) +======================= + Fixes + ----- + 1. Problem: (CQ33967) Ethtool register test fails. + + Cause: Register 0x1004 context status register should not be tested. + + Change: Removed 0x1004 from register test. + + Impact: None. + + Enhancements + ------------ + 1. Change: Incorporated latest 5709 4.4.9 firmware to fix various iSCSI + problems. + + Impact: 5709. + +v1.7.3i (April 4, 2008) +======================= + Fixes + ----- + 1. Problem: (CQ33948) iSCSI over IPv6 does not work on SLES10. + + Cause: The CNIC driver does not handle IPv6 on older kernels without + NETEVENTS support. + + Change: Modified the logic to support older kernels. + + Impact: All iSCSI connections using IPv4 or IPv6. + + 2. Problem: (CQ34315) Scanning iSCSI disks during max sessions test causes + connection problems. + + Cause: 5709 context memeory allocated by CNIC driver does not match + the iSCSI and PG CID range used by the firmware. + + Change: Changed to the matching CID range. + + Impact: 5709 iSCSI. + +v1.7.3h (March 28, 2008) +======================= + Fixes + ----- + 1. Problem: (CQ33984, CQ34210, CQ34310, CQ34597) Various compiler warnings + and errors on RHEL, SuSE, and 2.4 kernels. + + Cause: New features not compatible with older kernels, or older + vendor kernels backported with newer features. + + Change: Fine-tuned compatibility compiler directives. + + Impact: All kernels. + +v1.7.3g (March 10, 2008) +======================= + Enhancements + ------------ + 1. Change: Updated to latest 5709 firmware version 4.4.7. + + Impact: 5709. + +v1.7.3f (March 10, 2008) +======================= + Fixes + ----- + 1. Problem: CNIC takes a long time to resolve ARP, causing iSCSI to fail. + + Cause: NETEVENT_NEIGH_UPDATE event notifier sometimes takes a long time + to be called. + + Change: This is under investigation. For now, the workaround is to + use the existing neighbour entry if it is in NUD_VALID state. + Previously, we would only use it if it was in NUD_CONNECTED + state. + + Impact: iSCSI neighbour resolution. + + 2. Problem: iSCSI sometimes fails to initialize. + + Cause: The KWQ is in full condition and prevents any KWQE to be sent + to the firmware. This is caused by the KWQ consumer index in + the status block not initializing to 0. + + Change: Added workaround to skip the KWQ full condition check when + sending the first KWQE. This should be properly fixed in + firmware. + + Impact: None. + +v1.7.3d (February 15, 2008) +============================ + Fixes + ----- + 1. Problem: System hangs with iSCSI traffic. + + Cause: Infinite loop in NAPI poll. + + Change: Fixed the problem of not comparing the CNIC tag properly when + determining whether there is more work in NAPI poll. + + Impact: None. + + 2. Problem: iSCSI does not work on 5709 + + Cause: Not initializing 5709 properly for iSCSI. The chip_id field was + not propagated to the CNIC driver. + + Change: Fixed the chip_id field. + + Impact: None. + +v1.7.3c (February 09, 2008) +============================ + Enhancements + ------------ + 1. Change: Updated 5708 and 5709 firmware to version 4.4.2. + + Impact: All devices. + + 2. Change: Updated RV2P firmware to the latest. This may fix the jumbo + frame issue. + + Impact: All devices, especially jumbo frames on 5709 Bx. + + 3. Change: Modified CNIC interface in preparation for 57710 devices. + + Impact: iSCSI. + +v1.7.2f (February 04, 2008) +============================ + Fixes + ----- + 1. Problem: Temporary workaround for one prototype 5709S mezz. card not + working. + + Cause: The driver was looking at the wrong shared memory location to + identify the card. + + Change: Fixed the code to check the correct BNX2_SHARED_HW_CFG_CONFIG + location. + + Impact: 5709S. + +v1.7.2e (January 31, 2008) +============================ + Fixes + ----- + 1. Problem: (CQ33704) CNIC driver fails MSI-X mode on 5709. + + Cause: When implementing the fix, the wrong constant was used + for the CNIC MSI-X vector. + + Change: Corrected to use the correct constant BNX2_CNIC_VEC. + + Impact: None. + + 2. Problem: iSCSI outgoing packets over IPv6 are bigger than 1518 bytes. + + Cause: MSS calculation was based on IPv4 and did not accout for the + IPv6 header size. + + Change: Adjusted MSS calculation for IPv6. + + Impact: None. + +v1.7.2c (January 30, 2008) +============================ + Fixes + ----- + 1. Problem: CNIC driver does not work in MSI-X mode on 5709. + + Cause: This is a regression caused by moving the CNIC MSI-X + vector from 8 to 2. The context memory did not get + updated with the new vector. + + Change: Changed the definition. All bits that depend on the + MSI-X vector will now have the proper definitions automatically. + + Impact: None. + +v1.7.2b (January 30, 2008) +============================ + Enhancements + ------------ + 1. Change: Updated 5709 firmware to 4.4.1. + + Impact: 5709. + + 2. Change: Added temporary workaround for one prototype 5709S mezz. card. + The workaround will be removed once newer cards are available. + + Impact: 5709S. + +v1.7.1e (January 14, 2008) +============================ + Enhancements + ------------ + 1. Change: Updated 5709 firmware to 4.1.1 for IPv6 iSCSI support. + + Impact: 5709. + + 2. Change: Updated 5708 RXP firmware to 4.1.1. + + Impact: 5706/5708. + + 3. Change: Updated RV2P firmware for 5709. + + Impact: 5709. + + 4. Change: Added IPv6 support to CNIC driver. + + Impact: New BNX2I driver must be used with this new CNIC driver. + +v1.7.1d.3 (February 7, 2008) +============================ + Fixes + ----- + 1. Problem: 5706S continues to not link down occasionally on some blade + systems. + + Cause: In some cases, even turning off the tx current does not + cause the link to go down. + + Change: Link transition can be triggered based on the SYNC_FAILED bit + in the AN_DEBUG register when all else fails. + + Impact: 5706S. + +v1.7.1d.1 (February 1, 2008) +============================ + Fixes + ----- + 1. Problem: 5706S still not linking down occasionally on some blade + systems when the switch blade is disconnected. + + Cause: Occasionally, all the bits that we check will falsely + indicate link up and trigger the parallel detect logic on + this blade platform. Once in parallel detect mode with + autoneg disabled, the driver will never detect link down. + + Change: Disabled parallel detect on this platform by checking + PCI sub IDs to identify the device. + + Impact: Parallel detect not supported on this vendor device. + +v1.7.1d (January 25, 2008) +============================ + Fixes + ----- + 1. Problem: 5706S still not linking down on some blade systems when the + switch blade is disconnected. + + Cause: This is the same problem that was fixed in 1.7.0b. After + switch module is removed, the SYNC_FAILED bit sometimes + toggles low, and this causes the parallel detect logic in the + driver to trigger and disable autoneg. Disabling autoneg + causes false link to come up immediately. + + Change: It seems that the RUDI_INVALID bit can be used to prevent the + above from happening. Modified parallel detect code to + additionally check the RUDI_INVALID bit and trigger only when + the bit is cleared. + + Impact: Parallel detect logic in 5706S. + +v1.7.1c (December 27, 2007) +============================ + Fixes + ----- + 1. Problem: Driver crashes when adding VLAN and bonding. + + Cause: Bonding calls driver's VLAN function in interrupt context with lock + held. We recently added a VLAN feature in 1.6.8b which requires + chip reset when VLANs are added or removed. The reset will call + vmalloc/vfree which must not be called in interrupt context. + + Change: Reverted the VLAN feature for now. Will workaround it in the + future. + + Impact: 5709 only. This new VLAN feature is no longer available. + +v1.7.1b (December 24, 2007) +============================ + Fixes + ----- + 1. Problem: Driver crashes when using jumbo frames on 5709 A1. + + Cause: RV2P firmware problem. + + Change: Disabled jumbo page ring on 5709 Ax chips. + + Impact: Jumbo receive page rings not supported on 5709 Ax. + + 2. Problem: CNIC driver prints "Failed waiting for ref count to go to zero" + when unloading the driver. This is a minor cosmetic issue. + + Cause: Minor bug in driver comparing ref_count to 1 instead of zero + during exit. + + Change: Fixed the code to check for ref_count == 0. + + Impact: None. + +v1.7.0b (December 12, 2007) +============================ + Fixes + ----- + 1. Problem: (CQ33032) CPU utilization increases dramatically when TSO + is enabled. + + Cause: Driver is spending a lot of unnecessary time in TX handling + code when TSO is enabled. This regression was introduced when + the TX handling code was changed to match the RX handling code. + The RX handling code was changed to fix a packet rot problem. + When TSO is enabled, the tx index can stop at non-packet + boundaries and the code will keep waiting until it gets to the + next packet boundary. + + Change: Fixed it to handle this condition properly. + + Impact: None. + + 2. Problem: 5706S does not link down in some blade systems. + + Cause: Hardware problem. + + Change: Added workaround logic to check periodically and force a + link down when the PHY reports SYNC status failed. This + only works when autoneg is enabled. + + Impact: 5706S. + +v1.6.8e (December 10, 2007) +============================ + Fixes + ----- + 1. Problem: When using jumbo MTU with large RX ring sizes, system will + crash or the driver won't load. + + Cause: Bug in driver where the page descriptor array was declared + too small. + + Change: Fixed the array size. + + Impact: None. + + 2. Problem: CNIC driver won't compile on kernels without CONFIG_PCI_MSI. + + Cause: Some code needs to be conditionally compiled only when + CONFIG_PCI_MSI is set. + + Change: Added #ifdef CONFIG_PCI_MSI where necessary. + + Impact: None. + + 3. Change: Disabled iSCSI on 5709 until next release. + + Impact: None. + +v1.6.8d (December 07, 2007) +============================ + Change: Reverted the 5706S workaround made in 1.6.8c because it was + unreliable. + + Impact: None. + +v1.6.8c (December 07, 2007) +============================ + Fixes + ----- + 1. Problem: (CQ32794) Kernel panic (skb_over_panic) with jumbo frames. + + Cause: RV2P firmware incorrectly placed jumbo data into SKB. + + Change: New RV2P firmware. + + Impact: Jumbo frames on all hardware. + + 2. Problem: (CQ32817) ethtool loopback test fails. + 3. Problem: (CQ32842) Chariot test fails. + + Cause: RX buffer size too small to contain MTU-sized frame. + This was introduced by the new Jumbo RX logic. + + Change: Fixed RX buffer size logic. + + Impact: Traffic on all hardware. + + 4. Problem: (CQ32862) Fails to compile on RH9u9. + + Cause: Kernel API skb_fill_page_desc() backported to RH kernel + causing duplicate definition. + + Change: Adjusted compatibility code. + + Impact: None. + + 5. Problem: (CQ32866) ISCSI driver fails to compile with 1.6.8b. + + Cause: print_mac() multiply defined when bnx2.h is included + in the bnx2i driver. + + Change: Changed print_mac() to static inline. + + Impact: None. + + 6. Problem: 5706S does not link down in some blade systems. + + Cause: Hardware problem. + + Change: Added workaround logic to check periodically. + + Impact: 5706S. + + Enhancements + ------------ + 1. Change: Added flow control logic on 5709. + + Impact: 5709. + + 2. Change: Updated all firmware to 4.0.5. + + Impact: All hardware. + +v1.6.8b (November 28, 2007) +============================ + Enhancements + ------------ + 1. Change: Added MSIX support for 5709 B0. + + Impact: 5709 B0. + + 2. Change: Added multi-page buffer support for Receive Jumbo frames. + + Impact: All chips. This feature is experimental at this point. + + 3. Change: Allow VLAN tags in receive packets when management firmware + is enabled. + + Impact: 5709 only. This feature is experimental and requires new + boot code and UMP/IPMI code. + + 4. Change: Updated firmware to 4.0.3. + + Impact: All chips. + +v1.6.7b (October 16, 2007) +============================ + Fixes + ----- + 1. Problem: (CQ31861) Remote copper PHY does not link up if WoL is enabled. + + Cause: bnx2_set_power_state() assumes that only copper devices support + WoL and proceeds to configure the copper PHY to 10/100 Mbps. + This assumption is no longer valid after Serdes WoL support + was added in 1.6.3c. + + Change: Fixed the code to take check for Serdes and copper when setting + up the PHY for WoL. + + Impact: WoL. + +v1.6.6b (October 10, 2007) +============================ + 1. Change: Update 5708 firmware to 3.7.19. + +v1.6.5k (October 10, 2007) +============================ + 1. Problem: Will not compile on 2.6.23 kernel. + + Cause: get_perm_addr no longer defined in struct ethtool_ops + + Change: Adjusted kernel compatibility #ifdef. + + Impact: None. + +v1.6.5j (October 10, 2007) +============================ + No change to bnx2. Updated cnic driver to 1.1.24. + +v1.6.5i (October 8, 2007) +============================ + Enhancements + ------------ + 1. Change: Update 5708 firmware to 3.7.18. + +v1.6.5h (October 4, 2007) +============================ + Enhancements + ------------ + 1. Change: Update 5708 firmware to 3.7.17. + +v1.6.5g (October 1, 2007) +============================ + Enhancements + ------------ + 1. Change: Update 5708 firmware to 3.7.16. + +v1.6.5f (September 30, 2007) +============================ + Enhancements + ------------ + 1. Change: Update 5708 firmware to newer 3.7.15. + +v1.6.5e (September 29, 2007) +============================ + Enhancements + ------------ + 1. Change: Update 5708 firmware to 3.7.15. + +v1.6.5d (September 28, 2007) +============================ + Enhancements + ------------ + 1. Change: Combined RPM package and tar file with CNIC driver. + +v1.6.5c (September 26, 2007) +============================ + Enhancements + ------------ + 1. Change: Update 5708 firmware to 3.7.14. + +v1.6.5b (September 25, 2007) +============================ + Fixes + ----- + 1. Problem: (CQ30086) 5709 A1 does not link up when connected to + some Broadcom devices. + + Cause: Early DAC wakeup workaround needed for 5709 A1. + + Change: Added workaround. + + Impact: None. + + Enhancements + ------------ + 1. Change: Added CNIC support for iSCSI. + + Impact: Only enabled on 2.6.16 and newer kernels. + +v1.6.3c (July 23, 2007) +======================= + Fixes + ----- + 1. Problem: (CQ30591) ethtool -t fails on remote PHY systems. + + Cause: Link test and loopback test were not modified to support + remote PHY. + + Change: Added remote PHY support to link test and changed the code + to skip PHY loopback on remote PHY since it's not supported. + + Impact: None. + + 2. Problem: (CQ30625) ethtool speed settings are lost after ifdown. + + Cause: The remote PHY settings stored in firmware is not the same + as the driver's last setting. + + Change: Modified the code to keep the driver's setting unless there's + is change in the remote PHY media. + + Impact: ethtool -s on remote PHY systems. + + Enhancements + ------------ + 1. Change: Change ethtool's wol setting to NVRAM's default and + add support for Serdes WoL if NVRAM supports it. + + Impact: ethtool wol's default setting. + +v1.6.3b (July 13, 2007) +======================= + 1. Problem: Problems reading and writing flash on 5709. + + Cause: Hardware changes in 5709. + + Change: Rearranged current flash code to properly support 5709. + + Impact: Changes will impact flash operations on 5706/5708/5709 devices. + + 2. Problem: (CQ30516) Management firmware version string disappears + on 2nd modprobe of the driver. + + Cause: The 2nd modprobe will program the device back to D0 from D3hot. + This causes a reset and it will take some time before the + management firmware information will be available. + + Change: Added a proper wait loop to wait for the firmware information + to be deposited in shared memory. + + Impact: None. + +v1.6.2f (July 3, 2007) +======================= + 1. Problem: The fix for CQ29488 in the previous version is problematic + and needs to be reverted. + + Cause: Adding the netif_carrier_off() call before register_netdev() + in bnx2_init_one() can cause problems if register_netdev() + fails. netif_carrier_off() can queue a link watch event + that can run later. If register_netdev() fails, the netdev + structure will be freed and the link watch event may panic + when it is scheduled to run. + + Change: Moving the netif_carrier_off() to after register_netdev() + also will not work as it can cause race conditions with + the interrupt handler. So the best thing to do is to + revert the earlier fix and not address the CQ29488 issue. + + Impact: CQ29488 is not fixed. + +v1.6.2e (July 3, 2007) +======================= + Fixes: + ------ + 1. Problem: (CQ29528) Link does not come up using remote PHY (continued). + + Cause: ifup script may be using mii-tool to obtain link status, + and interfering with remote PHY firmware. + + Change: Block mii-tool ioctl access when remote PHY firmware is present. + + Impact: None. + + 2. Problem: (CQ29488) Driver reports link up in ethtool before the interface + is brought up. + + Cause: The netdevice structure's initial link setting is up and that + gets reported directly by ethtool. + + Change: Added netif_carrier_off() call in bnx2_init_one(). + + Impact: None. + +v1.6.2d (June 22, 2007) +======================= + Fixes: + ------ + 1. Problem: (CQ29528) Link does not come up using remote PHY. + + Cause: The fix in 1.6.2b still has a problem. The copper or Serdes + default link setting does not follow the correct port when + the port is changed during chip reset. + + Change: Fix the code to always refresh the port and link setting + during chip reset. + + Impact: None. + + 2. Problem: (CQ30181) Cannot compile on 2.4 kernels. + + Cause: USEC_TO_SEC constant is not defined on older kernels. + + Change: Added conditional #define for this constant on older kernels. + + Impact: None. + + Enhancements: + ------------- + 1. Change: Update 5709 firmware to 3.7.1. + + Impact: 5709 devices. + +v1.6.2b (June 21, 2007) +======================= + Enhancements: + ------------- + 1. Change: Added management firmware version to ethtool -i. + + Impact: None. + + Fixes: + ------ + 1. Problem: (CQ29528) Link does not come up using remote PHY. + + Cause: The driver is using the wrong port when setting up the remote + PHY link. + + Change: Fixed bug to always use the correct requested port or + default port when setting remote PHY link. + + Impact: None. + + 2. Problem: (CQ30028) Advertised Autoneg not set in ethtool. + + Cause: Remote PHY code is not setting the flag which is for + information only. + + Change: Added code the set the flag for consistency. + + Impact: None. + +v1.6.1c (May 22, 2007) +===================== + Enhancements: + ------------- + 1. Change: (CQ28907) Added support for setting copper and fibre speeds. + + Impact: Only available on Remote PHY systems. + +v1.6.1b (May 7, 2007) +===================== + Fixes: + ------ + 1. Problem: (CQ29301) Setting invalid speed setting causes driver to + crash. + + Cause: Lock taken without unlock when exiting iwith error the + procedure to set speed. + + Change: Fixed the code with proper unlock. + + Impact: None. + +v1.6.0b (April 30, 2007) +======================== + Enhancements: + ------------- + 1. Change: Improved handshake with remote PHY firmware. + + Impact: None. + +v1.5.10c (May 4 , 2007) +======================= + Fixes: + ------ + 1. Problem: Extremely low TSO performance when destination has a + smaller MSS than the source. + + Cause: Packets smaller than MTU are not processed as TSO packets. + + Change: Removed the SKB length check when doing TSO. + + Impact: None. + +v1.5.10b (May 1, 2007) +========================= + Enhancements: + ------------- + 1. Change: Removed Remote PHY until next release. + + Impact: No Remote PHY support. + + 2. Change: Added check for 2.5G capability before enabling 2.5G on + 5709S. + + Impact: None. + + Fixes: + ------ + 1. Problem: (CQ28438) NETDEV WATCHDOG after OS boot. + + Cause: The mailbox queue is halted after the OS reads config. + register 0x94. + + Change: Set MQ_CONFIG_HALT_DIS in register MQ_CONFIG on 5709 A0 and + A1. + + Impact: None. + +v1.5.9b (April 20, 2007) +======================= + Enhancements: + ------------- + 1. Change: Enhanced remote PHY support for 5708S. + + Impact: None. + + Fixes: + ------ + 1. Problem: (CQ29056) Driver does not compile on SLES 10 SP1 kernel. + + Cause: Backported netif_tx_lock() causes compatibility problem. + + Change: Adjuested compatibility #ifdef. + + Impact: None. + + 2. Problem: (CQ28880) Driver fails register test after running traffic. + + Cause: A reserved register 0x500c was mistakenly included in the test. + + Change: Removed this register from the test. + + Impact: None. + +v1.5.8b (March 13, 2007) +======================= + Enhancements: + ------------- + 1. Change: Added remote PHY support for 5708S. + + Impact: None. + +v1.5.7b (March 2, 2007) +======================= + Enhancements: + ------------- + 1. Change: Added IPV6 TSO for 5709. + + Impact: None. + + 2. Change: Added 2.5G parallel detection for 5709s. + + Impact: None. + +v1.5.6b (February 28, 2007) +=========================== + Enhancements: + ------------- + 1. Change: Added Serdes support for 5709. + + Impact: None. + + Fixes: + ------ + 1. Problem: (CQ28433) Driver does not compile on some early 2.6 and + 2.4 kernels. + + Cause: Kernel compatibility issue. + + Change: Added some missing #ifdef CONFIG_PCI_MSI. + + Impact: None. + + 2. Problem: (CQ28527) System hangs when running bonding scripts on 5709. + + Cause: Bonding driver reads MDIO registers after the device is in + D3hot state. + + Change: Added check to disallow MDIO access after the device is down. + + Impact: None. + +v1.5.5b (January 31, 2007) +=========================== + Fixes: + ------ + 1. Problem: (CQ28252) Driver reports the same MAC address for both + ports of 5709. + + Cause: The driver was obtaining the MAC address from the same + shared memory area for both ports. + + Change: Updated driver to read from the deicated shared memory + area for each port. + + Impact: None. + + 2. Problem: (CQ28226) 5709C does not link up when connected to Dell + 5324 and some other switches. + + Cause: PHY's Early DAC Wakeup Enable bit's strapped value is set. + + Change: Disable Early DAC Wakeup enable on 5709 A0 after PHY reset. + + Impact: None. + +v1.5.4b (January 8, 2006) +=========================== + Fixes: + ------ + 1. Problem: 5709 copper device detected as serdes. + + Cause: Media detection code not complete. + + Change: Added complete media detection code. + + Impact: None. + + Enhancement: + ------------ + 1. Change: Add IPv6 checksum support. + + Impact: None. + +v1.5.2b (December 22, 2006) +=========================== + Merged in all latest changes. + +v1.5.1c (November 30, 2006) +=========================== + Enhancement: + ------------ + 1. Change: Remove 5709 FPGA code. + + Impact: None. + +v1.5.1b (November 15, 2006) +=========================== + Enhancement: + ------------ + 1. Change: Added support for 5709. + + Impact: None. + +v1.4.52c (March 07, 2007) +========================= + Enhancement: + ------------ + 1. Change: Increase maximum receive ring size to 4080. + The default is kept the same at 255. + + Impact: None. + +v1.4.51b (December 21, 2006) +============================ + Fixes: + ------ + 1. Problem: (CQ27802) Cannot enable flow control in some cases. + + Cause: In some cases when flow control is forced to on, the + driver detects no changes in advertisement, and as a + result, will not re-autoneg. Without the link change, + flow control settings will not change. + + Change: Added some code to setup flow control when no other + PHY settings are made. This will ensure that flow + control changes will take effect immediately. + + Impact: None. + +v1.4.50b (December 15, 2006) +============================ + Fixes: + ------ + 1. Problem: (CQ27424) Panic when writing to nvram. + + Cause: Bug in driver when an alignment buffer is used + + Change: Fixed the bug so that the alignment buffer is freed with the + correct pointer. + + Impact: None. + +v1.4.49b (December 13, 2006) +============================ + Fixes: + ------ + 1. Problem: (CQ27459) Driver reports link down when link is up. + + Cause: Hotplug scripts can cause bnx2_open() to be called immediately + after bnx2_init_one() calls register_netdev(). The initial + link state set after register_netdev() may overwrite the actual + link state set by the link handler on SMP systems. + + Change: Move all initial state setup code to before calling + register_netdev(). + + Impact : None. + +v1.4.48b (December 08, 2006) +============================ + Fixes: + ------ + 1. Problem: (RHBZ 212055) Panic in bnx2_poll(). + + Cause: Bug in determining tx ring space when the tx ring is + completely full. + + Change: Fixed the flawed logic in bnx2_tx_avail(). + + Impact: None. + + 2. Problem: (CQ27424) Buffer overflow when writing to nvram. + + Cause: Bug in driver when the beginning NVRAM offset is + not 4-byte aligned. + + Change: Fixed the bug to allocate enough buffer space. + + Impact: None. + + 3. Problem: (CQ27543) Loopback failed if MAC address is changed. + + Cause: Driver uses permanent MAC address for loopback which may + be different from a locally administered address. + + Change: Fixed by alwaysing using the current MAC address in the + netdevice structure. + + Impact: None. + +v1.4.47b (November 02, 2006) +=========================== + Fixes: + ------ + 1. Problem: (CQ25922) 5708S does not pass PHY loopback test on + some blade servers. + + Cause: It takes longer (about 100msec) to link up in some + backplane environments when the Serdes is set to PHY + loopback mode. + + Change: Increase delay loop to up to 1 sec when waiting for + link up after PHY loopback. Previous wait time was + 100usec. + + Impact: None. + +v1.4.46b (October 30, 2006) +=========================== + Fixes: + ------ + 1. Problem: (CQ25916) Device does not receive packets when configured + by the Xen bridge script and when IPMI is enabled. + + Cause: Firmware does not forward packets to the host when the + device is in promiscuous mode in some cases. + + Change: Firmware change and driver change to enable the + BNX2_RPM_SORT_USER0_PROM_VLAN bit when going into + promiscuous mode. + + Impact: None. + +v1.4.45b (October 19, 2006) +=========================== + Enhancement: + ------------ + 1. Change: Added software parallel detection on 5708S to link at + 1000Mbps or 2500Mbps automatically if the link partner is + forced at 2500Mbps. + + Impact: None. + + 2. Change: Added 2500Mbps forced speed setting. Setting it requires + a future version of ethtool or custom apps. + + Impact: None. + + 3. Change: Port driver to compile on 2.6.19 kernel. + + Impact: None. + + Fixes: + ------ + 1. Problem: Driver does not complile on 2.4 kernels. + + Cause: TSO symbols not defined in 2.4 kernels. + + Change: Added #ifdef for compatibility. + + Impact: None. + + 2. Problem: MSI doesn't work on 5706 when the host bridge is AMD8132. + + Cause: Compatibility issue. + + Change: Added code to disable MSI when 5706 is on a systems with + AMD8132. + + Impact: 5706 cannot use MSI on such systems. + +v1.4.44b (August 10, 2006) +========================== + Fixes: + ------ + 1. Problem: A rare tx race window exists in the tx path. + + Cause: CPU re-ordering can cause the tx queue to be stopped + forever when the tx ring is full in a very rare + condition. See ChangeLog for more details. + + Change: Fixed the race condition with memory barriers. + See ChangeLog for more details. + + Impact: None. + + Enhancement: + ------------ + 1. Change: Increase default rx ring size to 255 to prevent + packet drops under heavy load. + + Impact: Higher memory usage. + + 2. Change: Port driver to run on 2.6.18 kernel. + + Impact: None. + + +v1.4.43b (June 26, 2006) +======================= + Enhancement: + ------------ + 1. Change: (CQ25508) Use DRV_MSG_CODE_UNLOAD_LNK_DN message on + 5708 B1 and older chips so that the link will turn + off after ifdown. + + Impact: None. + +v1.4.42b (June 2, 2006) +======================= + Fixes: + ------ + 1. Problem: (CQ22585) WoL cannot be enabled on 5708 B2. + + Cause: Hardware limitation in previous 5708 versions required + driver to always disable driver WoL on 5708. + + Change: With the issue fixed in B2, the driver was changed to + allow 5708 B2 and above to support driver WoL. + + Impact: None. + + 2. Problem: (CQ21779) Driver does not report dropped receive packets. + + Cause: Packets dropped by firmware are reported in a scratch pad + location in memory separate from other counters. + + Change: Added code to report this firmware counter. + + Impact: None. + + Enhancements: + ------------- + 1. Change: (CQ24690) Allow driver to be compiled for non-running kernels. + + Impact: None. + +v1.4.41b (May 17, 2006) +======================= + Fixes: + ------ + 1. Problem: Driver writes wrong data to non-buffered flash. + + Cause: Bug in driver causing the unchanged portion of the page + to be corrupted. + + Change: Fixed bug. + + Impact: None. + +v1.4.40b (April 17, 2006) +========================= + Enhancements: + ------------- + 1. Change: Combine 2 small memory allocations into 1. + + Impact: None. + + 2. Change: Separate tx consumer and producer entries into separate + cache lines for better performance. + + Impact: None. + + 3. Change: Compress firmware data to reduce driver size. + + Impact: Driver now requires zib_inflate library which should be + available on most vendor kernels. + +v1.4.39b (Mar. 10, 2006) +======================= + Fixes: + ------ + 1. Problem: (CQ23181) Driver loses track of link changes in some cases. + + Cause: In some cases, the status block gets a link change indication + that contradicts with the MII link status. This is due to + transient link signals during link changes. + + Change: Add coalesce_now at the end of link change interrupt to flush + out the transient behavior. + + Impact: None. + + 2. Problem: 5708 only supports 40-bit DMA addresses. + + Cause: Limitation of the built-in PCIE-to-PCIX bridge. + + Change: Add workaround code to make sure all DMA addresses are + less than 40 bits. + + Impact: Some minor impact on performance on some 64-bits systems. + There should be practically no performance impact on i386 and + x86_64 systems. + + 3. Problem: "Copyright" deprecated in RPM spec file. + + Change: Changed to "License". + + Impact: Should be none on the distributions that we support. + +v1.4.38b (Feb. 10, 2006) +======================= + Fixes: + ------ + 1. Problem: Setting rx ring size to 255 causes memory allocation errors. + + Cause: Multiple bugs that do not handle the full ring properly. + + Change: Fixed all bugs related to full ring handling and changed + maximum ring size from 4080 to 1020 after discussion with + Dave Miller. + + Impact: Maximum rx ring size is now smaller. + + 2. Problem: Compile warnings and panic on IA64. + + Cause: not included. + + Change: Added include . + + Impact: None. + +v1.4.36b (Jan. 19, 2006) +======================= + Version number update. + +v1.3.36 (Jan. 17, 2006) +======================= + Fixes: + ------ + 1. Problem: (CQ22827) bnx2 does not compile on 2.6.6 and earlier + kernels. + + Cause: Introduced bug while fixing CQ22666. + + Change: Fix compatibility code. + + Impact: None. + + 2. Problem: (CQ22823) bnx2 reports extra .5 at the end of firmware + version string. + + Change: Skip the last digit in the firmware version string. + + Impact: None. + + 3. Problem: (CQ22585) WoL still does not work on 5708. + + Cause: New WoL mechanism introduced in 1.3.34 has a flaw. + + Change: Removed the workaround code and disabled WoL support + on 5708. During shutdown and suspend, the driver will + shutdown the device and WoL will be setup by the + bootcode according to the nvram's out-of-box WoL + setting. + + Impact: 5708 will not support driver-initiated (ethtool-intiated) + WoL. + +v1.3.35 (Jan. 17, 2006) +======================= + Fixes: + ------ + 1. Problem: (CQ22666) bnx2 driver does not compile on RHEL3 U7. + + Cause: if_mii() back-ported to RHEL3 U7. + + Change: Adjusted the compatibility code. + + Impact: None. + + 2. Problem: (CQ22718) UMP connection is lost after enabling WoL. + + Cause: FORCE LINK was mistakenly set during WoL setup. + + Change: Do not set FORCE LINK. + + Impact: None. + +v1.3.34 (Jan. 13, 2006) +======================= + Fixes: + ------ + 1. Problem: (CQ22585) WoL does not work on 5708. + + Cause: Internal bridge not in proper PME state. + + Change: Added new firmware message to allow bootcode to help set up + WoL. + + Impact: None. + + Enhancements: + ------------- + 1. Change: Added compile option to include crc32 function for RH2.4 + boot kernels. + + Impact: None. + + 2. Change: Updated TSO firmware to handle ECN. + + Impact: None. + +v1.3.33 (Dec. 12, 2005) +======================= + Enhancements: + ------------- + 1. Change: Increased firmware hand-shake timeout from 50 msec to + 100 msec. Also changed the delay from busy-wait udelay + to non-busy-wait msleep. + + Impact: None. + + 2. Change: Added code to read nvram size from shared memory if + available. Otherwise, the size will come from the + flash table which only gives the smallest size for + that type of flash. + + Impact: None. + + 3. Change: Added workaround to issue 2 separate IOs for interrupt + acknowledgement. The workaround is only used for INTA + (non-MSI) interrupts. + + Impact: Slightly higher overhead for INTA interrupts. + +v1.3.32 (Dec. 01, 2005) +======================= + Enhancements: + ------------- + 1. Change: Added PHY loopback to the loopback selftest. + + Impact: None. + + 2. Change: (CQ21810) Added ethtool -d register dump. + + Impact: None. + + 3. Change: Reduce the number of registers tested in selftest to make + the driver smaller. + + Impact: None. + + 4. Change: Increased maximum rx ring size to 4080. + + Impact: None. + + 5. Change: Changed rx checksum logic for better reliability. + + Impact: None. + + 6. Change: Added 5708 B1 workarounds. + + Impact: None. + +v1.3.31 (Nov. 18, 2005) +======================= + Fixes: + ------ + 1. Problem: (CQTO2080) Cannot pass traffic during 2nd ifup if IPMI + is enabled. + + Cause: A flaw in driver/firmware handshake. + + Change: Changed driver/firmware handshake by doing WAIT0 first before + writing the first signature. + + Impact: None. + + 2. Problem: (CQTO2056) VLAN-tagged IPMI traffic fails after driver is + loaded. + + Cause: Driver configures the NIC to keep the VLAN tag by default, + causing VLAN-tagged IPMI traffic to fail. + + Change: Changed driver to always strip VLAN tags if IPMI is enabled. + + Impact: VLAN will always be stripped when IPMI is enabled. This may + affect some applications that rely on raw VLAN packets. + + Enhancements: + ------------- + 1. Change: Sync'ed up with latest upstream bnx2. + +v1.3.29 (Oct. 6, 2005) +======================= + Fixes: + ------ + 1. Problem: (CQTO1929) Traffic stops when MTU is greater than 1500 + with TSO enabled. + + Cause: TSO firmware bug. + + Change: Updated to latest firmware. + + Impact: None. + +v1.3.28 (Sep. 29, 2005) +======================= + Fixes: + ------ + 1. Problem: (CQTO1875) Traffic stops when running Nettack with TSO + turned on. + + Cause: TSO firmware bug. + + Change: Updated to latest firmware. + + Impact: None. + + 2. Problem: (CQTO1575) ethtool -S shows non-zero rx_error_bytes. + + Cause: Hardware errata. + + Change: Ignore this counter and always report zero. + + Impact: None. + +v1.3.27 (Sep. 21, 2005) +======================= + Enhancements: + ------------- + 1. Change: Enabled ethernet@wirespeed on copper devices. + + Impact: None. + + 2. Change: Enabled customized pre-emphasis and other serdes control + settings on 5708S backplane devices. A special PHY + register will be programmed according to the NVRAM value. + + Impact: None. + + 3. Change: Modified Makefile to check for the source symbolic link + in the kbuild environment. + + Impact: None. + + 4. Change: Improved NAPI poll processing so that all events will be + processed before interrupts are re-enabled. This will + reduce the number of unnecessary interrupts. + + Impact: None. + + 5. Change: Add support for ETHTOOL_GPERMADDR (from John Linville) + + Impact: None. + + Fixes: + ------ + 1. Problem: Bug in irq handler causing extra register read. + + Cause: Bug in bnx2_interrupt() reading BNX2_PCICFG_MISC_STATUS + register when it is not necessary. + + Change: Fixed the logic bug and added prefetch of status block in + msi handler. + + Impact: None. + +v1.3.25 (Aug. 22, 2005) +======================= + Fixes: + ------ + 1. Problem: Ethtool register and memory tests fail on 5708. + + Cause: Some code was not merged properly many versions ago. + + Change: Merged in properly code. + + Impact: None. + + Enhancements: + ------------- + 1. Change: Slow down flash clock speeds + + Impact: None. + + 2. Change: Code re-sync'ed with upstream driver. + + Impact: None. + +v1.3.24 (Aug. 16, 2005) +======================= + Enhancements: + ------------- + 1. Change: Changed all spin_lock_irqsave locks to spin_lock_bh for + better overall system performance. All code requiring + spin_lock runs in BH or user context. + + Impact: None. + + 2. Change: Removed atomic operations in the transmit fast path. + Code should run a bit faster as atomic operations can be + quite slow. + + Impact: None. + +v1.3.23 (Aug. 10, 2005) +======================= + Enhancements: + ------------- + 1. Change: Added reporting of link speed to firmware. + + Impact: None. + + 2. Change: Added shared memory base look-up. + + Impact: None. + + 3. Change: Possible sparse fixes (from Peter Hagervall) + - use C99 struct initializers + - make a few arrays and structs static + - remove a few uses of literal 0 as NULL pointer + - use convenience function instead of cast+dereference in + bnx2_ioctl() + - remove superfluous casts to u8 * in calls to readl/writel + + Impact: None. + + 4. Change: Updated documentation with the crc32 library requirement and + other minor updates. + + Impact: None. + +v1.3.22 (July 25, 2005) +======================= + Enhancements: + ------------- + 1. Change: Added check for default link speed setting in shared memory. + This allows some blade servers that don't support autoneg to + default to forced speed to speed up link-up time. + + Impact: None. + + 2. Change: Changed timer code to speed up the timer after serdes autoneg. + This speeds up the time for parallel detection when the link + partner does not autoneg. After autoneg or parallel detction + completes, timer goes back to normal 1 HZ interval. With the + new scheme, link should be detected up using parallel detection + in less than 0.7 seconds. Autoneg normally completes in 120 + msec exchanging base pages and 3 next pages. + + Impact: None. + +v1.3.21 (July 21, 2005) +======================= + Enhancements: + ------------- + 1. Change: Updated firmware to properly handle TSO packets with ECN + bits set. + + Impact: None. + + 2. Change: Updated to latest rxp firmware. + + Impact: None. + + 3. Change: Added check for the 2.5G capability in shared memory. + + Impact: None. + + 4. Change: Expanded nvram support for 5708. + + Impact: None. + + Fixes: + ------ + 1. Problem: (CQTO1511) Downing a team of bnx2 adapters causes hang. + + Cause: Deadlock when the second adapter's close routine calls + flush_scheduled_work() because the linkwatch_event + is scheduled on the work queue when the first adapter + is closed. The linkwatch_event will try to get the rtnl_lock + which is already held by the bnx2_close routine. + + Change: Fixed by not calling flush_scheduled_work(). Instead, + use a flag to indicate work is pending and wait until + the flag is cleared. + + Impact: None. + + 2. Problem: (CQTO1439) ethtool -S on 5708 causes oops. + + Cause: A pointer is not initialized if the device is 5708. + + Change: Added proper initialization. + + Impact: None. + + 3. Problem: Some error counters are mis-reporting. + + Cause: The workaround for the hw errata is not applied properly + on the chips with the errata. + + Change: Added proper checks for the workaround. + + Impact: None. + + 4. Problem: High BER on 5708 SerDes. + + Cause: hardware errata. + + Change: Added workaround for 5708 A0 and B0. + + Impact: None. + +v1.3.19 (May 27, 2005) +======================= + Enhancements: + ------------- + 1. Change: Merged 5708 code with the latest 1.1.19. + + Impact: None. + + Fixes: + ------ + 1. Problem: (CQTO1260) 5708S does not link at 1000Mbps when forced. It + links at 2500Mbps when connected back-to-back with another + 5708S. + + Cause: 2.5G bit is still turned on. + + Change: Fixed by turning off 2.5G when the speed is forced. + + Impact: None. + +v1.1.19 (May 26, 2005) +======================= + Enhancements: + ------------- + 1. Change: Made significant changes to merge into the kernel. Among + them, consolidate all source files into bnx2.[ch] and + bnx2_fw.h, change structure style register definitions + to offset based. + + Impact: None. + + Fixes: + ------ + 1. Problem: Excessive stack usage. + + Cause: Use of local variable array. + + Change: Fix excessive stack usage in bnx2_alloc_bad_rbuf() by + replacing local variable array with kmalloc array. Also + changed function to return error code, and changed some + of the callers to check for the return code. + + Impact: None. + + 2. Problem: Bug in receive checksum. + + Cause: Software bug. + + Change: Fix bug in rx checksum by indicating CHECKSUM_UNNECESSARY + only when the hw calculated checksum is 0xffff. + + Impact: None. + + 3. Problem: Excessive busy loop. + + Cause: Software bug. + + Change: Replaced one excessive udelay of 15msec with msleep since + the calling context is always process. + + Impact: None. + + 4. Problem: Need to call flush_scheduled_work() and bnx2_netif_stop(). + + Cause: Bug in bnx2_close(). + + Change: Fix bug in bnx2_close() by calling flush_scheduled_work() + since we are using a work queue in netdev watchdog. Also + added bnx2_netif_stop() call in bnx2_close(). + + Impact: None. + + 5. Problem: Some symbols do not have bnx2 prefix and are not static. + + Change: Add bnx2_ prefix to some remaining names and mark some + remaining names static. + + Impact: None. + +v1.1.17 (Apr. 7, 2005) +======================= + Fixes: + ------ + 1. Problem: Driver does not compile on 2.6.12 kernel. + + Cause: pci_dev->slot_name has been deprecated. + + Change: replaced with pci_name. + + Impact: None. All kernels supported by bnx2 including 2.4.21-*EL have + pci_name defined. + +v1.1.16 (Mar. 16, 2005) +======================= + Fixes: + ------ + 1. Problem: (CQTO936 & 1004) Tx carrier and rx_byte_errors reported + during normal operations. + + Cause: 5706 hardware errata on these 2 statistics counters. + + Change: Modified driver to skip these 2 counters. + + Impact: These 2 counters will always report 0 and not the true + statistics. + +v1.3.15 (Mar. 11, 2005) +====================== + Enhancements: + ------------- + 1. Change: Added SGMII 10/100/1000 Mbps and fiber mode 1000/2500 Mbps + support. + + Impact: None. + +v1.1.15 (Mar. 09, 2005) +======================= + Enhancements: + ------------- + 1. Change: Added code to program proper phy settings for jumbo + frames on copper and serdes devices. + + Impact: None. + + 2. Change: Changed INTx ISR to handle a valid case where INTx + arrives before status block update. + + Impact: None. + + 3. Change: Added a separate ISR for MSI to handle subtle differences + between INTx and MSI. + + Impact: None. + +v1.1.14 (Mar. 03, 2005) +====================== + Fixes: + ------ + 1. Problem: netdump does not work on latest RH3EL errata kernel. + + Cause: #define RED_HAT_LINUX_KERNEL removed from kernel + include files, causing netdump poll function to + be compiled wrong for RH kernels. + + Change: Changed Makefile to workaround the problem. + + Impact: None. + +v1.1.12 (Feb. 25, 2005) +====================== + Fixes: + ------ + 1. Problem: (CQTO1062) Driver allows ethtool to set illegal speed/duplex + on fiber cards. + + Cause: Missing checks for the case where autoneg is off. + + Change: Added proper checks. + + Impact: No. + +v1.1.11 (Feb. 18, 2005) +====================== + Enhancements: + ------------- + 1. Change: (CQTO901) Added bnx2(4) man page. + + Impact: None. + +v1.1.10 (Feb. 16, 2005) +====================== + Fixes: + ------ + 1. Problem: Wrong device ID in pci_dev_id table. + + Change: Fixed ID. + + Impact: None. + +v1.1.9 (Feb. 10, 2005) +====================== + Fixes: + ------ + 1. Problem: (CQTO771) Driver fails to compile on RHEL3 update 4. + + Cause: The RH kernel has back-ported some netif functions + not found in the same vanilla kernel version. Duplicate + function names are defined. + + Change: Added intelligence in Makefile to detect such conditions + and change the CFLAGS accordingly. + + Impact: None. + + 2. Problem: (CQTO1017) Driver allows 1000 half duplex autoneg off. + + Cause: Missing validity check. + + Change: Added check. + + Impact: None. + + Enhancements: + ------------- + 1. Change: The code to handle tx queue full condition was redone + to properly handle all boundary and race conditions. + + Impact: Minimum tx ring size is now MAX_SKB_FRAGS+1. + + 2. Change: Tx timeout code was changed to work queue context. + This allows netif to be shutdown gracefully before + chip reset. + + Impact: None. + + 3. Change: Added memory barriers to ISR and NAPI poll for + correctness. + + Impact: None. + +v1.1.8 (Feb. 01, 2005) +====================== + Fixes: + ------ + 1. Problem: (CQTO969) No interupts on a system that does not + support MSI. + + Cause: On this system, the MSI generated in the MSI test + terminates with Master Abort which is a fatal + condition for the chip. + + Change: A chip reset is added when MSI test fails to bring + the chip back to normal operations again (in INTx mode). + + Impact: None. + + Enhancements: + ------------- + 1. Change: Removed register 0x84 from the ethtool register test. + This is an interrupt control register that should + not be touched to prevent unwanted interrupts. + + Impact: None. + +v1.1.7 (Jan. 28, 2005) +====================== + Enhancements: + ------------- + 1. Change: Added interrupt test when MSI is enabled. If MSI test + fails, driver will fallback to INTx mode. + + Impact: None. + + 2. Change: Added test for certain chipsets where MSI is known + to fail, and disable MSI unconditionally when + such chipsets are found. + + Impact: None. + + 3. Change: Removed support for 1000 Mbps half duplex. + + Impact: 1000 Mbps half duplex will no longer work. + +v1.1.6 (Jan. 26, 2005) +====================== + Fixes: + ------ + 1. Problem: (CQTO940) MTU size not set properly when device is down. + + Cause: bnx2_change_mtu only handled the "up" case. + + Change: Simple fix to handle the "down" case as well. + + Impact: None + + 2. Problem: (CQTO926) 5706S does not autoneg properly after the + tx cable is pulled. + + Cause: If the link partner is autonegotiating, autoneg will + fail when the tx cable is pulled. The software will + turn off autoneg seeing that the rx cable is still + attached with SIGNAL_DETECT. When the tx cable is + re-attached, autoneg is still off and the link partner + will not establish link. + + Change: Added code to poll another PHY register to check if + configs are received when autoneg is disabled. + + Impact: None + + Enhancements: + ------------- + 1. Change: Added OEM product name strings. + + Impact: None. + +v1.1.5 (Jan. 25, 2005) +====================== + Fixes: + ------ + 1. Problem: PCI-X bus speed may not be detected correctly by the driver, + causing failure because certain register settings are done + based on bus speed. + + Cause: hardware problem. + + Change: Use a different register to detect PCI-X speed. + + Impact: None. + +v1.1.4 (Jan. 17, 2005) +====================== + Fixes: + ------ + 1. Problem: 5706S does not reliably detect link up or down. + + Cause: hardware problem. + + Change: Enabled hardware workaround which is effective in + A2 and newer chips only. + + Impact: None. + +v1.1.3 (Jan. 13, 2005) +====================== + Fixes: + ------ + 1. Problem: netdump does not work. + + Cause: poll_controller routine does not work in NAPI mode. + + Change: Fixed poll_controller routine for NAPI mode. + + Impact: None. + + Enhancements: + ------------- + 1. Change: Added the new mmiowb macro which is used mainly in NUMA + machines to guarantee PCI ordering. + + Impact: None. + + 2. Change: Added OEM product name strings. + + Impact: None. + + 3. Change: Changed ONE_TDMA workaround on systems with P64H bridge to + PING_PONG_DMA on all systems using PCI. + + Impact: Better performance and better compatibility on all PCI + systems. + + 4. Change: Added "disable_msi" parameter to allow disabling MSI. + + Impact: None. + + 5. Change: Reduced default tx coalescing parameters for better + tx only performance such as ttcp. + + Impact: None. + + 6. Change: Changed NIC detection console message to display more + information. + + Impact: None. + + 7. Change: Cleaned up some Linux .h files that are not needed. + + Impact: None. + + 8. Change: Cleaned up the nvram routines. + + Impact: None. + +v1.1.2 (Nov. 11, 2004) +====================== + Fixes: + ------ + 1. Problem: Unable to load the driver on more than 2 or 3 devices. + + Cause: Default register space is too big. System fails to map + the register space on the 3rd or 4th device. + + Change: Changed driver to map a smaller register space as the higher + registers are not needed for standard operations. + + Impact: None. + +v1.1.1 (Nov. 9, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO656) One remaining ethtool parameter can be set out of + range. + + Cause: Wrong upper bounds in code. + + Change: Fixed upper bound. + + Impact: None. + + 1. Problem: 5706 A1 cannot be run on a PCI bus. + + Cause: Hardware errata. + + Change: Added workaround to detect 5706 A1 on PCI bus and abort. + + Impact: Driver will not load 5706 A1 on PCI bus. + +v1.1.0 (Oct. 29, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO663) No link on fiber NIC set to autoneg when + the link partner is set to forced speed. + + Cause: No code to handle this case. + + Change: Added code to check SIGNAL DETECT when autoneg fails, + and turn off autoneg when appropriate. Also added code + to force a link down when speed settings are changed. + + Impact: None. + + 2. Problem: (CQTO649) ifconfig up/down can cause system to + hang on 2.6 kernels using mmconfig. + + Cause: Dummy read in the kernel's pci_mmcfg_write routine + may cause system to hang during D3hot -> D0 + transition. The PLL may lose lock during the internal + D0 transition chip reset and cause the dummy read to + hang. + + Change: The PLL problem will be fixed in A2. Meanwhile, the + driver is changed to go into D3hot only if wol is + enabled. + + Impact: Minor impact in increased power consumption when + the driver is shutdown with wol disabled. + + 3. Change: Changed to use pci_get_device as pci_find_device + will be deprecated soon. + + Impact: None. + + 4. Problem: (CQTO656) Some ethtool parameters can be set out of range. + + Cause: Wrong upper bounds in code or documentation. + + Change: Fixed code and documentation. + + Impact: None. + +v1.0.15 (Oct. 15, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO644) Data corruption when sending FTP traffic on + Dell Precision 530MT. + + Cause: PCI bridge errata. + + Change: Added code to enable ONE_TDMA workaround when P64H + PCI bridge is detected. + + Impact: Slower tx performance on systems using P64H bridge. + + 2. Problem: (CQTO643) Driver allows MTU to be set up to 9004. + + Cause: Driver used the wrong upper bound limit. + + Change: Fixed the upper bound. + + Impact: None. + + 3. Problem: Driver does not support forced speed on 5706S. + + Cause: Missing software logic. + + Change: Added code to support forced 1000 Full and Half duplex. + + Impact: None. + + 4. Change: Added workaround to disable PERR generation on + 5706 A1 when it is in PCI 64-bit slot. + + Impact: None. + +v1.0.14 (Oct. 05, 2004) +====================== + Fixes: + ------ + 1. Problem: Driver load/unload hangs on some machines. + + Cause: Transition delay required during D3hot -> D0 transition. + + Change: Added necessary delay. + + Impact: None. + +v1.0.12 (Oct. 04, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO609) New flow control parameter does not take + effect until ifdown/ifup or link down/up. + + Cause: New flow control settings are only configured during + link up. The driver did not check flow control + advertisement changes and therefore no re-negotiation + and no link down. + + Change: Fixed logic so that link will always go down whenever + there is a change a flow control advertisement. + + Impact: None. + + 2. Problem: (CQTO612) VLAN packet reception not working. + + Cause: VLAN #define not setup properly. + + Change: Fixed #define for VLAN. + + Impact: None. + + 3. Problem: (CQTO628) WoL not functioning correctly. + + Cause: Multiple problems in the WoL code. + + Change: Fixed by: + 1. using sort mode. + 2. setting up mac address correctly. + + Impact: None. + + Enhancements: + ------------- + 1. Change: Added support for non-dword and non-aligned write access + to eeprom, allowing ethtool -E to work. + + Impact: None. + +v1.0.11 (Sep. 24, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO600) Kernel panic when attempting to ftp on + Fedora kernel. + + Cause: skbuffs are 4-byte aligned on Fedora, causing + the rx headers to be misaligned. skbuffs are + 16-byte aligned on other standard kernels. + + Change: Removed any assumptions about skbuff alignment + and added code to align all rx skbuffs to 8-byte + boundaries. + + Impact: None. + + Enhancements: + ------------- + 1. Change: Added netif_carrier_off to Driver init code so that + the initial link state is off. + + Impact: None. + +v1.0.10 (Sep. 23, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO599) Driver fails to compile on RH 3.0ES. + + Cause: Bug in poll_bcm5706 where bp is referenced without + being defined. + + Change: Fixed the minor bug. + + Impact: None. + +v1.0.9 (Sep. 23, 2004) +====================== + Fixes: + ------ + 1. Problem: Jumbo frames not working reliably. + + Cause: Bug in bcm5706_reuse_rx_skb. + + Change: Fixed the bug so that the host address is properly + copied from one BD to the other. + + Impact: None. + + Enhancements: + ------------- + 1. Change: Change the locking scheme for more reliable open/close/ioctl + operations where the spinlock is needed. + + Impact: None. + + 2. Change: Added minimum MTU size check for MTU changes. + + Impact: None. + +v1.0.8 (Sep. 20, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO561) Kernel panic on RH30EL after repeated + driver load/unload. + + Cause: Occasionally, the system is unable to map device + registers, causing subsequent register access to fail. + + Change: Added check for the return value from ioremap, and exit + gracefully if it fails. + + Impact: None. + + 2. Problem: (CQTO574) NETDEV WATCHDOG when running 5706 A1 on + PCIX 133MHz slot. + + Cause: Some hw register setup required for 133 Mhz. + + Change: Added the register setup code. + + Impact: None. + + 3. Problem: (CQTO468) Link does not go down after changing from + 10Mbps full (forced) to 10Mbps half (forced) and vice versa + when the link partner is also using forced speed. + + Cause: Delay not long enough for the link to go down. + + Change: Not practical to increase delay as a very long delay is + needed for the link to go down. Added code to record the + proper link speed and duplex when speed is forced so that + ethtool will return the proper speed without the link going + down. Note that the link will go down if the link partner + has autoneg turned on, ensuring that the link partner will + see the link change. + + Impact: None. + + Enhancements: + ------------- + 1. Change: Added power management and WOL during device close. This + will allow WOL to work after Linux shutdown. + + Impact: None. + + 2. Change: Changed driver so that ethtool will report invalid + speed after ifdown. + + Impact: None. + + 3. Change: Updated tpatfw.h + + Impact: None. + +v1.0.7 (Sep. 14, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO523) 5706 A1 hangs after a thousand iterations of + load/ifup/ifdown/unload. + + Cause: The driver may be servicing an interrupt + within 15 msec of chip reset, causing the chip to hang. + The problem was aggravated by the timer mode + coalescing which created an excessive number of + interrupts (hardware problem). + + The Driver may also occasionally see + the wrong firmware signature that will cause it to abort. + + Change: Added synchronization to make sure all pending interrupts + are serviced before resetting the chip. Also changed to + collect mode coalescing. The firmware signature problem + will be fixed in boot code. + + Impact: The use of collect mode coalescing may affect performance. + + 2. Problem: (CQTO558) Unable to load driver on some early 2.6 kernels. + + Cause: pci_dma_sync_single_for_* routines not defined on early + 2.6 kernels. + + Change: Added compatibility defines for earlier kernels. + + Impact: None. + +v1.0.6 (Sep. 08, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO523) 5706 A1 hangs during repeated driver ifup/down. + + Cause: No delay after chip reset for A1. + + Change: Added 15 msec delay after chip reset for A1. + + Impact: None. + + 2. Problem: (CQTO468) Link does not go down after changing from + 10Mbps full (forced) to 10Mbps half (forced) and vice versa. + + Cause: Not enough delay after forcing link down. + + Change: Added proper delay for link to go down. + + Impact: None. + +v1.0.5 (Sep. 02, 2004) +====================== + Enhancements: + ------------- + 1. Change: Added support for ethtool -c, ethtool -C, ethtool -p, + and firmware version for ethtool -i. + + Impact: None. + + Fixes: + ------ + 1. Problem: 5706C not advertising 1000 half duplex. + + Cause: 1000 full duplex overwrites 1000 half duplex. + + Change: Fixed the bug so that the 2 bits are or'ed together. + + Impact: None. + + 2. Problem: ethtool -A does not change the flow control settings + in all cases. + + Cause: Flow control settings not zeroed out before setting new + values. + + Change: Minor bug fix to zero out the value. + + Impact: None. + +v1.0.4 (Sep. 02, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO504) Multiple NFS transfers will hang some + connections. + + Cause: Driver discards some receive packets when the + error status is non-zero. + + Change: Changed the driver to only discard receive packets + when the defined error bits in the error status are + non-zero. + + Impact: None. + +v1.0.3 (Sep. 01, 2004) +====================== + Enhancements: + ------------- + 1. Problem: Driver does not support ethtool -t self test and + ethtool -S statistics. + + Change: Added ethtool self test and statistics. + + Impact: None. + + Fixes: + ------ + 1. Problem: (CQTO478) Kernel still panics when transmitting certain + files using NFS after many iterations + + Cause: Tx DMA stalls on certain UDP packets, causing NFS + client to stall. + + Change: More fixes made in firmware. + + Impact: None. + +v1.0.2 (Aug. 26, 2004) +====================== + Enhancements: + ------------- + 1. Problem: Driver does not work on big endian machines. + + Change: Ported and tested on SPARC64 machine. + + Impact: None. + + Fixes: + ------ + 1. Problem: (CQTO478) Kernel panics when transmitting certain + files using NFS. + + Cause: Tx DMA stalls on certain UDP packets, causing NFS + client to stall. + + Change: Fixed in firmware. + + Impact: None. + + 2. Problem: (CQTO469) Unable to modify flow control parameters + + Cause: Did not call setup_phy after the parameters are changed. + + Change: Added call to setup_phy after parameters are changed. + + Impact: None. + + 3. Problem: (CQTO467) 5706 does not autonegotiate properly after + changing settings with ethtool. + + Cause: This is an ethtool limitation as the proper advertising + parameters are not passed to the driver properly. + + Change: Implemented a new scheme to work around the problem to + allow users to specify full autoneg, single speed autoneg, + and fixed speed. + + Impact: None. + + 4. Problem: ethtool -e does not work. + + Cause: Various bugs in the nvram routines. + + Change: Fixed and tested nvram routines on little endian and big endian + machines. + + Impact: None. + +v1.0.1 (Aug. 18, 2004) +====================== + Fixes: + ------ + 1. Problem: (CQTO459) 5706 NIC does not get link. + + Cause: Boot code timing out in WAIT2 state before driver is ready. + + Change: Moved the WAIT2 state ahead in the driver reset sequence + as a temporary workaround until it is fixed in boot code. + + Impact: None. + + 2. Problem: (CQTO463) Kernel panics when loading the driver on + RH30EL (2.4.21-4.EL) + + Cause: Older kernel used different API to allocate and register + netdev. + + Change: Added older APIs to be used on older kernels. + + Impact: None. + +v1.0.0 (Aug. 13, 2004) +====================== + Initial release diff --git a/brcm_iscsi_uio/config.h.in b/brcm_iscsi_uio/config.h.in new file mode 100644 index 0000000..10b4824 --- /dev/null +++ b/brcm_iscsi_uio/config.h.in @@ -0,0 +1,161 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if the system has the type `int16_t'. */ +#undef HAVE_INT16_T + +/* Define to 1 if the system has the type `int32_t'. */ +#undef HAVE_INT32_T + +/* Define to 1 if the system has the type `int64_t'. */ +#undef HAVE_INT64_T + +/* Define to 1 if the system has the type `int8_t'. */ +#undef HAVE_INT8_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if the system has the type `uint16_t'. */ +#undef HAVE_UINT16_T + +/* Define to 1 if the system has the type `uint32_t'. */ +#undef HAVE_UINT32_T + +/* Define to 1 if the system has the type `uint64_t'. */ +#undef HAVE_UINT64_T + +/* Define to 1 if the system has the type `uint8_t'. */ +#undef HAVE_UINT8_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The size of `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* The size of `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of `short', as computed by sizeof. */ +#undef SIZEOF_SHORT + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Version number of package */ +#undef VERSION + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a + `char[]'. */ +#undef YYTEXT_POINTER + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/brcm_iscsi_uio/configure.ac b/brcm_iscsi_uio/configure.ac new file mode 100644 index 0000000..d0ce7cb --- /dev/null +++ b/brcm_iscsi_uio/configure.ac @@ -0,0 +1,73 @@ +dnl admin_sock.h: CNIC UIO uIP user space stack +dnl +dnl Copyright (c) 2004-2008 Broadcom Corporation +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation. +dnl +dnl Written by: Benjamin Li (benli@broadcom.com) +dnl + +PACKAGE=brcm_iscsiuio +VERSION=0.5.2 + +AC_INIT(brcm_iscsi, 0.5.2, benli@broadcom.com) + +AM_INIT_AUTOMAKE($PACKAGE, $VERSION) +AC_CONFIG_HEADER(config.h) +AC_PATH_PROGS(BASH, bash) + +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_LEX +AC_PROG_YACC + +AC_GNU_SOURCE +AC_PROG_INSTALL +AC_PROG_GCC_TRADITIONAL + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_CHECK_TYPES(int8_t) +AC_CHECK_TYPES(uint8_t) +AC_CHECK_TYPES(int16_t) +AC_CHECK_TYPES(uint16_t) +AC_CHECK_TYPES(int32_t) +AC_CHECK_TYPES(uint32_t) +AC_CHECK_TYPES(int64_t) +AC_CHECK_TYPES(uint64_t) +AC_CHECK_SIZEOF(short, 2) +AC_CHECK_SIZEOF(int, 4) +AC_CHECK_SIZEOF(long, 4) + +AC_C_BIGENDIAN(AC_SUBST([ENDIAN],[BIG]),AC_SUBST([ENDIAN],[LITTLE])) + +AC_LIBTOOL_DLOPEN + +# libtool stuff +AC_PROG_LIBTOOL + +CFLAGS="-O2 -Wall" +## check for --enable-debug first before checking CFLAGS before +## so that we don't mix -O and -g +AC_ARG_ENABLE(debug, +[ --enable-debug Turn on compiler debugging information (default=no)], + [if eval "test x$enable_debug = xyes"; then + CFLAGS="${CFLAGS} -g -O0" + fi]) +AM_CONDITIONAL([DEBUG], [test x$debug = xtrue]) + +AC_CONFIG_COMMANDS([default],[[ echo 'char *build_date ="'`date`'";' > src/unix/build_date.c && echo 'char *build_date; '> src/unix/build_date.h]],[[]]) + +AC_OUTPUT([Makefile +src/Makefile +src/apps/Makefile +src/apps/dhcpc/Makefile +src/apps/brcm-iscsi/Makefile +src/uip/Makefile +src/unix/Makefile +src/unix/libs/Makefile]) diff --git a/brcm_iscsi_uio/docs/brcm_iscsiuio.8 b/brcm_iscsi_uio/docs/brcm_iscsiuio.8 new file mode 100644 index 0000000..bfb381d --- /dev/null +++ b/brcm_iscsi_uio/docs/brcm_iscsiuio.8 @@ -0,0 +1,64 @@ +.\" Copyright (c) 200o Broadcom Corporation +.\" This is free documentation; you can redistribute it and/or +.\" modify it under the terms of the GNU General Public License as +.\" published by the Free Software Foundation. +.\" +.\" bnx2.4,v 0.5.0 +.\" +.TH brcm_iscsiuio 8 "01/08/09" "Broadcom Corporation" +.\" +.\" NAME part +.\" +.SH NAME +brcm_iscsiuio \- Broadcom iSCSI UserSpace I/O driver +.\" +.\" SYNOPSIS part +.\" +.SH SYNOPSIS +.B brcm_iscsiuio +.RB [ --config --version --help ] +.PP +.\" +.\" DESCRIPTION part +.\" +.SH DESCRIPTION +brcm_iscsiuio is the UserSpace I/O driver for the Broadcom NetXtreme II +BCM5706/5708/5709 series PCI/PCI-X Gigabit Ethernet Network Interface Card +(NIC) and for the Broadcom NetXtreme II BCM57710/57711/570711E series +PCI-E 10 Gigabit Ethernet Network Interface Card. +The driver has been tested on 2.6.28 kernels and above. +.PP +Refer to the README.TXT from the driver package on how to +compile and install the driver. +.PP +Refer to various Linux documentations +on how to configure network protocol and address. +.\" +.\" DRIVER DEPENDENCIES part +.\" +.SH DRIVER DEPENDENCIES + +.\" +.\" PARAMETER part +.\" +.SH PARAMETERS +There are very few parameters when running this application. +.TP +.BI -d +This is to enable debug mode where debug messages will be sent to stdout +.PP +.TP +.TP +.BI -f +This is to enable forground mode so that this application doesn't get sent +into the background. +.PP +.TP +.BI -v +This is to print the version. + +.\" +.\" AUTHOR part +.\" +.SH AUTHOR +Benjamin Li \- benli@broadcom.com diff --git a/brcm_iscsi_uio/include/config.h b/brcm_iscsi_uio/include/config.h new file mode 100644 index 0000000..ffada25 --- /dev/null +++ b/brcm_iscsi_uio/include/config.h @@ -0,0 +1,237 @@ +/* + * iSCSI Configuration + * + * Copyright (C) 2002 Cisco Systems, Inc. + * maintained by linux-iscsi-devel@lists.sourceforge.net + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include +//#include +//#include "types.h" +//#include "auth.h" /* for the username and password sizes */ +#include "list.h" + +/* ISIDs now have a typed naming authority in them. We use an OUI */ +#define DRIVER_ISID_0 0x00 +#define DRIVER_ISID_1 0x02 +#define DRIVER_ISID_2 0x3D + +/* number of possible connections per session */ +#define ISCSI_CONN_MAX 1 +/* max len of interface */ +#define ISCSI_MAX_IFACE_LEN 65 + +/* the following structures store the options set in the config file. + * a structure is defined for each logically-related group of options. + * if you are adding a new option, first check if it should belong + * to one of the existing groups. If it does, add it. If not, define + * a new structure. + */ + +#if 0 +/* all authentication-related options should be added to this structure. + * this structure is per-session, and can be configured + * by TargetName but not Subnet. + */ +struct iscsi_auth_config { + unsigned int authmethod; + char username[AUTH_STR_MAX_LEN]; + unsigned char password[AUTH_STR_MAX_LEN]; + unsigned int password_length; + char username_in[AUTH_STR_MAX_LEN]; + unsigned char password_in[AUTH_STR_MAX_LEN]; + unsigned int password_in_length; +}; + +/* all per-connection timeouts go in this structure. + * this structure is per-portal, and can be configured + * both by TargetName and Subnet. + */ +struct iscsi_connection_timeout_config { + int login_timeout; + int logout_timeout; + int auth_timeout; + int active_timeout; + int noop_out_interval; + int noop_out_timeout; +}; + +/* all per-connection timeouts go in this structure. + * this structure is per-session, and can be configured + * by TargetName but not by Subnet. + */ +struct iscsi_session_timeout_config { + int replacement_timeout; +}; + +/* all error handling timeouts go in this structure. + * this structure is per-portal, and can be configured + * both by TargetName and Subnet. + */ +struct iscsi_error_timeout_config { + int abort_timeout; + int host_reset_timeout; + int lu_reset_timeout; +}; + +/* all TCP options go in this structure. + * this structure is per-portal, and can be configured + * both by TargetName and Subnet. + */ +struct iscsi_tcp_config { + int window_size; + int type_of_service; /* try to set IP TOS bits */ +}; + +struct iscsi_conn_operational_config { + int MaxRecvDataSegmentLength; + int MaxXmitDataSegmentLength; + int HeaderDigest; + int DataDigest; + int IFMarker; + int OFMarker; +}; + +/* all iSCSI operational params go in this structure. + * this structure is per-portal, and can be configured + * both by TargetName and Subnet. + */ +struct iscsi_session_operational_config { + int DataPDUInOrder; + int DataSequenceInOrder; + int protocol; + int InitialR2T; + int ImmediateData; + int FirstBurstLength; + int MaxBurstLength; + int DefaultTime2Wait; + int DefaultTime2Retain; + int MaxConnections; + int MaxOutstandingR2T; + int ERL; + int FastAbort; +}; + +#define CONFIG_DIGEST_NEVER 0 +#define CONFIG_DIGEST_ALWAYS 1 +#define CONFIG_DIGEST_PREFER_ON 2 +#define CONFIG_DIGEST_PREFER_OFF 3 + +struct iscsi_sendtargets_config { + int reopen_max; + struct iscsi_auth_config auth; + struct iscsi_connection_timeout_config conn_timeo; + struct iscsi_conn_operational_config iscsi; +}; + +struct iscsi_slp_config { + char *scopes; + char *interfaces; /* for multicast, list of interfaces names, + * "all", or "none" */ + int poll_interval; + struct iscsi_auth_config auth; +}; + +typedef enum iscsi_startup { + ISCSI_STARTUP_MANUAL, + ISCSI_STARTUP_AUTOMATIC, + ISCSI_STARTUP_ONBOOT, +} iscsi_startup_e; + +typedef enum discovery_type { + DISCOVERY_TYPE_SENDTARGETS, + DISCOVERY_TYPE_OFFLOAD_SENDTARGETS, + DISCOVERY_TYPE_SLP, + DISCOVERY_TYPE_ISNS, + DISCOVERY_TYPE_STATIC, + DISCOVERY_TYPE_FW, +} discovery_type_e; + +typedef struct conn_rec { + iscsi_startup_e startup; + char address[NI_MAXHOST]; + int port; + struct iscsi_tcp_config tcp; + struct iscsi_connection_timeout_config timeo; + struct iscsi_conn_operational_config iscsi; +} conn_rec_t; + +typedef struct session_rec { + int initial_cmdsn; + int reopen_max; + int xmit_thread_priority; + int cmds_max; + int queue_depth; + int initial_login_retry_max; + struct iscsi_auth_config auth; + struct iscsi_session_timeout_config timeo; + struct iscsi_error_timeout_config err_timeo; + struct iscsi_session_operational_config iscsi; +} session_rec_t; +#endif + +#define ISCSI_TRANSPORT_NAME_MAXLEN 16 + +typedef struct iface_rec { + struct list_head list; + /* iscsi iface record name */ + char name[ISCSI_MAX_IFACE_LEN]; + /* network layer iface name (eth0) */ + char netdev[IFNAMSIZ]; + char ipaddress[NI_MAXHOST]; + /* + * TODO: we may have to make this bigger and interconnect + * specific for infinniband + */ + char hwaddress[ISCSI_MAX_IFACE_LEN]; + char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; + /* + * This is only used for boot now, but the iser guys + * can use this for their virtualization idea. + */ + char alias[TARGET_NAME_MAXLEN + 1]; + char iname[TARGET_NAME_MAXLEN + 1]; +} iface_rec_t; + +#if 0 +typedef struct node_rec { + struct list_head list; + char name[TARGET_NAME_MAXLEN]; + int tpgt; + iscsi_startup_e startup; + session_rec_t session; + conn_rec_t conn[ISCSI_CONN_MAX]; + iface_rec_t iface; + discovery_type_e disc_type; + char disc_address[NI_MAXHOST]; + int disc_port; +} node_rec_t; + +typedef struct discovery_rec { + iscsi_startup_e startup; + discovery_type_e type; + char address[NI_MAXHOST]; + int port; + union { + struct iscsi_sendtargets_config sendtargets; + struct iscsi_slp_config slp; + } u; +} discovery_rec_t; +#endif + +#endif /* CONFIG_H */ diff --git a/brcm_iscsi_uio/include/fw_context.h b/brcm_iscsi_uio/include/fw_context.h new file mode 100644 index 0000000..d3971c4 --- /dev/null +++ b/brcm_iscsi_uio/include/fw_context.h @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright (C) IBM Corporation. 2007 + * Author: Doug Maxey + * "Prasanna Mumbai" + * + */ +#ifndef FWPARAM_CONTEXT_H_ +#define FWPARAM_CONTEXT_H_ + +#include + +#include "iscsi_proto.h" +#include "list.h" +#include "auth.h" + +struct boot_context { + struct list_head list; + + /* target settings */ + int target_port; + char targetname[TARGET_NAME_MAXLEN + 1]; + char target_ipaddr[32]; + char chap_name[AUTH_STR_MAX_LEN]; + char chap_password[AUTH_STR_MAX_LEN]; + char chap_name_in[AUTH_STR_MAX_LEN]; + char chap_password_in[AUTH_STR_MAX_LEN]; + + /* initiator settings */ + char isid[10]; + char initiatorname[TARGET_NAME_MAXLEN + 1]; + + /* network settings */ + char dhcp[18]; + char iface[IF_NAMESIZE]; + char mac[18]; + char ipaddr[18]; + char gateway[18]; + char primary_dns[18]; + char secondary_dns[18]; + char mask[18]; + char lun[17]; + char vlan[15]; +}; + +extern int fw_get_entry(struct boot_context *context); +extern void fw_print_entry(struct boot_context *context); +extern int fw_get_targets(struct list_head *list); +extern void fw_free_targets(struct list_head *list); + +#endif /* FWPARAM_CONTEXT_H_ */ diff --git a/brcm_iscsi_uio/include/iscsi_if.h b/brcm_iscsi_uio/include/iscsi_if.h new file mode 100644 index 0000000..a9ac145 --- /dev/null +++ b/brcm_iscsi_uio/include/iscsi_if.h @@ -0,0 +1,473 @@ +/* + * iSCSI User/Kernel Shares (Defines, Constants, Protocol definitions, etc) + * + * Copyright (C) 2005 Dmitry Yusupov + * Copyright (C) 2005 Alex Aizman + * maintained by open-iscsi@googlegroups.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ + +#ifndef ISCSI_IF_H +#define ISCSI_IF_H + +#include "iscsi_proto.h" +#include +//#include + +#define ISCSI_NL_GRP_ISCSID 1 +#define ISCSI_NL_GRP_UIP 2 + +#define UEVENT_BASE 10 +#define KEVENT_BASE 100 +#define ISCSI_ERR_BASE 1000 + +enum iscsi_uevent_e { + ISCSI_UEVENT_UNKNOWN = 0, + + /* down events */ + ISCSI_UEVENT_CREATE_SESSION = UEVENT_BASE + 1, + ISCSI_UEVENT_DESTROY_SESSION = UEVENT_BASE + 2, + ISCSI_UEVENT_CREATE_CONN = UEVENT_BASE + 3, + ISCSI_UEVENT_DESTROY_CONN = UEVENT_BASE + 4, + ISCSI_UEVENT_BIND_CONN = UEVENT_BASE + 5, + ISCSI_UEVENT_SET_PARAM = UEVENT_BASE + 6, + ISCSI_UEVENT_START_CONN = UEVENT_BASE + 7, + ISCSI_UEVENT_STOP_CONN = UEVENT_BASE + 8, + ISCSI_UEVENT_SEND_PDU = UEVENT_BASE + 9, + ISCSI_UEVENT_GET_STATS = UEVENT_BASE + 10, + ISCSI_UEVENT_GET_PARAM = UEVENT_BASE + 11, + + ISCSI_UEVENT_TRANSPORT_EP_CONNECT = UEVENT_BASE + 12, + ISCSI_UEVENT_TRANSPORT_EP_POLL = UEVENT_BASE + 13, + ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT = UEVENT_BASE + 14, + + ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15, + ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16, + ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17, + ISCSI_UEVENT_CREATE_BOUND_SESSION = UEVENT_BASE + 18, + ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST = UEVENT_BASE + 19, + + ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20, + + /* up events */ + ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, + ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2, + ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3, + ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4, + ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5, + ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6, + + ISCSI_KEVENT_PATH_REQ = KEVENT_BASE + 7, + ISCSI_KEVENT_IF_DOWN = KEVENT_BASE + 8, +}; + +enum iscsi_tgt_dscvr { + ISCSI_TGT_DSCVR_SEND_TARGETS = 1, + ISCSI_TGT_DSCVR_ISNS = 2, + ISCSI_TGT_DSCVR_SLP = 3, +}; + +struct iscsi_uevent { + uint32_t type; /* k/u events type */ + uint32_t iferror; /* carries interface or resource errors */ + uint64_t transport_handle; + + union { + /* messages u -> k */ + struct msg_create_session { + uint32_t initial_cmdsn; + uint16_t cmds_max; + uint16_t queue_depth; + } c_session; + struct msg_create_bound_session { + uint64_t ep_handle; + uint32_t initial_cmdsn; + uint16_t cmds_max; + uint16_t queue_depth; + } c_bound_session; + struct msg_destroy_session { + uint32_t sid; + } d_session; + struct msg_create_conn { + uint32_t sid; + uint32_t cid; + } c_conn; + struct msg_bind_conn { + uint32_t sid; + uint32_t cid; + uint64_t transport_eph; + uint32_t is_leading; + } b_conn; + struct msg_destroy_conn { + uint32_t sid; + uint32_t cid; + } d_conn; + struct msg_send_pdu { + uint32_t sid; + uint32_t cid; + uint32_t hdr_size; + uint32_t data_size; + } send_pdu; + struct msg_set_param { + uint32_t sid; + uint32_t cid; + uint32_t param; /* enum iscsi_param */ + uint32_t len; + } set_param; + struct msg_start_conn { + uint32_t sid; + uint32_t cid; + } start_conn; + struct msg_stop_conn { + uint32_t sid; + uint32_t cid; + uint64_t conn_handle; + uint32_t flag; + } stop_conn; + struct msg_get_stats { + uint32_t sid; + uint32_t cid; + } get_stats; + struct msg_transport_connect { + uint32_t non_blocking; + } ep_connect; + struct msg_transport_connect_through_host { + uint32_t host_no; + uint32_t non_blocking; + } ep_connect_through_host; + struct msg_transport_poll { + uint64_t ep_handle; + uint32_t timeout_ms; + } ep_poll; + struct msg_transport_disconnect { + uint64_t ep_handle; + } ep_disconnect; + struct msg_tgt_dscvr { + enum iscsi_tgt_dscvr type; + uint32_t host_no; + /* + * enable = 1 to establish a new connection + * with the server. enable = 0 to disconnect + * from the server. Used primarily to switch + * from one iSNS server to another. + */ + uint32_t enable; + } tgt_dscvr; + struct msg_set_host_param { + uint32_t host_no; + uint32_t param; /* enum iscsi_host_param */ + uint32_t len; + } set_host_param; + struct msg_set_path { + uint32_t host_no; + } set_path; + } u; + union { + /* messages k -> u */ + int retcode; + struct msg_create_session_ret { + uint32_t sid; + uint32_t host_no; + } c_session_ret; + struct msg_create_conn_ret { + uint32_t sid; + uint32_t cid; + } c_conn_ret; + struct msg_unbind_session { + uint32_t sid; + uint32_t host_no; + } unbind_session; + struct msg_recv_req { + uint32_t sid; + uint32_t cid; + uint64_t recv_handle; + } recv_req; + struct msg_conn_error { + uint32_t sid; + uint32_t cid; + uint32_t error; /* enum iscsi_err */ + } connerror; + struct msg_session_destroyed { + uint32_t host_no; + uint32_t sid; + } d_session; + struct msg_transport_connect_ret { + uint64_t handle; + } ep_connect_ret; + struct msg_req_path { + uint32_t host_no; + } req_path; + struct msg_notify_if_down { + uint32_t host_no; + } notify_if_down; + } r; +} __attribute__ ((aligned (sizeof(uint64_t)))); + +/* + * To keep the struct iscsi_uevent size the same for userspace code + * compatibility, the main structure for ISCSI_UEVENT_PATH_UPDATE and + * ISCSI_KEVENT_PATH_REQ is defined separately and comes after the + * struct iscsi_uevent in the NETLINK_ISCSI message. + */ +struct iscsi_path { + uint64_t handle; + uint8_t mac_addr[6]; + uint8_t mac_addr_old[6]; + uint32_t ip_addr_len; /* 4 or 16 */ + union { + struct in_addr v4_addr; + struct in6_addr v6_addr; + } src; + union { + struct in_addr v4_addr; + struct in6_addr v6_addr; + } dst; + uint16_t vlan_id; + uint16_t pmtu; +} __attribute__ ((aligned (sizeof(uint64_t)))); + + +/* + * Common error codes + */ +enum iscsi_err { + ISCSI_OK = 0, + + ISCSI_ERR_DATASN = ISCSI_ERR_BASE + 1, + ISCSI_ERR_DATA_OFFSET = ISCSI_ERR_BASE + 2, + ISCSI_ERR_MAX_CMDSN = ISCSI_ERR_BASE + 3, + ISCSI_ERR_EXP_CMDSN = ISCSI_ERR_BASE + 4, + ISCSI_ERR_BAD_OPCODE = ISCSI_ERR_BASE + 5, + ISCSI_ERR_DATALEN = ISCSI_ERR_BASE + 6, + ISCSI_ERR_AHSLEN = ISCSI_ERR_BASE + 7, + ISCSI_ERR_PROTO = ISCSI_ERR_BASE + 8, + ISCSI_ERR_LUN = ISCSI_ERR_BASE + 9, + ISCSI_ERR_BAD_ITT = ISCSI_ERR_BASE + 10, + ISCSI_ERR_CONN_FAILED = ISCSI_ERR_BASE + 11, + ISCSI_ERR_R2TSN = ISCSI_ERR_BASE + 12, + ISCSI_ERR_SESSION_FAILED = ISCSI_ERR_BASE + 13, + ISCSI_ERR_HDR_DGST = ISCSI_ERR_BASE + 14, + ISCSI_ERR_DATA_DGST = ISCSI_ERR_BASE + 15, + ISCSI_ERR_PARAM_NOT_FOUND = ISCSI_ERR_BASE + 16, + ISCSI_ERR_NO_SCSI_CMD = ISCSI_ERR_BASE + 17, + ISCSI_ERR_INVALID_HOST = ISCSI_ERR_BASE + 18, + ISCSI_ERR_XMIT_FAILED = ISCSI_ERR_BASE + 19, +}; + +/* + * iSCSI Parameters (RFC3720) + */ +enum iscsi_param { + /* passed in using netlink set param */ + ISCSI_PARAM_MAX_RECV_DLENGTH, + ISCSI_PARAM_MAX_XMIT_DLENGTH, + ISCSI_PARAM_HDRDGST_EN, + ISCSI_PARAM_DATADGST_EN, + ISCSI_PARAM_INITIAL_R2T_EN, + ISCSI_PARAM_MAX_R2T, + ISCSI_PARAM_IMM_DATA_EN, + ISCSI_PARAM_FIRST_BURST, + ISCSI_PARAM_MAX_BURST, + ISCSI_PARAM_PDU_INORDER_EN, + ISCSI_PARAM_DATASEQ_INORDER_EN, + ISCSI_PARAM_ERL, + ISCSI_PARAM_IFMARKER_EN, + ISCSI_PARAM_OFMARKER_EN, + ISCSI_PARAM_EXP_STATSN, + ISCSI_PARAM_TARGET_NAME, + ISCSI_PARAM_TPGT, + ISCSI_PARAM_PERSISTENT_ADDRESS, + ISCSI_PARAM_PERSISTENT_PORT, + ISCSI_PARAM_SESS_RECOVERY_TMO, + + /* pased in through bind conn using transport_fd */ + ISCSI_PARAM_CONN_PORT, + ISCSI_PARAM_CONN_ADDRESS, + + ISCSI_PARAM_USERNAME, + ISCSI_PARAM_USERNAME_IN, + ISCSI_PARAM_PASSWORD, + ISCSI_PARAM_PASSWORD_IN, + + ISCSI_PARAM_FAST_ABORT, + ISCSI_PARAM_ABORT_TMO, + ISCSI_PARAM_LU_RESET_TMO, + ISCSI_PARAM_HOST_RESET_TMO, + + ISCSI_PARAM_PING_TMO, + ISCSI_PARAM_RECV_TMO, + + ISCSI_PARAM_IFACE_NAME, + ISCSI_PARAM_ISID, + ISCSI_PARAM_INITIATOR_NAME, + /* must always be last */ + ISCSI_PARAM_MAX, +}; + +#define ISCSI_MAX_RECV_DLENGTH (1ULL << ISCSI_PARAM_MAX_RECV_DLENGTH) +#define ISCSI_MAX_XMIT_DLENGTH (1ULL << ISCSI_PARAM_MAX_XMIT_DLENGTH) +#define ISCSI_HDRDGST_EN (1ULL << ISCSI_PARAM_HDRDGST_EN) +#define ISCSI_DATADGST_EN (1ULL << ISCSI_PARAM_DATADGST_EN) +#define ISCSI_INITIAL_R2T_EN (1ULL << ISCSI_PARAM_INITIAL_R2T_EN) +#define ISCSI_MAX_R2T (1ULL << ISCSI_PARAM_MAX_R2T) +#define ISCSI_IMM_DATA_EN (1ULL << ISCSI_PARAM_IMM_DATA_EN) +#define ISCSI_FIRST_BURST (1ULL << ISCSI_PARAM_FIRST_BURST) +#define ISCSI_MAX_BURST (1ULL << ISCSI_PARAM_MAX_BURST) +#define ISCSI_PDU_INORDER_EN (1ULL << ISCSI_PARAM_PDU_INORDER_EN) +#define ISCSI_DATASEQ_INORDER_EN (1ULL << ISCSI_PARAM_DATASEQ_INORDER_EN) +#define ISCSI_ERL (1ULL << ISCSI_PARAM_ERL) +#define ISCSI_IFMARKER_EN (1ULL << ISCSI_PARAM_IFMARKER_EN) +#define ISCSI_OFMARKER_EN (1ULL << ISCSI_PARAM_OFMARKER_EN) +#define ISCSI_EXP_STATSN (1ULL << ISCSI_PARAM_EXP_STATSN) +#define ISCSI_TARGET_NAME (1ULL << ISCSI_PARAM_TARGET_NAME) +#define ISCSI_TPGT (1ULL << ISCSI_PARAM_TPGT) +#define ISCSI_PERSISTENT_ADDRESS (1ULL << ISCSI_PARAM_PERSISTENT_ADDRESS) +#define ISCSI_PERSISTENT_PORT (1ULL << ISCSI_PARAM_PERSISTENT_PORT) +#define ISCSI_SESS_RECOVERY_TMO (1ULL << ISCSI_PARAM_SESS_RECOVERY_TMO) +#define ISCSI_CONN_PORT (1ULL << ISCSI_PARAM_CONN_PORT) +#define ISCSI_CONN_ADDRESS (1ULL << ISCSI_PARAM_CONN_ADDRESS) +#define ISCSI_USERNAME (1ULL << ISCSI_PARAM_USERNAME) +#define ISCSI_USERNAME_IN (1ULL << ISCSI_PARAM_USERNAME_IN) +#define ISCSI_PASSWORD (1ULL << ISCSI_PARAM_PASSWORD) +#define ISCSI_PASSWORD_IN (1ULL << ISCSI_PARAM_PASSWORD_IN) +#define ISCSI_FAST_ABORT (1ULL << ISCSI_PARAM_FAST_ABORT) +#define ISCSI_ABORT_TMO (1ULL << ISCSI_PARAM_ABORT_TMO) +#define ISCSI_LU_RESET_TMO (1ULL << ISCSI_PARAM_LU_RESET_TMO) +#define ISCSI_HOST_RESET_TMO (1ULL << ISCSI_PARAM_HOST_RESET_TMO) +#define ISCSI_PING_TMO (1ULL << ISCSI_PARAM_PING_TMO) +#define ISCSI_RECV_TMO (1ULL << ISCSI_PARAM_RECV_TMO) +#define ISCSI_IFACE_NAME (1ULL << ISCSI_PARAM_IFACE_NAME) +#define ISCSI_ISID (1ULL << ISCSI_PARAM_ISID) +#define ISCSI_INITIATOR_NAME (1ULL << ISCSI_PARAM_INITIATOR_NAME) + +/* iSCSI HBA params */ +enum iscsi_host_param { + ISCSI_HOST_PARAM_HWADDRESS, + ISCSI_HOST_PARAM_INITIATOR_NAME, + ISCSI_HOST_PARAM_NETDEV_NAME, + ISCSI_HOST_PARAM_IPADDRESS, + ISCSI_HOST_PARAM_MAX, +}; + +#define ISCSI_HOST_HWADDRESS (1ULL << ISCSI_HOST_PARAM_HWADDRESS) +#define ISCSI_HOST_INITIATOR_NAME (1ULL << ISCSI_HOST_PARAM_INITIATOR_NAME) +#define ISCSI_HOST_NETDEV_NAME (1ULL << ISCSI_HOST_PARAM_NETDEV_NAME) +#define ISCSI_HOST_IPADDRESS (1ULL << ISCSI_HOST_PARAM_IPADDRESS) + +#define iscsi_ptr(_handle) ((void*)(unsigned long)_handle) +#define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr) + +/* + * These flags presents iSCSI Data-Path capabilities. + */ +#define CAP_RECOVERY_L0 0x1 +#define CAP_RECOVERY_L1 0x2 +#define CAP_RECOVERY_L2 0x4 +#define CAP_MULTI_R2T 0x8 +#define CAP_HDRDGST 0x10 +#define CAP_DATADGST 0x20 +#define CAP_MULTI_CONN 0x40 +#define CAP_TEXT_NEGO 0x80 +#define CAP_MARKERS 0x100 +#define CAP_FW_DB 0x200 +#define CAP_SENDTARGETS_OFFLOAD 0x400 /* offload discovery process */ +#define CAP_DATA_PATH_OFFLOAD 0x800 /* offload entire IO path */ +#define CAP_DIGEST_OFFLOAD 0x1000 /* offload hdr and data digests */ +#define CAP_PADDING_OFFLOAD 0x2000 /* offload padding insertion, removal, + and verification */ + +/* + * These flags describes reason of stop_conn() call + */ +#define STOP_CONN_TERM 0x1 +#define STOP_CONN_SUSPEND 0x2 +#define STOP_CONN_RECOVER 0x3 + +#define ISCSI_STATS_CUSTOM_MAX 32 +#define ISCSI_STATS_CUSTOM_DESC_MAX 64 +struct iscsi_stats_custom { + char desc[ISCSI_STATS_CUSTOM_DESC_MAX]; + uint64_t value; +}; + +/* + * struct iscsi_stats - iSCSI Statistics (iSCSI MIB) + * + * Note: this structure contains counters collected on per-connection basis. + */ +struct iscsi_stats { + /* octets */ + uint64_t txdata_octets; + uint64_t rxdata_octets; + + /* xmit pdus */ + uint32_t noptx_pdus; + uint32_t scsicmd_pdus; + uint32_t tmfcmd_pdus; + uint32_t login_pdus; + uint32_t text_pdus; + uint32_t dataout_pdus; + uint32_t logout_pdus; + uint32_t snack_pdus; + + /* recv pdus */ + uint32_t noprx_pdus; + uint32_t scsirsp_pdus; + uint32_t tmfrsp_pdus; + uint32_t textrsp_pdus; + uint32_t datain_pdus; + uint32_t logoutrsp_pdus; + uint32_t r2t_pdus; + uint32_t async_pdus; + uint32_t rjt_pdus; + + /* errors */ + uint32_t digest_err; + uint32_t timeout_err; + + /* + * iSCSI Custom Statistics support, i.e. Transport could + * extend existing MIB statistics with its own specific statistics + * up to ISCSI_STATS_CUSTOM_MAX + */ + uint32_t custom_length; + struct iscsi_stats_custom custom[0] + __attribute__ ((aligned (sizeof(uint64_t)))); +}; + +/* + * Network interface configuration + */ +enum iscsi_net_param { + ISCSI_NET_PARAM_UNKNOWN = 0x00, + ISCSI_NET_PARAM_MAC_ADDR = ISCSI_NET_PARAM_UNKNOWN + 1, + ISCSI_NET_PARAM_IPV4_ADDR = ISCSI_NET_PARAM_UNKNOWN + 2, + ISCSI_NET_PARAM_IPV6_ADDR = ISCSI_NET_PARAM_UNKNOWN + 3, + ISCSI_NET_PARAM_IPV4_NETMASK = ISCSI_NET_PARAM_UNKNOWN + 4, + ISCSI_NET_PARAM_IPV6_NETMASK = ISCSI_NET_PARAM_UNKNOWN + 5, + ISCSI_NET_PARAM_IPV4_GATEWAY = ISCSI_NET_PARAM_UNKNOWN + 6, + ISCSI_NET_PARAM_IPV6_GATEWAY = ISCSI_NET_PARAM_UNKNOWN + 7, + ISCSI_NET_PARAM_BOOTPROTO = ISCSI_NET_PARAM_UNKNOWN + 8, + ISCSI_NET_PARAM_IPV6_AUTO_PARAM = ISCSI_NET_PARAM_UNKNOWN + 9, + ISCSI_NET_PARAM_MTU = ISCSI_NET_PARAM_UNKNOWN + 10, + ISCSI_NET_PARAM_VLAN = ISCSI_NET_PARAM_UNKNOWN + 11, +}; + +struct iscsi_net_config { + uint32_t param; + uint32_t length; + uint8_t value[1]; +}; + +#endif diff --git a/brcm_iscsi_uio/include/iscsi_proto.h b/brcm_iscsi_uio/include/iscsi_proto.h new file mode 100644 index 0000000..d1e0589 --- /dev/null +++ b/brcm_iscsi_uio/include/iscsi_proto.h @@ -0,0 +1,637 @@ +/* + * RFC 3720 (iSCSI) protocol data types + * + * Copyright (C) 2005 Dmitry Yusupov + * Copyright (C) 2005 Alex Aizman + * maintained by open-iscsi@googlegroups.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ + +#ifndef ISCSI_PROTO_H +#define ISCSI_PROTO_H + +#ifndef __KERNEL__ +#include +#endif +#include + +#define ISCSI_DRAFT20_VERSION 0x00 + +/* default iSCSI listen port for incoming connections */ +#define ISCSI_LISTEN_PORT 3260 + +/* Padding word length */ +#define ISCSI_PAD_LEN 4 + +/* + * useful common(control and data pathes) macro + */ +#define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2])) +#define hton24(p, v) { \ + p[0] = (((v) >> 16) & 0xFF); \ + p[1] = (((v) >> 8) & 0xFF); \ + p[2] = ((v) & 0xFF); \ +} +#define zero_data(p) {p[0]=0;p[1]=0;p[2]=0;} + +/* + * If running svn modules we may need to define these. + * This should not go upstream since this is already properly defined there + */ +#ifdef __CHECKER__ +#define __bitwise__ __attribute__((bitwise)) +#else +#define __bitwise__ +#endif +#ifdef __CHECK_ENDIAN__ +#define __bitwise __bitwise__ +#else +#define __bitwise +#endif + +/* initiator tags; opaque for target */ +typedef uint32_t __bitwise__ itt_t; +/* below makes sense only for initiator that created this tag */ +#define build_itt(itt, age) ((__force itt_t)\ + ((itt) | ((age) << ISCSI_AGE_SHIFT))) +#define get_itt(itt) ((__force uint32_t)(itt_t)(itt) & ISCSI_ITT_MASK) +#define RESERVED_ITT ((__force itt_t)0xffffffff) + +/* + * iSCSI Template Message Header + */ +struct iscsi_hdr { + uint8_t opcode; + uint8_t flags; /* Final bit */ + uint8_t rsvd2[2]; + uint8_t hlength; /* AHSs total length */ + uint8_t dlength[3]; /* Data length */ + uint8_t lun[8]; + itt_t itt; /* Initiator Task Tag, opaque for target */ + __be32 ttt; /* Target Task Tag */ + __be32 statsn; + __be32 exp_statsn; + __be32 max_statsn; + uint8_t other[12]; +}; + +/************************* RFC 3720 Begin *****************************/ + +#define ISCSI_RESERVED_TAG 0xffffffff + +/* Opcode encoding bits */ +#define ISCSI_OP_RETRY 0x80 +#define ISCSI_OP_IMMEDIATE 0x40 +#define ISCSI_OPCODE_MASK 0x3F + +/* Initiator Opcode values */ +#define ISCSI_OP_NOOP_OUT 0x00 +#define ISCSI_OP_SCSI_CMD 0x01 +#define ISCSI_OP_SCSI_TMFUNC 0x02 +#define ISCSI_OP_LOGIN 0x03 +#define ISCSI_OP_TEXT 0x04 +#define ISCSI_OP_SCSI_DATA_OUT 0x05 +#define ISCSI_OP_LOGOUT 0x06 +#define ISCSI_OP_SNACK 0x10 + +#define ISCSI_OP_VENDOR1_CMD 0x1c +#define ISCSI_OP_VENDOR2_CMD 0x1d +#define ISCSI_OP_VENDOR3_CMD 0x1e +#define ISCSI_OP_VENDOR4_CMD 0x1f + +/* Target Opcode values */ +#define ISCSI_OP_NOOP_IN 0x20 +#define ISCSI_OP_SCSI_CMD_RSP 0x21 +#define ISCSI_OP_SCSI_TMFUNC_RSP 0x22 +#define ISCSI_OP_LOGIN_RSP 0x23 +#define ISCSI_OP_TEXT_RSP 0x24 +#define ISCSI_OP_SCSI_DATA_IN 0x25 +#define ISCSI_OP_LOGOUT_RSP 0x26 +#define ISCSI_OP_R2T 0x31 +#define ISCSI_OP_ASYNC_EVENT 0x32 +#define ISCSI_OP_REJECT 0x3f + +struct iscsi_ahs_hdr { + __be16 ahslength; + uint8_t ahstype; + uint8_t ahspec[5]; +}; + +#define ISCSI_AHSTYPE_CDB 1 +#define ISCSI_AHSTYPE_RLENGTH 2 +#define ISCSI_CDB_SIZE 16 + +/* iSCSI PDU Header */ +struct iscsi_cmd { + uint8_t opcode; + uint8_t flags; + __be16 rsvd2; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t lun[8]; + itt_t itt; /* Initiator Task Tag */ + __be32 data_length; + __be32 cmdsn; + __be32 exp_statsn; + uint8_t cdb[ISCSI_CDB_SIZE]; /* SCSI Command Block */ + /* Additional Data (Command Dependent) */ +}; + +/* Command PDU flags */ +#define ISCSI_FLAG_CMD_FINAL 0x80 +#define ISCSI_FLAG_CMD_READ 0x40 +#define ISCSI_FLAG_CMD_WRITE 0x20 +#define ISCSI_FLAG_CMD_ATTR_MASK 0x07 /* 3 bits */ + +/* SCSI Command Attribute values */ +#define ISCSI_ATTR_UNTAGGED 0 +#define ISCSI_ATTR_SIMPLE 1 +#define ISCSI_ATTR_ORDERED 2 +#define ISCSI_ATTR_HEAD_OF_QUEUE 3 +#define ISCSI_ATTR_ACA 4 + +struct iscsi_rlength_ahdr { + __be16 ahslength; + uint8_t ahstype; + uint8_t reserved; + __be32 read_length; +}; + +/* Extended CDB AHS */ +struct iscsi_ecdb_ahdr { + __be16 ahslength; /* CDB length - 15, including reserved byte */ + uint8_t ahstype; + uint8_t reserved; + /* 4-byte aligned extended CDB spillover */ + uint8_t ecdb[260 - ISCSI_CDB_SIZE]; +}; + +/* SCSI Response Header */ +struct iscsi_cmd_rsp { + uint8_t opcode; + uint8_t flags; + uint8_t response; + uint8_t cmd_status; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd[8]; + itt_t itt; /* Initiator Task Tag */ + __be32 rsvd1; + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + __be32 exp_datasn; + __be32 bi_residual_count; + __be32 residual_count; + /* Response or Sense Data (optional) */ +}; + +/* Command Response PDU flags */ +#define ISCSI_FLAG_CMD_BIDI_OVERFLOW 0x10 +#define ISCSI_FLAG_CMD_BIDI_UNDERFLOW 0x08 +#define ISCSI_FLAG_CMD_OVERFLOW 0x04 +#define ISCSI_FLAG_CMD_UNDERFLOW 0x02 + +/* iSCSI Status values. Valid if Rsp Selector bit is not set */ +#define ISCSI_STATUS_CMD_COMPLETED 0 +#define ISCSI_STATUS_TARGET_FAILURE 1 +#define ISCSI_STATUS_SUBSYS_FAILURE 2 + +/* Asynchronous Event Header */ +struct iscsi_async { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd2[2]; + uint8_t rsvd3; + uint8_t dlength[3]; + uint8_t lun[8]; + uint8_t rsvd4[8]; + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + uint8_t async_event; + uint8_t async_vcode; + __be16 param1; + __be16 param2; + __be16 param3; + uint8_t rsvd5[4]; +}; + +/* iSCSI Event Codes */ +#define ISCSI_ASYNC_MSG_SCSI_EVENT 0 +#define ISCSI_ASYNC_MSG_REQUEST_LOGOUT 1 +#define ISCSI_ASYNC_MSG_DROPPING_CONNECTION 2 +#define ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS 3 +#define ISCSI_ASYNC_MSG_PARAM_NEGOTIATION 4 +#define ISCSI_ASYNC_MSG_VENDOR_SPECIFIC 255 + +/* NOP-Out Message */ +struct iscsi_nopout { + uint8_t opcode; + uint8_t flags; + __be16 rsvd2; + uint8_t rsvd3; + uint8_t dlength[3]; + uint8_t lun[8]; + itt_t itt; /* Initiator Task Tag */ + __be32 ttt; /* Target Transfer Tag */ + __be32 cmdsn; + __be32 exp_statsn; + uint8_t rsvd4[16]; +}; + +/* NOP-In Message */ +struct iscsi_nopin { + uint8_t opcode; + uint8_t flags; + __be16 rsvd2; + uint8_t rsvd3; + uint8_t dlength[3]; + uint8_t lun[8]; + itt_t itt; /* Initiator Task Tag */ + __be32 ttt; /* Target Transfer Tag */ + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + uint8_t rsvd4[12]; +}; + +/* SCSI Task Management Message Header */ +struct iscsi_tm { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd1[2]; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t lun[8]; + itt_t itt; /* Initiator Task Tag */ + itt_t rtt; /* Reference Task Tag */ + __be32 cmdsn; + __be32 exp_statsn; + __be32 refcmdsn; + __be32 exp_datasn; + uint8_t rsvd2[8]; +}; + +#define ISCSI_FLAG_TM_FUNC_MASK 0x7F + +/* Function values */ +#define ISCSI_TM_FUNC_ABORT_TASK 1 +#define ISCSI_TM_FUNC_ABORT_TASK_SET 2 +#define ISCSI_TM_FUNC_CLEAR_ACA 3 +#define ISCSI_TM_FUNC_CLEAR_TASK_SET 4 +#define ISCSI_TM_FUNC_LOGICAL_UNIT_RESET 5 +#define ISCSI_TM_FUNC_TARGET_WARM_RESET 6 +#define ISCSI_TM_FUNC_TARGET_COLD_RESET 7 +#define ISCSI_TM_FUNC_TASK_REASSIGN 8 + +/* SCSI Task Management Response Header */ +struct iscsi_tm_rsp { + uint8_t opcode; + uint8_t flags; + uint8_t response; /* see Response values below */ + uint8_t qualifier; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd2[8]; + itt_t itt; /* Initiator Task Tag */ + itt_t rtt; /* Reference Task Tag */ + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + uint8_t rsvd3[12]; +}; + +/* Response values */ +#define ISCSI_TMF_RSP_COMPLETE 0x00 +#define ISCSI_TMF_RSP_NO_TASK 0x01 +#define ISCSI_TMF_RSP_NO_LUN 0x02 +#define ISCSI_TMF_RSP_TASK_ALLEGIANT 0x03 +#define ISCSI_TMF_RSP_NO_FAILOVER 0x04 +#define ISCSI_TMF_RSP_NOT_SUPPORTED 0x05 +#define ISCSI_TMF_RSP_AUTH_FAILED 0x06 +#define ISCSI_TMF_RSP_REJECTED 0xff + +/* Ready To Transfer Header */ +struct iscsi_r2t_rsp { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd2[2]; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t lun[8]; + itt_t itt; /* Initiator Task Tag */ + __be32 ttt; /* Target Transfer Tag */ + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + __be32 r2tsn; + __be32 data_offset; + __be32 data_length; +}; + +/* SCSI Data Hdr */ +struct iscsi_data { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd2[2]; + uint8_t rsvd3; + uint8_t dlength[3]; + uint8_t lun[8]; + itt_t itt; + __be32 ttt; + __be32 rsvd4; + __be32 exp_statsn; + __be32 rsvd5; + __be32 datasn; + __be32 offset; + __be32 rsvd6; + /* Payload */ +}; + +/* SCSI Data Response Hdr */ +struct iscsi_data_rsp { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd2; + uint8_t cmd_status; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t lun[8]; + itt_t itt; + __be32 ttt; + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + __be32 datasn; + __be32 offset; + __be32 residual_count; +}; + +/* Data Response PDU flags */ +#define ISCSI_FLAG_DATA_ACK 0x40 +#define ISCSI_FLAG_DATA_OVERFLOW 0x04 +#define ISCSI_FLAG_DATA_UNDERFLOW 0x02 +#define ISCSI_FLAG_DATA_STATUS 0x01 + +/* Text Header */ +struct iscsi_text { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd2[2]; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd4[8]; + itt_t itt; + __be32 ttt; + __be32 cmdsn; + __be32 exp_statsn; + uint8_t rsvd5[16]; + /* Text - key=value pairs */ +}; + +#define ISCSI_FLAG_TEXT_CONTINUE 0x40 + +/* Text Response Header */ +struct iscsi_text_rsp { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd2[2]; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd4[8]; + itt_t itt; + __be32 ttt; + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + uint8_t rsvd5[12]; + /* Text Response - key:value pairs */ +}; + +/* Login Header */ +struct iscsi_login { + uint8_t opcode; + uint8_t flags; + uint8_t max_version; /* Max. version supported */ + uint8_t min_version; /* Min. version supported */ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t isid[6]; /* Initiator Session ID */ + __be16 tsih; /* Target Session Handle */ + itt_t itt; /* Initiator Task Tag */ + __be16 cid; + __be16 rsvd3; + __be32 cmdsn; + __be32 exp_statsn; + uint8_t rsvd5[16]; +}; + +/* Login PDU flags */ +#define ISCSI_FLAG_LOGIN_TRANSIT 0x80 +#define ISCSI_FLAG_LOGIN_CONTINUE 0x40 +#define ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK 0x0C /* 2 bits */ +#define ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK 0x03 /* 2 bits */ + +#define ISCSI_LOGIN_CURRENT_STAGE(flags) \ + ((flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2) +#define ISCSI_LOGIN_NEXT_STAGE(flags) \ + (flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK) + +/* Login Response Header */ +struct iscsi_login_rsp { + uint8_t opcode; + uint8_t flags; + uint8_t max_version; /* Max. version supported */ + uint8_t active_version; /* Active version */ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t isid[6]; /* Initiator Session ID */ + __be16 tsih; /* Target Session Handle */ + itt_t itt; /* Initiator Task Tag */ + __be32 rsvd3; + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + uint8_t status_class; /* see Login RSP ststus classes below */ + uint8_t status_detail; /* see Login RSP Status details below */ + uint8_t rsvd4[10]; +}; + +/* Login stage (phase) codes for CSG, NSG */ +#define ISCSI_INITIAL_LOGIN_STAGE -1 +#define ISCSI_SECURITY_NEGOTIATION_STAGE 0 +#define ISCSI_OP_PARMS_NEGOTIATION_STAGE 1 +#define ISCSI_FULL_FEATURE_PHASE 3 + +/* Login Status response classes */ +#define ISCSI_STATUS_CLS_SUCCESS 0x00 +#define ISCSI_STATUS_CLS_REDIRECT 0x01 +#define ISCSI_STATUS_CLS_INITIATOR_ERR 0x02 +#define ISCSI_STATUS_CLS_TARGET_ERR 0x03 + +/* Login Status response detail codes */ +/* Class-0 (Success) */ +#define ISCSI_LOGIN_STATUS_ACCEPT 0x00 + +/* Class-1 (Redirection) */ +#define ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP 0x01 +#define ISCSI_LOGIN_STATUS_TGT_MOVED_PERM 0x02 + +/* Class-2 (Initiator Error) */ +#define ISCSI_LOGIN_STATUS_INIT_ERR 0x00 +#define ISCSI_LOGIN_STATUS_AUTH_FAILED 0x01 +#define ISCSI_LOGIN_STATUS_TGT_FORBIDDEN 0x02 +#define ISCSI_LOGIN_STATUS_TGT_NOT_FOUND 0x03 +#define ISCSI_LOGIN_STATUS_TGT_REMOVED 0x04 +#define ISCSI_LOGIN_STATUS_NO_VERSION 0x05 +#define ISCSI_LOGIN_STATUS_ISID_ERROR 0x06 +#define ISCSI_LOGIN_STATUS_MISSING_FIELDS 0x07 +#define ISCSI_LOGIN_STATUS_CONN_ADD_FAILED 0x08 +#define ISCSI_LOGIN_STATUS_NO_SESSION_TYPE 0x09 +#define ISCSI_LOGIN_STATUS_NO_SESSION 0x0a +#define ISCSI_LOGIN_STATUS_INVALID_REQUEST 0x0b + +/* Class-3 (Target Error) */ +#define ISCSI_LOGIN_STATUS_TARGET_ERROR 0x00 +#define ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE 0x01 +#define ISCSI_LOGIN_STATUS_NO_RESOURCES 0x02 + +/* Logout Header */ +struct iscsi_logout { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd1[2]; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd2[8]; + itt_t itt; /* Initiator Task Tag */ + __be16 cid; + uint8_t rsvd3[2]; + __be32 cmdsn; + __be32 exp_statsn; + uint8_t rsvd4[16]; +}; + +/* Logout PDU flags */ +#define ISCSI_FLAG_LOGOUT_REASON_MASK 0x7F + +/* logout reason_code values */ + +#define ISCSI_LOGOUT_REASON_CLOSE_SESSION 0 +#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION 1 +#define ISCSI_LOGOUT_REASON_RECOVERY 2 +#define ISCSI_LOGOUT_REASON_AEN_REQUEST 3 + +/* Logout Response Header */ +struct iscsi_logout_rsp { + uint8_t opcode; + uint8_t flags; + uint8_t response; /* see Logout response values below */ + uint8_t rsvd2; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd3[8]; + itt_t itt; /* Initiator Task Tag */ + __be32 rsvd4; + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + __be32 rsvd5; + __be16 t2wait; + __be16 t2retain; + __be32 rsvd6; +}; + +/* logout response status values */ + +#define ISCSI_LOGOUT_SUCCESS 0 +#define ISCSI_LOGOUT_CID_NOT_FOUND 1 +#define ISCSI_LOGOUT_RECOVERY_UNSUPPORTED 2 +#define ISCSI_LOGOUT_CLEANUP_FAILED 3 + +/* SNACK Header */ +struct iscsi_snack { + uint8_t opcode; + uint8_t flags; + uint8_t rsvd2[14]; + itt_t itt; + __be32 begrun; + __be32 runlength; + __be32 exp_statsn; + __be32 rsvd3; + __be32 exp_datasn; + uint8_t rsvd6[8]; +}; + +/* SNACK PDU flags */ +#define ISCSI_FLAG_SNACK_TYPE_MASK 0x0F /* 4 bits */ + +/* Reject Message Header */ +struct iscsi_reject { + uint8_t opcode; + uint8_t flags; + uint8_t reason; + uint8_t rsvd2; + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd3[8]; + __be32 ffffffff; + uint8_t rsvd4[4]; + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; + __be32 datasn; + uint8_t rsvd5[8]; + /* Text - Rejected hdr */ +}; + +/* Reason for Reject */ +#define ISCSI_REASON_CMD_BEFORE_LOGIN 1 +#define ISCSI_REASON_DATA_DIGEST_ERROR 2 +#define ISCSI_REASON_DATA_SNACK_REJECT 3 +#define ISCSI_REASON_PROTOCOL_ERROR 4 +#define ISCSI_REASON_CMD_NOT_SUPPORTED 5 +#define ISCSI_REASON_IMM_CMD_REJECT 6 +#define ISCSI_REASON_TASK_IN_PROGRESS 7 +#define ISCSI_REASON_INVALID_SNACK 8 +#define ISCSI_REASON_BOOKMARK_INVALID 9 +#define ISCSI_REASON_BOOKMARK_NO_RESOURCES 10 +#define ISCSI_REASON_NEGOTIATION_RESET 11 + +/* Max. number of Key=Value pairs in a text message */ +#define MAX_KEY_VALUE_PAIRS 8192 + +/* maximum length for text keys/values */ +#define KEY_MAXLEN 64 +#define VALUE_MAXLEN 255 +#define TARGET_NAME_MAXLEN VALUE_MAXLEN + +#define ISCSI_DEF_MAX_RECV_SEG_LEN 8192 +#define ISCSI_MIN_MAX_RECV_SEG_LEN 512 +#define ISCSI_MAX_MAX_RECV_SEG_LEN 16777215 + +#define ISCSI_DEF_FIRST_BURST_LEN 65536 +#define ISCSI_MIN_FIRST_BURST_LEN 512 +#define ISCSI_MAX_FIRST_BURST_LEN 16777215 + +#define ISCSI_DEF_MAX_BURST_LEN 262144 +#define ISCSI_MIN_MAX_BURST_LEN 512 +#define ISCSI_MAX_MAX_BURST_LEN 16777215 + +#define ISCSI_DEF_TIME2WAIT 2 + +/************************* RFC 3720 End *****************************/ + +#endif /* ISCSI_PROTO_H */ diff --git a/brcm_iscsi_uio/include/list.h b/brcm_iscsi_uio/include/list.h new file mode 100644 index 0000000..bbf3425 --- /dev/null +++ b/brcm_iscsi_uio/include/list.h @@ -0,0 +1,93 @@ +#ifndef __LIST_H__ +#define __LIST_H__ + +#include +/* taken from linux kernel */ + +#undef offsetof +#ifdef __compiler_offsetof +#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) +#else +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = entry->prev = NULL; +} + +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +#endif diff --git a/brcm_iscsi_uio/include/mgmt_ipc.h b/brcm_iscsi_uio/include/mgmt_ipc.h new file mode 100644 index 0000000..94827ea --- /dev/null +++ b/brcm_iscsi_uio/include/mgmt_ipc.h @@ -0,0 +1,147 @@ +/* + * iSCSI Daemon/Admin Management IPC + * + * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman + * maintained by open-iscsi@googlegroups.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ +#ifndef MGMT_IPC_H +#define MGMT_IPC_H + +//#include "types.h" +#include "iscsi_if.h" +#include "config.h" + +#define ISCSIADM_NAMESPACE "ISCSIADM_ABSTRACT_NAMESPACE" +#define PEERUSER_MAX 64 + +typedef enum mgmt_ipc_err { + MGMT_IPC_OK = 0, + MGMT_IPC_ERR = 1, + MGMT_IPC_ERR_NOT_FOUND = 2, + MGMT_IPC_ERR_NOMEM = 3, + MGMT_IPC_ERR_TRANS_FAILURE = 4, + MGMT_IPC_ERR_LOGIN_FAILURE = 5, + MGMT_IPC_ERR_IDBM_FAILURE = 6, + MGMT_IPC_ERR_INVAL = 7, + MGMT_IPC_ERR_TRANS_TIMEOUT = 8, + MGMT_IPC_ERR_INTERNAL = 9, + MGMT_IPC_ERR_LOGOUT_FAILURE = 10, + MGMT_IPC_ERR_PDU_TIMEOUT = 11, + MGMT_IPC_ERR_TRANS_NOT_FOUND = 12, + MGMT_IPC_ERR_ACCESS = 13, + MGMT_IPC_ERR_TRANS_CAPS = 14, + MGMT_IPC_ERR_EXISTS = 15, + MGMT_IPC_ERR_INVALID_REQ = 16, + MGMT_IPC_ERR_ISNS_UNAVAILABLE = 17, + MGMT_IPC_ERR_ISCSID_COMM_ERR = 18, + MGMT_IPC_ERR_FATAL_LOGIN_FAILURE = 19, + MGMT_IPC_ERR_ISCSID_NOTCONN = 20, +} mgmt_ipc_err_e; + +typedef enum iscsiadm_cmd { + MGMT_IPC_UNKNOWN = 0, + MGMT_IPC_SESSION_LOGIN = 1, + MGMT_IPC_SESSION_LOGOUT = 2, + MGMT_IPC_SESSION_ACTIVESTAT = 4, + MGMT_IPC_CONN_ADD = 5, + MGMT_IPC_CONN_REMOVE = 6, + MGMT_IPC_SESSION_STATS = 7, + MGMT_IPC_CONFIG_INAME = 8, + MGMT_IPC_CONFIG_IALIAS = 9, + MGMT_IPC_CONFIG_FILE = 10, + MGMT_IPC_IMMEDIATE_STOP = 11, + MGMT_IPC_SESSION_SYNC = 12, + MGMT_IPC_SESSION_INFO = 13, + MGMT_IPC_ISNS_DEV_ATTR_QUERY = 14, + MGMT_IPC_SEND_TARGETS = 15, + MGMT_IPC_SET_HOST_PARAM = 16, + MGMT_IPC_NOTIFY_ADD_NODE = 17, + MGMT_IPC_NOTIFY_DEL_NODE = 18, + MGMT_IPC_NOTIFY_ADD_PORTAL = 19, + MGMT_IPC_NOTIFY_DEL_PORTAL = 20, + MGMT_IPC_GET_IPADDR = 21, + + __MGMT_IPC_MAX_COMMAND +} iscsiadm_cmd_e; + +/* IPC Request */ +typedef struct iscsiadm_req { + iscsiadm_cmd_e command; + uint32_t payload_len; + + union { + /* messages */ + struct ipc_msg_session { + int sid; +// node_rec_t rec; + } session; + struct ipc_msg_conn { + int sid; + int cid; + } conn; + struct ipc_msg_send_targets { + int host_no; + int do_login; + struct sockaddr_storage ss; + } st; + struct ipc_msg_set_host_param { + int host_no; + int param; + /* TODO: make this variable len to support */ + char value[IFNAMSIZ + 1]; + + } set_host_param; + } u; +} iscsiadm_req_t; + +/* IPC Response */ +typedef struct iscsiadm_rsp { + iscsiadm_cmd_e command; + mgmt_ipc_err_e err; + + union { +#define MGMT_IPC_GETSTATS_BUF_MAX (sizeof(struct iscsi_uevent) + \ + sizeof(struct iscsi_stats) + \ + sizeof(struct iscsi_stats_custom) * \ + ISCSI_STATS_CUSTOM_MAX) + struct ipc_msg_getstats { + struct iscsi_uevent ev; + struct iscsi_stats stats; + char custom[sizeof(struct iscsi_stats_custom) * + ISCSI_STATS_CUSTOM_MAX]; + } getstats; + struct ipc_msg_config { + char var[VALUE_MAXLEN]; + } config; + struct ipc_msg_session_state { + int session_state; + int conn_state; + } session_state; + struct ipc_msg_get_ipaddr { + int addr; + } get_ipaddr; + } u; +} iscsiadm_rsp_t; + +struct queue_task; +typedef mgmt_ipc_err_e mgmt_ipc_fn_t(struct queue_task *); + +struct queue_task; +void mgmt_ipc_write_rsp(struct queue_task *qtask, mgmt_ipc_err_e err); +int mgmt_ipc_listen(void); +void mgmt_ipc_close(int fd); +void mgmt_ipc_handle(int accept_fd); + +#endif /* MGMT_IPC_H */ diff --git a/brcm_iscsi_uio/include/sysdeps.h b/brcm_iscsi_uio/include/sysdeps.h new file mode 100644 index 0000000..2f18e3b --- /dev/null +++ b/brcm_iscsi_uio/include/sysdeps.h @@ -0,0 +1,27 @@ +/* + * wrapping of libc features and kernel interfaces + * + * Copyright (C) 2005-2006 Kay Sievers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _SYSDEPS_H_ +#define _SYSDEPS_H_ + +extern size_t strlcpy(char *dst, const char *src, size_t size); +extern size_t strlcat(char *dst, const char *src, size_t size); + +#endif diff --git a/brcm_iscsi_uio/include/uip_mgmt_ipc.h b/brcm_iscsi_uio/include/uip_mgmt_ipc.h new file mode 100644 index 0000000..79c9b79 --- /dev/null +++ b/brcm_iscsi_uio/include/uip_mgmt_ipc.h @@ -0,0 +1,64 @@ +/* + * uIP iSCSI Daemon/Admin Management IPC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ +#ifndef UIP_MGMT_IPC_H +#define UIP_MGMT_IPC_H + +//#include "types.h" +#include "iscsi_if.h" +#include "config.h" +#include "mgmt_ipc.h" + +#define ISCSID_UIP_NAMESPACE "ISCSID_UIP_ABSTRACT_NAMESPACE" + +typedef enum iscsid_uip_cmd { + ISCSID_UIP_IPC_UNKNOWN = 0, + ISCSID_UIP_IPC_GET_IFACE = 1, + + __ISCSID_UIP_IPC_MAX_COMMAND +} iscsid_uip_cmd_e; + + +typedef struct iscsid_uip_broadcast_header { + iscsid_uip_cmd_e command; + uint32_t payload_len; +} iscsid_uip_broadcast_header_t; + +/* IPC Request */ +typedef struct iscsid_uip_broadcast { + struct iscsid_uip_broadcast_header header; + + union { + /* messages */ + struct ipc_broadcast_iface_rec { + struct iface_rec rec; + } iface_rec; + } u; +} iscsid_uip_broadcast_t; + +typedef enum iscsid_uip_mgmt_ipc_err { + ISCSID_UIP_MGMT_IPC_OK = 0, + ISCISD_UIP_MGMT_IPC_ERR = 1, + ISCISD_UIP_MGMT_IPC_ERR_NOT_FOUND = 2, + ISCISD_UIP_MGMT_IPC_ERR_NOMEM = 3, +} iscsid_uip_mgmt_ipc_err_e; + +/* IPC Response */ +typedef struct iscsid_uip_mgmt_rsp { + iscsid_uip_cmd_e command; + iscsid_uip_mgmt_ipc_err_e err; +} iscsid_uip_rsp_t; + +#endif /* UIP_MGMT_IPC_H */ diff --git a/brcm_iscsi_uio/src/Makefile.am b/brcm_iscsi_uio/src/Makefile.am new file mode 100644 index 0000000..44b0085 --- /dev/null +++ b/brcm_iscsi_uio/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = apps uip unix diff --git a/brcm_iscsi_uio/src/README b/brcm_iscsi_uio/src/README new file mode 100644 index 0000000..b501538 --- /dev/null +++ b/brcm_iscsi_uio/src/README @@ -0,0 +1,13 @@ +uIP is a very small implementation of the TCP/IP stack that is written +by Adam Dunkels . More information can be obtained +at the uIP homepage at http://www.sics.se/~adam/uip/. + +This is version $Name: uip-1-0 $. + +The directory structure look as follows: + +apps/ - Example applications +doc/ - Documentation +lib/ - Library code used by some applications +uip/ - uIP TCP/IP stack code +unix/ - uIP as a user space process under FreeBSD or Linux diff --git a/brcm_iscsi_uio/src/apps/Makefile.am b/brcm_iscsi_uio/src/apps/Makefile.am new file mode 100644 index 0000000..08ed18d --- /dev/null +++ b/brcm_iscsi_uio/src/apps/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = dhcpc brcm-iscsi diff --git a/brcm_iscsi_uio/src/apps/README b/brcm_iscsi_uio/src/apps/README new file mode 100644 index 0000000..0096c4e --- /dev/null +++ b/brcm_iscsi_uio/src/apps/README @@ -0,0 +1,2 @@ +This directory contains a few example applications. They are not all +heavily tested, however. diff --git a/brcm_iscsi_uio/src/apps/brcm-iscsi/Makefile.am b/brcm_iscsi_uio/src/apps/brcm-iscsi/Makefile.am new file mode 100644 index 0000000..1b30671 --- /dev/null +++ b/brcm_iscsi_uio/src/apps/brcm-iscsi/Makefile.am @@ -0,0 +1,12 @@ +INCLUDES = -I${top_srcdir}/src/unix \ + -I${top_srcdir}/src/uip \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/src/apps/brcm-iscsi \ + -I${top_srcdir}/include + +noinst_LIBRARIES = libbrcm_apps_brcm_iscsi.a + +libbrcm_apps_brcm_iscsi_a_SOURCES = brcm_iscsi.c + +libbrcm_apps_brcm_iscsi_a_CFLAGS = $(AM_CFLAGS) \ + -DBYTE_ORDER=@ENDIAN@ diff --git a/brcm_iscsi_uio/src/apps/brcm-iscsi/Makefile.brcm-iscsi b/brcm_iscsi_uio/src/apps/brcm-iscsi/Makefile.brcm-iscsi new file mode 100644 index 0000000..732275f --- /dev/null +++ b/brcm_iscsi_uio/src/apps/brcm-iscsi/Makefile.brcm-iscsi @@ -0,0 +1 @@ +APP_SOURCES += brcm-iscsi.c diff --git a/brcm_iscsi_uio/src/apps/brcm-iscsi/brcm_iscsi.c b/brcm_iscsi_uio/src/apps/brcm-iscsi/brcm_iscsi.c new file mode 100644 index 0000000..8ac75cc --- /dev/null +++ b/brcm_iscsi_uio/src/apps/brcm-iscsi/brcm_iscsi.c @@ -0,0 +1,53 @@ +/** + * \addtogroup brcm-iscsi + * @{ + */ + +/** + * \file + * An example of how to write uIP applications + * with protosockets. + * \author + * Adam Dunkels + */ + +/* + * This is a short example of how to write uIP applications using + * protosockets. + */ + +/* + * We define the application state (struct hello_world_state) in the + * hello-world.h file, so we need to include it here. We also include + * uip.h (since this cannot be included in hello-world.h) and + * , since we use the memcpy() function in the code. + */ +#include "brcm_iscsi.h" +#include "uip.h" +#include +#include + +#include "uip_arp.h" + +/*---------------------------------------------------------------------------*/ +/* + * The initialization function. We must explicitly call this function + * from the system initialization code, some time after uip_init() is + * called. + */ +void +brcm_iscsi_init(void) +{ +} +/*---------------------------------------------------------------------------*/ +/* + * In hello-world.h we have defined the UIP_APPCALL macro to + * hello_world_appcall so that this funcion is uIP's application + * function. This function is called whenever an uIP event occurs + * (e.g. when a new connection is established, new data arrives, sent + * data is acknowledged, data needs to be retransmitted, etc.). + */ +void +brcm_iscsi_appcall(struct uip_stack *ustack) +{ +} diff --git a/brcm_iscsi_uio/src/apps/brcm-iscsi/brcm_iscsi.h b/brcm_iscsi_uio/src/apps/brcm-iscsi/brcm_iscsi.h new file mode 100644 index 0000000..94a67ae --- /dev/null +++ b/brcm_iscsi_uio/src/apps/brcm-iscsi/brcm_iscsi.h @@ -0,0 +1,54 @@ +/** + * \addtogroup apps + * @{ + */ + +/** + * \defgroup helloworld Hello, world + * @{ + * + * A small example showing how to write applications with + * \ref psock "protosockets". + */ + +/** + * \file + * Header file for an example of how to write uIP applications + * with protosockets. + * \author + * Benjamin Li + */ + +#ifndef __BRCM_ISCSI_H__ +#define __BRCM_ISCSI_H__ + +/* Since this file will be included by uip.h, we cannot include uip.h + here. But we might need to include uipopt.h if we need the u8_t and + u16_t datatypes. */ +#include "uipopt.h" +#include "uip.h" +#include "psock.h" + +/* Next, we define the uip_tcp_appstate_t datatype. This is the state + of our application, and the memory required for this state is + allocated together with each TCP connection. One application state + for each TCP connection. */ +typedef struct hello_world_state { + struct psock p; + u8_t inputbuffer[32]; + u8_t name[40]; + + struct uip_udp_conn *conn; +} uip_tcp_appstate_t; + +/* Finally we define the application function to be called by uIP. */ +void brcm_iscsi_appcall(struct uip_stack *ustack); +#ifndef UIP_APPCALL +#define UIP_APPCALL brcm_iscsi_appcall +#endif /* UIP_APPCALL */ + +void brcm_iscsi_init(void); + +#endif /* __BRCM_ISCSI_H__ */ +/** @} */ +/** @} */ diff --git a/brcm_iscsi_uio/src/apps/dhcpc/Makefile.am b/brcm_iscsi_uio/src/apps/dhcpc/Makefile.am new file mode 100644 index 0000000..936c7f1 --- /dev/null +++ b/brcm_iscsi_uio/src/apps/dhcpc/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = -I${top_srcdir}/src/unix \ + -I${top_srcdir}/src/uip \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/src/apps/brcm-iscsi \ + -I${top_srcdir}/include + +noinst_LIBRARIES = libbrcm_apps_dhcpc.a + +libbrcm_apps_dhcpc_a_SOURCES = dhcpc.c + +libbrcm_apps_dhcpc_a_CFLAGS = $(AM_CFLAGS) \ + -DBYTE_ORDER=@ENDIAN@ + + diff --git a/brcm_iscsi_uio/src/apps/dhcpc/Makefile.dhcpc b/brcm_iscsi_uio/src/apps/dhcpc/Makefile.dhcpc new file mode 100644 index 0000000..f84c84f --- /dev/null +++ b/brcm_iscsi_uio/src/apps/dhcpc/Makefile.dhcpc @@ -0,0 +1 @@ +APP_SOURCES += dhcpc.c timer.c diff --git a/brcm_iscsi_uio/src/apps/dhcpc/dhcpc.c b/brcm_iscsi_uio/src/apps/dhcpc/dhcpc.c new file mode 100644 index 0000000..425eb51 --- /dev/null +++ b/brcm_iscsi_uio/src/apps/dhcpc/dhcpc.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * @(#)$Id: dhcpc.c,v 1.2 2006/06/11 21:46:37 adam Exp $ + */ + +#include +#include +#include +#include +#include +#include + +#include "uip.h" +#include "dhcpc.h" +#include "timer.h" +#include "pt.h" + +#include "debug.h" +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" + +struct __attribute__ ((__packed__)) dhcp_msg { + u8_t op, htype, hlen, hops; + u8_t xid[4]; + u16_t secs, flags; + u8_t ciaddr[4]; + u8_t yiaddr[4]; + u8_t siaddr[4]; + u8_t giaddr[4]; + u8_t chaddr[16]; +#ifndef UIP_CONF_DHCP_LIGHT + u8_t sname[64]; + u8_t file[128]; +#endif + u8_t options[312]; +}; + +#define BOOTP_BROADCAST 0x8000 + +#define DHCP_REQUEST 1 +#define DHCP_REPLY 2 +#define DHCP_HTYPE_ETHERNET 1 +#define DHCP_HLEN_ETHERNET 6 +#define DHCP_MSG_LEN 236 + +#define DHCPC_SERVER_PORT 67 +#define DHCPC_CLIENT_PORT 68 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 + +#define DHCP_OPTION_SUBNET_MASK 1 +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_REQ_IPADDR 50 +#define DHCP_OPTION_LEASE_TIME 51 +#define DHCP_OPTION_MSG_TYPE 53 +#define DHCP_OPTION_SERVER_ID 54 +#define DHCP_OPTION_REQ_LIST 55 +#define DHCP_OPTION_END 255 + +static u8_t xid[4] = { 0xad, 0xde, 0x12, 0x23 }; +static const u8_t magic_cookie[4] = { 99, 130, 83, 99 }; + +struct dhcpc_options dhcpc_opt = { + .enable_random_xid = 1, +}; + +/*---------------------------------------------------------------------------*/ +static u8_t *add_msg_type(u8_t * optptr, u8_t type) +{ + *optptr++ = DHCP_OPTION_MSG_TYPE; + *optptr++ = 1; + *optptr++ = type; + return optptr; +} + +/*---------------------------------------------------------------------------*/ +static u8_t *add_server_id(struct dhcpc_state *s, u8_t * optptr) +{ + *optptr++ = DHCP_OPTION_SERVER_ID; + *optptr++ = 4; + memcpy(optptr, s->serverid, 4); + return optptr + 4; +} + +/*---------------------------------------------------------------------------*/ +static u8_t *add_req_ipaddr(struct dhcpc_state *s, u8_t * optptr) +{ + *optptr++ = DHCP_OPTION_REQ_IPADDR; + *optptr++ = 4; + memcpy(optptr, s->ipaddr, 4); + return optptr + 4; +} + +/*---------------------------------------------------------------------------*/ +static u8_t *add_req_options(u8_t * optptr) +{ + *optptr++ = DHCP_OPTION_REQ_LIST; + *optptr++ = 3; + *optptr++ = DHCP_OPTION_SUBNET_MASK; + *optptr++ = DHCP_OPTION_ROUTER; + *optptr++ = DHCP_OPTION_DNS_SERVER; + return optptr; +} + +/*---------------------------------------------------------------------------*/ +static u8_t *add_end(u8_t * optptr) +{ + *optptr++ = DHCP_OPTION_END; + return optptr; +} + +/*---------------------------------------------------------------------------*/ +static void create_msg(struct dhcpc_state *s, struct dhcp_msg *m) +{ + m->op = DHCP_REQUEST; + m->htype = DHCP_HTYPE_ETHERNET; + m->hlen = s->mac_len; + m->hops = 0; + memcpy(m->xid, xid, sizeof(m->xid)); + m->secs = 0; + m->flags = const_htons(BOOTP_BROADCAST); /* Broadcast bit. */ + /* uip_ipaddr_copy(m->ciaddr, uip_hostaddr); */ + memcpy(m->ciaddr, s->ustack->hostaddr, sizeof(m->ciaddr)); + memset(m->yiaddr, 0, sizeof(m->yiaddr)); + memset(m->siaddr, 0, sizeof(m->siaddr)); + memset(m->giaddr, 0, sizeof(m->giaddr)); + memcpy(m->chaddr, s->mac_addr, s->mac_len); + memset(&m->chaddr[s->mac_len], 0, sizeof(m->chaddr) - s->mac_len); +#ifndef UIP_CONF_DHCP_LIGHT + memset(m->sname, 0, sizeof(m->sname)); + memset(m->file, 0, sizeof(m->file)); +#endif + + memcpy(m->options, magic_cookie, sizeof(magic_cookie)); +} + +/*---------------------------------------------------------------------------*/ +static void send_discover(struct dhcpc_state *s) +{ + u8_t *end; + struct dhcp_msg *m = (struct dhcp_msg *)s->ustack->uip_appdata; + + create_msg(s, m); + + end = add_msg_type(&m->options[4], DHCPDISCOVER); + end = add_req_options(end); + end = add_end(end); + + uip_send(s->ustack, s->ustack->uip_appdata, + end - (u8_t *) s->ustack->uip_appdata); +} + +/*---------------------------------------------------------------------------*/ +static void send_request(struct dhcpc_state *s) +{ + u8_t *end; + struct dhcp_msg *m = (struct dhcp_msg *)s->ustack->uip_appdata; + + create_msg(s, m); + + end = add_msg_type(&m->options[4], DHCPREQUEST); + end = add_server_id(s, end); + end = add_req_ipaddr(s, end); + end = add_end(end); + + uip_send(s->ustack, s->ustack->uip_appdata, + end - (u8_t *) s->ustack->uip_appdata); +} + +/*---------------------------------------------------------------------------*/ +static u8_t parse_options(struct dhcpc_state *s, u8_t * optptr, int len) +{ + u8_t *end = optptr + len; + u8_t type = 0; + + while (optptr < end) { + switch (*optptr) { + case DHCP_OPTION_SUBNET_MASK: + memcpy(s->netmask, optptr + 2, 4); + break; + case DHCP_OPTION_ROUTER: + memcpy(s->default_router, optptr + 2, 4); + break; + case DHCP_OPTION_DNS_SERVER: + memcpy(s->dnsaddr, optptr + 2, 4); + break; + case DHCP_OPTION_MSG_TYPE: + type = *(optptr + 2); + break; + case DHCP_OPTION_SERVER_ID: + memcpy(s->serverid, optptr + 2, 4); + break; + case DHCP_OPTION_LEASE_TIME: + memcpy(s->lease_time, optptr + 2, 4); + break; + case DHCP_OPTION_END: + return type; + } + + optptr += optptr[1] + 2; + } + return type; +} + +/*---------------------------------------------------------------------------*/ +static u8_t parse_msg(struct dhcpc_state *s) +{ + struct dhcp_msg *m = (struct dhcp_msg *)s->ustack->uip_appdata; + + if (m->op == DHCP_REPLY && + memcmp(m->xid, xid, sizeof(xid)) == 0 && + memcmp(m->chaddr, s->mac_addr, s->mac_len) == 0) { + memcpy(s->ipaddr, m->yiaddr, 4); + return parse_options(s, &m->options[4], uip_datalen(s->ustack)); + } + return 0; +} + +/*---------------------------------------------------------------------------*/ +static PT_THREAD(handle_dhcp(struct uip_stack *ustack)) +{ + struct dhcpc_state *s; + s = ustack->dhcpc; + + PT_BEGIN(&s->pt); + + if(s == NULL) + { + LOG_WARN("Could not find dhcpc state"); + goto dhcpc_state_null; + } + + /* try_again: */ + s->state = STATE_SENDING; + s->ticks = CLOCK_SECOND; + + do { + send_discover(s); + timer_set(&s->timer, s->ticks); + PT_WAIT_UNTIL(&s->pt, uip_newdata(s->ustack) + || timer_expired(&s->timer)); + + if (uip_newdata(s->ustack) && parse_msg(s) == DHCPOFFER) { + s->state = STATE_OFFER_RECEIVED; + break; + } + + if (s->ticks < CLOCK_SECOND * 60) { + s->ticks *= 2; + } + } while (s->state != STATE_OFFER_RECEIVED); + + s->ticks = CLOCK_SECOND; + + do { + send_request(s); + timer_set(&s->timer, s->ticks); + s->ustack->uip_flags &= ~UIP_NEWDATA; + PT_WAIT_UNTIL(&s->pt, uip_newdata(s->ustack) + || timer_expired(&s->timer)); + + if (uip_newdata(s->ustack) && parse_msg(s) == DHCPACK) { + s->state = STATE_CONFIG_RECEIVED; + break; + } + + if (s->ticks <= CLOCK_SECOND * 10) { + s->ticks += CLOCK_SECOND; + } else { + PT_RESTART(&s->pt); + } + } while (s->state != STATE_CONFIG_RECEIVED); + + LOG_INFO("Got IP address %d.%d.%d.%d", + uip_ipaddr1(s->ipaddr), uip_ipaddr2(s->ipaddr), + uip_ipaddr3(s->ipaddr), uip_ipaddr4(s->ipaddr)); + LOG_INFO("Got netmask %d.%d.%d.%d", + uip_ipaddr1(s->netmask), uip_ipaddr2(s->netmask), + uip_ipaddr3(s->netmask), uip_ipaddr4(s->netmask)); + LOG_INFO("Got DNS server %d.%d.%d.%d", + uip_ipaddr1(s->dnsaddr), uip_ipaddr2(s->dnsaddr), + uip_ipaddr3(s->dnsaddr), uip_ipaddr4(s->dnsaddr)); + LOG_INFO("Got default router %d.%d.%d.%d", + uip_ipaddr1(s->default_router), uip_ipaddr2(s->default_router), + uip_ipaddr3(s->default_router), uip_ipaddr4(s->default_router)); + LOG_INFO("Lease expires in %ld seconds", + ntohs(s->lease_time[0]) * 65536ul + ntohs(s->lease_time[1])); + + s->last_update = time(NULL); + + set_uip_stack(s->ustack, + (uip_ip4addr_t *) s->ipaddr, + (uip_ip4addr_t *) s->netmask, + (uip_ip4addr_t *) s->default_router, + (uint8_t *) s->mac_addr); + + /* Put the stack thread back into a long sleep */ + s->nic->state |= NIC_LONG_SLEEP; + + /* timer_stop(&s.timer); */ + + /* + * PT_END restarts the thread so we do this instead. Eventually we + * should reacquire expired leases here. + */ + +dhcpc_state_null: + + while (1) { + PT_YIELD(&s->pt); + } + + PT_END(&(s->pt)); +} + +/*---------------------------------------------------------------------------*/ +int dhcpc_init(nic_t *nic, struct uip_stack *ustack, + const void *mac_addr, int mac_len) +{ + uip_ip4addr_t addr; + + struct dhcpc_state *s = malloc(sizeof(*s)); + if(s == NULL) + { + LOG_ERR("Couldn't allocate size for dhcpc info"); + return -ENOMEM; + } + + memset(s, 0, sizeof(*s)); + s->nic = nic; + s->ustack = ustack; + s->mac_addr = mac_addr; + s->mac_len = mac_len; + s->state = STATE_INITIAL; + + /* Initialize XID to randomly */ + if (dhcpc_opt.enable_random_xid == 1) { + u32_t gen_xid; + gen_xid = random(); + memcpy(xid, &gen_xid, sizeof(gen_xid)); + } + + s->state = STATE_INITIAL; + uip_ipaddr(addr, 255, 255, 255, 255); + s->conn = uip_udp_new(ustack, &addr, const_htons(DHCPC_SERVER_PORT)); + if (s->conn != NULL) { + uip_udp_bind(s->conn, const_htons(DHCPC_CLIENT_PORT)); + } + + ustack->dhcpc = s; + + /* Let the RX poll value take over */ + nic->state &= ~NIC_LONG_SLEEP; + + PT_INIT(&s->pt); + + return 0; +} + +/*---------------------------------------------------------------------------*/ +void dhcpc_appcall(struct uip_stack *ustack) +{ + handle_dhcp(ustack); +} + +/*---------------------------------------------------------------------------*/ +void dhcpc_request(struct uip_stack *ustack) +{ + struct dhcpc_state *s = ustack->dhcpc; + + if (s != NULL && + s->state == STATE_INITIAL) { + handle_dhcp(ustack); + } +} + +/*---------------------------------------------------------------------------*/ diff --git a/brcm_iscsi_uio/src/apps/dhcpc/dhcpc.h b/brcm_iscsi_uio/src/apps/dhcpc/dhcpc.h new file mode 100644 index 0000000..81f8aa0 --- /dev/null +++ b/brcm_iscsi_uio/src/apps/dhcpc/dhcpc.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * @(#)$Id: dhcpc.h,v 1.3 2006/06/11 21:46:37 adam Exp $ + */ +#ifndef __DHCPC_H__ +#define __DHCPC_H__ + +#include + +#include "nic.h" +#include "timer.h" +#include "pt.h" +#include "uip.h" + +#define STATE_INITIAL 0 +#define STATE_SENDING 1 +#define STATE_OFFER_RECEIVED 2 +#define STATE_CONFIG_RECEIVED 3 + +struct dhcpc_state { + struct pt pt; + + nic_t *nic; + struct uip_stack *ustack; + char state; + struct uip_udp_conn *conn; + struct timer timer; + u16_t ticks; + const void *mac_addr; + int mac_len; + + u8_t serverid[4]; + + u16_t lease_time[2]; + u16_t ipaddr[2]; + u16_t netmask[2]; + u16_t dnsaddr[2]; + u16_t default_router[2]; + + time_t last_update; +}; + +struct dhcpc_options { + u8_t enable_random_xid; + u8_t xid[4]; +}; + +int dhcpc_init(nic_t *nic, struct uip_stack *ustack, + const void *mac_addr, int mac_len); +void dhcpc_request(struct uip_stack *ustack); + +void dhcpc_appcall(struct uip_stack *ustack); + +void dhcpc_configured(const struct dhcpc_state *s); + +typedef struct dhcpc_state uip_udp_appstate_t; +#define UIP_UDP_APPCALL dhcpc_appcall + +#endif /* __DHCPC_H__ */ diff --git a/brcm_iscsi_uio/src/uip-1.0-changelog.txt b/brcm_iscsi_uio/src/uip-1.0-changelog.txt new file mode 100644 index 0000000..1e6c61c --- /dev/null +++ b/brcm_iscsi_uio/src/uip-1.0-changelog.txt @@ -0,0 +1,98 @@ +* A new API: protosockets that are similar to BSD sockets but does not + require any underlying multithreading system. + +* Very rudimentary IPv6 support + +* New application: DHCP client. Web server rewritten with protosockets. + +* Removed uIP zero-copy functionality in order to simplify uIP device + driver coding: outbound packets are now *always* stored in full in + the uip_buf buffer. + +* Checksum computation is now part of uip.c, but it still is possible + to implement them in assembly code by specifying a configuration + option. Checksum code now runs on architectures with 2-byte alignment. + +* Added TCP persistent timer. + +* Made all IP address representations use the new uip_ipaddr_ip + datatype for clarity. + +* Updated window behavior so that sending to a host with a small open + window works better now. + +* UDP API change: uip_udp_new() now takes port numbers in network byte + order like TCP functions. + +* Allow reception of packets when no IP address is configured to make + DHCP work. + +* Moved Ethernet address into main uIP module from ARP module. + +* Made constants explicit #defines and moved them out of the code + (header sizes, TCP options, TCP header length field). + +* If uip_len is less than that reported by the IP header, the packet + is discarded. If uip_len is greater than the length reported by the + IP header, uip_len is adjusted. + +* Moved header size definitions into header file. + +* Added uIP call for polling an application without triggering any + timer events. Removed redundant assignments of uip_len and uip_slen. + +* Removed compiler warning about icmp_input label being defined when + UIP_PINGADDRCONF was not used. + +* Added UIP_APPDATA_SIZE macro that holds the available buffer size + for user data. + +* Added uip_udp_bind() call. + +* Moved checksum code into main uIP module. + +* Switched the TCP, UDP and IP header structures to be structs rather + than typedefs. + +* Prefixed TCP state names with UIP_ to avoid name space + contamination. + +* Changed declarations of uip_appdatap and friends to void * to avoid + explicit typecasts. + +* Bugfixes + + o TCP: Fixed bug with high byte of peer window size. + + o TCP: Fixed bug that in some cases prevented concurrent reception and + transmission of TCP data. + + o TCP: uip_connect() didn't correctly calculate age of TIME_WAIT + connections. + + o TCP: Array index for uip_conns[] array was out of bounds in + comparison. Comparison changed to make index within bounds. + + o TCP: if the remote host crashes and tries to reestablish an old + connection, uIP should respond with an ACK with the correct + sequence and acknowledgment numbers, to which the remote host + should respond with an ACK. uIP did not respond with the correct + ACK. + + o TCP: Fixed check for SYNACK segment: now checks only relevant TCP + control flags and discards flags reserved for future expansion. + + o TCP: Fixed bug where uIP did not inform application that a connection + had been aborted during an active open. + + o TCP: FIN segment was accepted even though application had stopped + incoming data with uip_stop(). + + o TCP: A FINACK segment would not always correctly acknowledge data. + + o UDP: checksums are now calculated after all fields have been + filled in. + + o UDP: network byte order on lastport in uip_udp_new(). + + o IP: memset() bugs in IP fragment reassembly code fixed. diff --git a/brcm_iscsi_uio/src/uip/Makefile.am b/brcm_iscsi_uio/src/uip/Makefile.am new file mode 100644 index 0000000..ea054b0 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = -I${top_srcdir}/src/unix \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/src/apps/brcm-iscsi \ + -I${top_srcdir}/include + +noinst_LIBRARIES = libbrcm_iscsi_uip.a + +libbrcm_iscsi_uip_a_SOURCES = uip.c \ + uip_arp.c \ + uiplib.c \ + psock.c \ + timer.c \ + uip-neighbor.c \ + uip_eth.c + +libbrcm_iscsi_uip_a_CFLAGS = -DBYTE_ORDER=@ENDIAN@ diff --git a/brcm_iscsi_uio/src/uip/Makefile.include b/brcm_iscsi_uio/src/uip/Makefile.include new file mode 100644 index 0000000..5e042ca --- /dev/null +++ b/brcm_iscsi_uio/src/uip/Makefile.include @@ -0,0 +1,47 @@ + + +ifdef APPS + APPDIRS = $(foreach APP, $(APPS), ../apps/$(APP)) + -include $(foreach APP, $(APPS), ../apps/$(APP)/Makefile.$(APP)) + CFLAGS += $(addprefix -I../apps/,$(APPS)) +endif + +ifndef CCDEP + CCDEP = $(CC) +endif +ifndef CCDEPCFLAGS + CCDEPCFLAGS = $(CFLAGS) +endif +ifndef OBJECTDIR + OBJECTDIR = obj +endif + +ifeq (${wildcard $(OBJECTDIR)},) + DUMMY := ${shell mkdir $(OBJECTDIR)} +endif + + +vpath %.c . ../uip ../lib $(APPDIRS) + +$(OBJECTDIR)/%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(OBJECTDIR)/%.d: %.c + @set -e; rm -f $@; \ + $(CCDEP) -MM $(CCDEPCFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,$(OBJECTDIR)/\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +UIP_SOURCES=uip.c uip_arp.c uiplib.c psock.c timer.c uip-neighbor.c uip_eth.c + + +ifneq ($(MAKECMDGOALS),clean) +-include $(addprefix $(OBJECTDIR)/,$(UIP_SOURCES:.c=.d) \ + $(APP_SOURCES:.c=.d)) +endif + +libuip.a: ${addprefix $(OBJECTDIR)/, $(UIP_SOURCES:.c=.o)} + $(AR) rc $@ $^ + +libapps.a: ${addprefix $(OBJECTDIR)/, $(APP_SOURCES:.c=.o)} + $(AR) rc $@ $^ diff --git a/brcm_iscsi_uio/src/uip/clock.h b/brcm_iscsi_uio/src/uip/clock.h new file mode 100644 index 0000000..f34d78f --- /dev/null +++ b/brcm_iscsi_uio/src/uip/clock.h @@ -0,0 +1,88 @@ +/** + * \defgroup clock Clock interface + * + * The clock interface is the interface between the \ref timer "timer library" + * and the platform specific clock functionality. The clock + * interface must be implemented for each platform that uses the \ref + * timer "timer library". + * + * The clock interface does only one this: it measures time. The clock + * interface provides a macro, CLOCK_SECOND, which corresponds to one + * second of system time. + * + * \sa \ref timer "Timer library" + * + * @{ + */ + +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: clock.h,v 1.3 2006/06/11 21:46:39 adam Exp $ + */ +#ifndef __CLOCK_H__ +#define __CLOCK_H__ + +#include "clock-arch.h" + +/** + * Initialize the clock library. + * + * This function initializes the clock library and should be called + * from the main() function of the system. + * + */ +void clock_init(void); + +/** + * Get the current clock time. + * + * This function returns the current system clock time. + * + * \return The current clock time, measured in system ticks. + */ +clock_time_t clock_time(void); + +/** + * A second, measured in system clock time. + * + * \hideinitializer + */ +#ifdef CLOCK_CONF_SECOND +#define CLOCK_SECOND CLOCK_CONF_SECOND +#else +#define CLOCK_SECOND (clock_time_t)32 +#endif + +#endif /* __CLOCK_H__ */ + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/debug.h b/brcm_iscsi_uio/src/uip/debug.h new file mode 100644 index 0000000..9d5ebbe --- /dev/null +++ b/brcm_iscsi_uio/src/uip/debug.h @@ -0,0 +1,9 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#ifdef DEBUG +#define UIP_DEBUG(args...) fprintf(stdout, args); fflush(stdout) +#else +#endif + +#endif diff --git a/brcm_iscsi_uio/src/uip/lc-addrlabels.h b/brcm_iscsi_uio/src/uip/lc-addrlabels.h new file mode 100644 index 0000000..fe1387e --- /dev/null +++ b/brcm_iscsi_uio/src/uip/lc-addrlabels.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2004-2005, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: lc-addrlabels.h,v 1.3 2006/06/12 08:00:30 adam Exp $ + */ + +/** + * \addtogroup lc + * @{ + */ + +/** + * \file + * Implementation of local continuations based on the "Labels as + * values" feature of gcc + * \author + * Adam Dunkels + * + * This implementation of local continuations is based on a special + * feature of the GCC C compiler called "labels as values". This + * feature allows assigning pointers with the address of the code + * corresponding to a particular C label. + * + * For more information, see the GCC documentation: + * http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html + * + * Thanks to dividuum for finding the nice local scope label + * implementation. + */ + +#ifndef __LC_ADDRLABELS_H__ +#define __LC_ADDRLABELS_H__ + +/** \hideinitializer */ +typedef void * lc_t; + +#define LC_INIT(s) s = NULL + + +#define LC_RESUME(s) \ + do { \ + if(s != NULL) { \ + goto *s; \ + } \ + } while(0) + +#define LC_SET(s) \ + do { ({ __label__ resume; resume: (s) = &&resume; }); }while(0) + +#define LC_END(s) + +#endif /* __LC_ADDRLABELS_H__ */ + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/lc-switch.h b/brcm_iscsi_uio/src/uip/lc-switch.h new file mode 100644 index 0000000..f32885f --- /dev/null +++ b/brcm_iscsi_uio/src/uip/lc-switch.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2004-2005, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: lc-switch.h,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ + +/** + * \addtogroup lc + * @{ + */ + +/** + * \file + * Implementation of local continuations based on switch() statment + * \author Adam Dunkels + * + * This implementation of local continuations uses the C switch() + * statement to resume execution of a function somewhere inside the + * function's body. The implementation is based on the fact that + * switch() statements are able to jump directly into the bodies of + * control structures such as if() or while() statmenets. + * + * This implementation borrows heavily from Simon Tatham's coroutines + * implementation in C: + * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html + */ + +#ifndef __LC_SWITCH_H__ +#define __LC_SWTICH_H__ + +/* WARNING! lc implementation using switch() does not work if an + LC_SET() is done within another switch() statement! */ + +/** \hideinitializer */ +typedef unsigned short lc_t; + +#define LC_INIT(s) s = 0; + +#define LC_RESUME(s) switch(s) { case 0: + +#define LC_SET(s) s = __LINE__; case __LINE__: + +#define LC_END(s) } + +#endif /* __LC_SWITCH_H__ */ + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/lc.h b/brcm_iscsi_uio/src/uip/lc.h new file mode 100644 index 0000000..a9e9d46 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/lc.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2004-2005, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: lc.h,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ + +/** + * \addtogroup pt + * @{ + */ + +/** + * \defgroup lc Local continuations + * @{ + * + * Local continuations form the basis for implementing protothreads. A + * local continuation can be set in a specific function to + * capture the state of the function. After a local continuation has + * been set can be resumed in order to restore the state of the + * function at the point where the local continuation was set. + * + * + */ + +/** + * \file lc.h + * Local continuations + * \author + * Adam Dunkels + * + */ + +#ifdef DOXYGEN +/** + * Initialize a local continuation. + * + * This operation initializes the local continuation, thereby + * unsetting any previously set continuation state. + * + * \hideinitializer + */ +#define LC_INIT(lc) + +/** + * Set a local continuation. + * + * The set operation saves the state of the function at the point + * where the operation is executed. As far as the set operation is + * concerned, the state of the function does not include the + * call-stack or local (automatic) variables, but only the program + * counter and such CPU registers that needs to be saved. + * + * \hideinitializer + */ +#define LC_SET(lc) + +/** + * Resume a local continuation. + * + * The resume operation resumes a previously set local continuation, thus + * restoring the state in which the function was when the local + * continuation was set. If the local continuation has not been + * previously set, the resume operation does nothing. + * + * \hideinitializer + */ +#define LC_RESUME(lc) + +/** + * Mark the end of local continuation usage. + * + * The end operation signifies that local continuations should not be + * used any more in the function. This operation is not needed for + * most implementations of local continuation, but is required by a + * few implementations. + * + * \hideinitializer + */ +#define LC_END(lc) + +/** + * \var typedef lc_t; + * + * The local continuation type. + * + * \hideinitializer + */ +#endif /* DOXYGEN */ + +#ifndef __LC_H__ +#define __LC_H__ + +#ifdef LC_CONF_INCLUDE +#include LC_CONF_INCLUDE +#else +#include "lc-switch.h" +#endif /* LC_CONF_INCLUDE */ + +#endif /* __LC_H__ */ + +/** @} */ +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/psock.c b/brcm_iscsi_uio/src/uip/psock.c new file mode 100644 index 0000000..80fdb85 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/psock.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: psock.c,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ + +#include +#include + +#include "uipopt.h" +#include "psock.h" +#include "uip.h" + +#define STATE_NONE 0 +#define STATE_ACKED 1 +#define STATE_READ 2 +#define STATE_BLOCKED_NEWDATA 3 +#define STATE_BLOCKED_CLOSE 4 +#define STATE_BLOCKED_SEND 5 +#define STATE_DATA_SENT 6 + +/* + * Return value of the buffering functions that indicates that a + * buffer was not filled by incoming data. + * + */ +#define BUF_NOT_FULL 0 +#define BUF_NOT_FOUND 0 + +/* + * Return value of the buffering functions that indicates that a + * buffer was completely filled by incoming data. + * + */ +#define BUF_FULL 1 + +/* + * Return value of the buffering functions that indicates that an + * end-marker byte was found. + * + */ +#define BUF_FOUND 2 + +/*---------------------------------------------------------------------------*/ +static void +buf_setup(struct psock_buf *buf, + u8_t *bufptr, u16_t bufsize) +{ + buf->ptr = bufptr; + buf->left = bufsize; +} +/*---------------------------------------------------------------------------*/ +static u8_t +buf_bufdata(struct psock_buf *buf, u16_t len, + u8_t **dataptr, u16_t *datalen) +{ + if(*datalen < buf->left) { + memcpy(buf->ptr, *dataptr, *datalen); + buf->ptr += *datalen; + buf->left -= *datalen; + *dataptr += *datalen; + *datalen = 0; + return BUF_NOT_FULL; + } else if(*datalen == buf->left) { + memcpy(buf->ptr, *dataptr, *datalen); + buf->ptr += *datalen; + buf->left = 0; + *dataptr += *datalen; + *datalen = 0; + return BUF_FULL; + } else { + memcpy(buf->ptr, *dataptr, buf->left); + buf->ptr += buf->left; + *datalen -= buf->left; + *dataptr += buf->left; + buf->left = 0; + return BUF_FULL; + } +} +/*---------------------------------------------------------------------------*/ +static u8_t +buf_bufto(register struct psock_buf *buf, u8_t endmarker, + register u8_t **dataptr, register u16_t *datalen) +{ + u8_t c; + while(buf->left > 0 && *datalen > 0) { + c = *buf->ptr = **dataptr; + ++*dataptr; + ++buf->ptr; + --*datalen; + --buf->left; + + if(c == endmarker) { + return BUF_FOUND; + } + } + + if(*datalen == 0) { + return BUF_NOT_FOUND; + } + + while(*datalen > 0) { + c = **dataptr; + --*datalen; + ++*dataptr; + + if(c == endmarker) { + return BUF_FOUND | BUF_FULL; + } + } + + return BUF_FULL; +} +/*---------------------------------------------------------------------------*/ +static char +send_data(register struct psock *s) +{ + if(s->state != STATE_DATA_SENT || uip_rexmit(s->ustack)) { + if(s->sendlen > uip_mss(s->ustack)) { + uip_send(s->ustack, s->sendptr, uip_mss(s->ustack)); + } else { + uip_send(s->ustack, s->sendptr, s->sendlen); + } + s->state = STATE_DATA_SENT; + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static char +data_acked(struct psock *s) +{ + if(s->state == STATE_DATA_SENT && uip_acked(s->ustack)) { + if(s->sendlen > uip_mss(s->ustack)) { + s->sendlen -= uip_mss(s->ustack); + s->sendptr += uip_mss(s->ustack); + } else { + s->sendptr += s->sendlen; + s->sendlen = 0; + } + s->state = STATE_ACKED; + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +PT_THREAD(psock_send(struct uip_stack *ustack, + register struct psock *s, const u8_t *buf, + unsigned int len)) +{ + PT_BEGIN(&s->psockpt); + + /* If there is no data to send, we exit immediately. */ + if(len == 0) { + PT_EXIT(&s->psockpt); + } + + /* Save the length of and a pointer to the data that is to be + sent. */ + s->sendptr = buf; + s->sendlen = len; + + s->state = STATE_NONE; + + /* We loop here until all data is sent. The s->sendlen variable is + updated by the data_sent() function. */ + while(s->sendlen > 0) { + + /* + * The condition for this PT_WAIT_UNTIL is a little tricky: the + * protothread will wait here until all data has been acknowledged + * (data_acked() returns true) and until all data has been sent + * (send_data() returns true). The two functions data_acked() and + * send_data() must be called in succession to ensure that all + * data is sent. Therefore the & operator is used instead of the + * && operator, which would cause only the data_acked() function + * to be called when it returns false. + */ + PT_WAIT_UNTIL(&s->psockpt, data_acked(s) & send_data(s)); + } + + s->state = STATE_NONE; + + PT_END(&s->psockpt); +} +/*---------------------------------------------------------------------------*/ +PT_THREAD(psock_generator_send(register struct psock *s, + unsigned short (*generate)(void *), void *arg)) +{ + PT_BEGIN(&s->psockpt); + + /* Ensure that there is a generator function to call. */ + if(generate == NULL) { + PT_EXIT(&s->psockpt); + } + + /* Call the generator function to generate the data in the + uip_appdata buffer. */ + s->sendlen = generate(arg); + s->sendptr = s->ustack->uip_appdata; + + s->state = STATE_NONE; + do { + /* Call the generator function again if we are called to perform a + retransmission. */ + if(uip_rexmit(s->ustack)) { + generate(arg); + } + /* Wait until all data is sent and acknowledged. */ + PT_WAIT_UNTIL(&s->psockpt, data_acked(s) & send_data(s)); + } while(s->sendlen > 0); + + s->state = STATE_NONE; + + PT_END(&s->psockpt); +} +/*---------------------------------------------------------------------------*/ +u16_t +psock_datalen(struct psock *psock) +{ + return psock->bufsize - psock->buf.left; +} +/*---------------------------------------------------------------------------*/ +char +psock_newdata(struct psock *s) +{ + if(s->readlen > 0) { + /* There is data in the uip_appdata buffer that has not yet been + read with the PSOCK_READ functions. */ + return 1; + } else if(s->state == STATE_READ) { + /* All data in uip_appdata buffer already consumed. */ + s->state = STATE_BLOCKED_NEWDATA; + return 0; + } else if(uip_newdata(s->ustack)) { + /* There is new data that has not been consumed. */ + return 1; + } else { + /* There is no new data. */ + return 0; + } +} +/*---------------------------------------------------------------------------*/ +PT_THREAD(psock_readto(register struct psock *psock, u8_t c)) +{ + PT_BEGIN(&psock->psockpt); + + buf_setup(&psock->buf, psock->bufptr, psock->bufsize); + + /* XXX: Should add buf_checkmarker() before do{} loop, if + incoming data has been handled while waiting for a write. */ + + do { + if(psock->readlen == 0) { + PT_WAIT_UNTIL(&psock->psockpt, psock_newdata(psock)); + psock->state = STATE_READ; + psock->readptr = (u8_t *)psock->ustack->uip_appdata; + psock->readlen = uip_datalen(psock->ustack); + } + } while((buf_bufto(&psock->buf, c, + &psock->readptr, + &psock->readlen) & BUF_FOUND) == 0); + + if(psock_datalen(psock) == 0) { + psock->state = STATE_NONE; + PT_RESTART(&psock->psockpt); + } + PT_END(&psock->psockpt); +} +/*---------------------------------------------------------------------------*/ +PT_THREAD(psock_readbuf(register struct psock *psock)) +{ + PT_BEGIN(&psock->psockpt); + + buf_setup(&psock->buf, psock->bufptr, psock->bufsize); + + /* XXX: Should add buf_checkmarker() before do{} loop, if + incoming data has been handled while waiting for a write. */ + + do { + if(psock->readlen == 0) { + PT_WAIT_UNTIL(&psock->psockpt, psock_newdata(psock)); + printf("Waited for newdata\n"); + psock->state = STATE_READ; + psock->readptr = (u8_t *)psock->ustack->uip_appdata; + psock->readlen = uip_datalen(psock->ustack); + } + } while(buf_bufdata(&psock->buf, psock->bufsize, + &psock->readptr, + &psock->readlen) != BUF_FULL); + + if(psock_datalen(psock) == 0) { + psock->state = STATE_NONE; + PT_RESTART(&psock->psockpt); + } + PT_END(&psock->psockpt); +} +/*---------------------------------------------------------------------------*/ +void +psock_init(struct uip_stack *ustack, + register struct psock *psock, u8_t *buffer, unsigned int buffersize) +{ + psock->state = STATE_NONE; + psock->readlen = 0; + psock->bufptr = buffer; + psock->bufsize = buffersize; + psock->ustack = ustack; + buf_setup(&psock->buf, buffer, buffersize); + PT_INIT(&psock->pt); + PT_INIT(&psock->psockpt); +} +/*---------------------------------------------------------------------------*/ diff --git a/brcm_iscsi_uio/src/uip/psock.h b/brcm_iscsi_uio/src/uip/psock.h new file mode 100644 index 0000000..ac2aade --- /dev/null +++ b/brcm_iscsi_uio/src/uip/psock.h @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: psock.h,v 1.3 2006/06/12 08:00:30 adam Exp $ + */ + +/** + * \defgroup psock Protosockets library + * @{ + * + * The protosocket library provides an interface to the uIP stack that is + * similar to the traditional BSD socket interface. Unlike programs + * written for the ordinary uIP event-driven interface, programs + * written with the protosocket library are executed in a sequential + * fashion and does not have to be implemented as explicit state + * machines. + * + * Protosockets only work with TCP connections. + * + * The protosocket library uses \ref pt protothreads to provide + * sequential control flow. This makes the protosockets lightweight in + * terms of memory, but also means that protosockets inherits the + * functional limitations of protothreads. Each protosocket lives only + * within a single function. Automatic variables (stack variables) are + * not retained across a protosocket library function call. + * + * \note Because the protosocket library uses protothreads, local + * variables will not always be saved across a call to a protosocket + * library function. It is therefore advised that local variables are + * used with extreme care. + * + * The protosocket library provides functions for sending data without + * having to deal with retransmissions and acknowledgements, as well + * as functions for reading data without having to deal with data + * being split across more than one TCP segment. + * + * Because each protosocket runs as a protothread, the protosocket has to be + * started with a call to PSOCK_BEGIN() at the start of the function + * in which the protosocket is used. Similarly, the protosocket protothread can + * be terminated by a call to PSOCK_EXIT(). + * + */ + +/** + * \file + * Protosocket library header file + * \author + * Adam Dunkels + * + */ + +#ifndef __PSOCK_H__ +#define __PSOCK_H__ + +#include "uip.h" +#include "uipopt.h" +#include "pt.h" + + /* + * The structure that holds the state of a buffer. + * + * This structure holds the state of a uIP buffer. The structure has + * no user-visible elements, but is used through the functions + * provided by the library. + * + */ +struct psock_buf { + u8_t *ptr; + unsigned short left; +}; + +/** + * The representation of a protosocket. + * + * The protosocket structrure is an opaque structure with no user-visible + * elements. + */ +struct psock { + struct pt pt, psockpt; /* Protothreads - one that's using the psock + functions, and one that runs inside the + psock functions. */ + const u8_t *sendptr; /* Pointer to the next data to be sent. */ + u8_t *readptr; /* Pointer to the next data to be read. */ + + u8_t *bufptr; /* Pointer to the buffer used for buffering + incoming data. */ + + u16_t sendlen; /* The number of bytes left to be sent. */ + u16_t readlen; /* The number of bytes left to be read. */ + + struct psock_buf buf; /* The structure holding the state of the + input buffer. */ + unsigned int bufsize; /* The size of the input buffer. */ + + unsigned char state; /* The state of the protosocket. */ + + struct uip_stack *ustack; +}; + +void psock_init(struct uip_stack *ustack, + struct psock *psock, u8_t *buffer, unsigned int buffersize); +/** + * Initialize a protosocket. + * + * This macro initializes a protosocket and must be called before the + * protosocket is used. The initialization also specifies the input buffer + * for the protosocket. + * + * \param psock (struct psock *) A pointer to the protosocket to be + * initialized + * + * \param buffer (char *) A pointer to the input buffer for the + * protosocket. + * + * \param buffersize (unsigned int) The size of the input buffer. + * + * \hideinitializer + */ +#define PSOCK_INIT(psock, buffer, buffersize) \ + psock_init(psock, buffer, buffersize) + +/** + * Start the protosocket protothread in a function. + * + * This macro starts the protothread associated with the protosocket and + * must come before other protosocket calls in the function it is used. + * + * \param psock (struct psock *) A pointer to the protosocket to be + * started. + * + * \hideinitializer + */ +#define PSOCK_BEGIN(psock) PT_BEGIN(&((psock)->pt)) + +PT_THREAD(psock_send(struct uip_stack *ustack, + struct psock *psock, const u8_t *buf, unsigned int len)); +/** + * Send data. + * + * This macro sends data over a protosocket. The protosocket protothread blocks + * until all data has been sent and is known to have been received by + * the remote end of the TCP connection. + * + * \param psock (struct psock *) A pointer to the protosocket over which + * data is to be sent. + * + * \param data (char *) A pointer to the data that is to be sent. + * + * \param datalen (unsigned int) The length of the data that is to be + * sent. + * + * \hideinitializer + */ +#define PSOCK_SEND(psock, data, datalen) \ + PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, data, datalen)) + +/** + * \brief Send a null-terminated string. + * \param psock Pointer to the protosocket. + * \param str The string to be sent. + * + * This function sends a null-terminated string over the + * protosocket. + * + * \hideinitializer + */ +#define PSOCK_SEND_STR(psock, str) \ + PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, str, strlen(str))) + +PT_THREAD(psock_generator_send(struct psock *psock, + unsigned short (*f)(void *), void *arg)); + +/** + * \brief Generate data with a function and send it + * \param psock Pointer to the protosocket. + * \param generator Pointer to the generator function + * \param arg Argument to the generator function + * + * This function generates data and sends it over the + * protosocket. This can be used to dynamically generate + * data for a transmission, instead of generating the data + * in a buffer beforehand. This function reduces the need for + * buffer memory. The generator function is implemented by + * the application, and a pointer to the function is given + * as an argument with the call to PSOCK_GENERATOR_SEND(). + * + * The generator function should place the generated data + * directly in the uip_appdata buffer, and return the + * length of the generated data. The generator function is + * called by the protosocket layer when the data first is + * sent, and once for every retransmission that is needed. + * + * \hideinitializer + */ +#define PSOCK_GENERATOR_SEND(psock, generator, arg) \ + PT_WAIT_THREAD(&((psock)->pt), \ + psock_generator_send(psock, generator, arg)) + + +/** + * Close a protosocket. + * + * This macro closes a protosocket and can only be called from within the + * protothread in which the protosocket lives. + * + * \param psock (struct psock *) A pointer to the protosocket that is to + * be closed. + * + * \hideinitializer + */ +#define PSOCK_CLOSE(psock) uip_close() + +PT_THREAD(psock_readbuf(struct psock *psock)); +/** + * Read data until the buffer is full. + * + * This macro will block waiting for data and read the data into the + * input buffer specified with the call to PSOCK_INIT(). Data is read + * until the buffer is full.. + * + * \param psock (struct psock *) A pointer to the protosocket from which + * data should be read. + * + * \hideinitializer + */ +#define PSOCK_READBUF(psock) \ + PT_WAIT_THREAD(&((psock)->pt), psock_readbuf(psock)) + +PT_THREAD(psock_readto(struct psock *psock, unsigned char c)); +/** + * Read data up to a specified character. + * + * This macro will block waiting for data and read the data into the + * input buffer specified with the call to PSOCK_INIT(). Data is only + * read until the specifieed character appears in the data stream. + * + * \param psock (struct psock *) A pointer to the protosocket from which + * data should be read. + * + * \param c (char) The character at which to stop reading. + * + * \hideinitializer + */ +#define PSOCK_READTO(psock, c) \ + PT_WAIT_THREAD(&((psock)->pt), psock_readto(psock, c)) + +/** + * The length of the data that was previously read. + * + * This macro returns the length of the data that was previously read + * using PSOCK_READTO() or PSOCK_READ(). + * + * \param psock (struct psock *) A pointer to the protosocket holding the data. + * + * \hideinitializer + */ +#define PSOCK_DATALEN(psock) psock_datalen(psock) + +u16_t psock_datalen(struct psock *psock); + +/** + * Exit the protosocket's protothread. + * + * This macro terminates the protothread of the protosocket and should + * almost always be used in conjunction with PSOCK_CLOSE(). + * + * \sa PSOCK_CLOSE_EXIT() + * + * \param psock (struct psock *) A pointer to the protosocket. + * + * \hideinitializer + */ +#define PSOCK_EXIT(psock) PT_EXIT(&((psock)->pt)) + +/** + * Close a protosocket and exit the protosocket's protothread. + * + * This macro closes a protosocket and exits the protosocket's protothread. + * + * \param psock (struct psock *) A pointer to the protosocket. + * + * \hideinitializer + */ +#define PSOCK_CLOSE_EXIT(psock) \ + do { \ + PSOCK_CLOSE(psock); \ + PSOCK_EXIT(psock); \ + } while(0) + +/** + * Declare the end of a protosocket's protothread. + * + * This macro is used for declaring that the protosocket's protothread + * ends. It must always be used together with a matching PSOCK_BEGIN() + * macro. + * + * \param psock (struct psock *) A pointer to the protosocket. + * + * \hideinitializer + */ +#define PSOCK_END(psock) PT_END(&((psock)->pt)) + +char psock_newdata(struct psock *s); + +/** + * Check if new data has arrived on a protosocket. + * + * This macro is used in conjunction with the PSOCK_WAIT_UNTIL() + * macro to check if data has arrived on a protosocket. + * + * \param psock (struct psock *) A pointer to the protosocket. + * + * \hideinitializer + */ +#define PSOCK_NEWDATA(psock) psock_newdata(psock) + +/** + * Wait until a condition is true. + * + * This macro blocks the protothread until the specified condition is + * true. The macro PSOCK_NEWDATA() can be used to check if new data + * arrives when the protosocket is waiting. + * + * Typically, this macro is used as follows: + * + \code + PT_THREAD(thread(struct psock *s, struct timer *t)) + { + PSOCK_BEGIN(s); + + PSOCK_WAIT_UNTIL(s, PSOCK_NEWADATA(s) || timer_expired(t)); + + if(PSOCK_NEWDATA(s)) { + PSOCK_READTO(s, '\n'); + } else { + handle_timed_out(s); + } + + PSOCK_END(s); + } + \endcode + * + * \param psock (struct psock *) A pointer to the protosocket. + * \param condition The condition to wait for. + * + * \hideinitializer + */ +#define PSOCK_WAIT_UNTIL(psock, condition) \ + PT_WAIT_UNTIL(&((psock)->pt), (condition)); + +#define PSOCK_WAIT_THREAD(psock, condition) \ + PT_WAIT_THREAD(&((psock)->pt), (condition)) + +#endif /* __PSOCK_H__ */ + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/pt.h b/brcm_iscsi_uio/src/uip/pt.h new file mode 100644 index 0000000..9f1f64d --- /dev/null +++ b/brcm_iscsi_uio/src/uip/pt.h @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2004-2005, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: pt.h,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ + +/** + * \addtogroup pt + * @{ + */ + +/** + * \file + * Protothreads implementation. + * \author + * Adam Dunkels + * + */ + +#ifndef __PT_H__ +#define __PT_H__ + +#include "lc.h" + +struct pt { + lc_t lc; +}; + +#define PT_WAITING 0 +#define PT_EXITED 1 +#define PT_ENDED 2 +#define PT_YIELDED 3 + +/** + * \name Initialization + * @{ + */ + +/** + * Initialize a protothread. + * + * Initializes a protothread. Initialization must be done prior to + * starting to execute the protothread. + * + * \param pt A pointer to the protothread control structure. + * + * \sa PT_SPAWN() + * + * \hideinitializer + */ +#define PT_INIT(pt) LC_INIT((pt)->lc) + +/** @} */ + +/** + * \name Declaration and definition + * @{ + */ + +/** + * Declaration of a protothread. + * + * This macro is used to declare a protothread. All protothreads must + * be declared with this macro. + * + * \param name_args The name and arguments of the C function + * implementing the protothread. + * + * \hideinitializer + */ +#define PT_THREAD(name_args) char name_args + +/** + * Declare the start of a protothread inside the C function + * implementing the protothread. + * + * This macro is used to declare the starting point of a + * protothread. It should be placed at the start of the function in + * which the protothread runs. All C statements above the PT_BEGIN() + * invokation will be executed each time the protothread is scheduled. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc) + +/** + * Declare the end of a protothread. + * + * This macro is used for declaring that a protothread ends. It must + * always be used together with a matching PT_BEGIN() macro. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \ + PT_INIT(pt); return PT_ENDED; } + +/** @} */ + +/** + * \name Blocked wait + * @{ + */ + +/** + * Block and wait until condition is true. + * + * This macro blocks the protothread until the specified condition is + * true. + * + * \param pt A pointer to the protothread control structure. + * \param condition The condition. + * + * \hideinitializer + */ +#define PT_WAIT_UNTIL(pt, condition) \ + do { \ + LC_SET((pt)->lc); \ + if(!(condition)) { \ + return PT_WAITING; \ + } \ + } while(0) + +/** + * Block and wait while condition is true. + * + * This function blocks and waits while condition is true. See + * PT_WAIT_UNTIL(). + * + * \param pt A pointer to the protothread control structure. + * \param cond The condition. + * + * \hideinitializer + */ +#define PT_WAIT_WHILE(pt, cond) PT_WAIT_UNTIL((pt), !(cond)) + +/** @} */ + +/** + * \name Hierarchical protothreads + * @{ + */ + +/** + * Block and wait until a child protothread completes. + * + * This macro schedules a child protothread. The current protothread + * will block until the child protothread completes. + * + * \note The child protothread must be manually initialized with the + * PT_INIT() function before this function is used. + * + * \param pt A pointer to the protothread control structure. + * \param thread The child protothread with arguments + * + * \sa PT_SPAWN() + * + * \hideinitializer + */ +#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread)) + +/** + * Spawn a child protothread and wait until it exits. + * + * This macro spawns a child protothread and waits until it exits. The + * macro can only be used within a protothread. + * + * \param pt A pointer to the protothread control structure. + * \param child A pointer to the child protothread's control structure. + * \param thread The child protothread with arguments + * + * \hideinitializer + */ +#define PT_SPAWN(pt, child, thread) \ + do { \ + PT_INIT((child)); \ + PT_WAIT_THREAD((pt), (thread)); \ + } while(0) + +/** @} */ + +/** + * \name Exiting and restarting + * @{ + */ + +/** + * Restart the protothread. + * + * This macro will block and cause the running protothread to restart + * its execution at the place of the PT_BEGIN() call. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_RESTART(pt) \ + do { \ + PT_INIT(pt); \ + return PT_WAITING; \ + } while(0) + +/** + * Exit the protothread. + * + * This macro causes the protothread to exit. If the protothread was + * spawned by another protothread, the parent protothread will become + * unblocked and can continue to run. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_EXIT(pt) \ + do { \ + PT_INIT(pt); \ + return PT_EXITED; \ + } while(0) + +/** @} */ + +/** + * \name Calling a protothread + * @{ + */ + +/** + * Schedule a protothread. + * + * This function shedules a protothread. The return value of the + * function is non-zero if the protothread is running or zero if the + * protothread has exited. + * + * \param f The call to the C function implementing the protothread to + * be scheduled + * + * \hideinitializer + */ +#define PT_SCHEDULE(f) ((f) == PT_WAITING) + +/** @} */ + +/** + * \name Yielding from a protothread + * @{ + */ + +/** + * Yield from the current protothread. + * + * This function will yield the protothread, thereby allowing other + * processing to take place in the system. + * + * \param pt A pointer to the protothread control structure. + * + * \hideinitializer + */ +#define PT_YIELD(pt) \ + do { \ + PT_YIELD_FLAG = 0; \ + LC_SET((pt)->lc); \ + if(PT_YIELD_FLAG == 0) { \ + return PT_YIELDED; \ + } \ + } while(0) + +/** + * \brief Yield from the protothread until a condition occurs. + * \param pt A pointer to the protothread control structure. + * \param cond The condition. + * + * This function will yield the protothread, until the + * specified condition evaluates to true. + * + * + * \hideinitializer + */ +#define PT_YIELD_UNTIL(pt, cond) \ + do { \ + PT_YIELD_FLAG = 0; \ + LC_SET((pt)->lc); \ + if((PT_YIELD_FLAG == 0) || !(cond)) { \ + return PT_YIELDED; \ + } \ + } while(0) + +/** @} */ + +#endif /* __PT_H__ */ + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/timer.c b/brcm_iscsi_uio/src/uip/timer.c new file mode 100644 index 0000000..74eedf6 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/timer.c @@ -0,0 +1,127 @@ +/** + * \addtogroup timer + * @{ + */ + +/** + * \file + * Timer library implementation. + * \author + * Adam Dunkels + */ + +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: timer.c,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ + +#include "clock.h" +#include "timer.h" + +/*---------------------------------------------------------------------------*/ +/** + * Set a timer. + * + * This function is used to set a timer for a time sometime in the + * future. The function timer_expired() will evaluate to true after + * the timer has expired. + * + * \param t A pointer to the timer + * \param interval The interval before the timer expires. + * + */ +void +timer_set(struct timer *t, clock_time_t interval) +{ + t->interval = interval; + t->start = clock_time(); +} +/*---------------------------------------------------------------------------*/ +/** + * Reset the timer with the same interval. + * + * This function resets the timer with the same interval that was + * given to the timer_set() function. The start point of the interval + * is the exact time that the timer last expired. Therefore, this + * function will cause the timer to be stable over time, unlike the + * timer_rester() function. + * + * \param t A pointer to the timer. + * + * \sa timer_restart() + */ +void +timer_reset(struct timer *t) +{ + t->start += t->interval; +} +/*---------------------------------------------------------------------------*/ +/** + * Restart the timer from the current point in time + * + * This function restarts a timer with the same interval that was + * given to the timer_set() function. The timer will start at the + * current time. + * + * \note A periodic timer will drift if this function is used to reset + * it. For preioric timers, use the timer_reset() function instead. + * + * \param t A pointer to the timer. + * + * \sa timer_reset() + */ +void +timer_restart(struct timer *t) +{ + t->start = clock_time(); +} +/*---------------------------------------------------------------------------*/ +/** + * Check if a timer has expired. + * + * This function tests if a timer has expired and returns true or + * false depending on its status. + * + * \param t A pointer to the timer + * + * \return Non-zero if the timer has expired, zero otherwise. + * + */ +int +timer_expired(struct timer *t) +{ + return (clock_time_t)(clock_time() - t->start) >= (clock_time_t)t->interval; +} +/*---------------------------------------------------------------------------*/ + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/timer.h b/brcm_iscsi_uio/src/uip/timer.h new file mode 100644 index 0000000..057bea4 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/timer.h @@ -0,0 +1,86 @@ +/** + * \defgroup timer Timer library + * + * The timer library provides functions for setting, resetting and + * restarting timers, and for checking if a timer has expired. An + * application must "manually" check if its timers have expired; this + * is not done automatically. + * + * A timer is declared as a \c struct \c timer and all access to the + * timer is made by a pointer to the declared timer. + * + * \note The timer library uses the \ref clock "Clock library" to + * measure time. Intervals should be specified in the format used by + * the clock library. + * + * @{ + */ + + +/** + * \file + * Timer library header file. + * \author + * Adam Dunkels + */ + +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: timer.h,v 1.3 2006/06/11 21:46:39 adam Exp $ + */ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#include "clock.h" + +/** + * A timer. + * + * This structure is used for declaring a timer. The timer must be set + * with timer_set() before it can be used. + * + * \hideinitializer + */ +struct timer { + clock_time_t start; + clock_time_t interval; +}; + +void timer_set(struct timer *t, clock_time_t interval); +void timer_reset(struct timer *t); +void timer_restart(struct timer *t); +int timer_expired(struct timer *t); + +#endif /* __TIMER_H__ */ + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/uip-fw.c b/brcm_iscsi_uio/src/uip/uip-fw.c new file mode 100644 index 0000000..01858ea --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip-fw.c @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: uip-fw.c,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ +/** + * \addtogroup uip + * @{ + */ + +/** + * \defgroup uipfw uIP packet forwarding + * @{ + * + */ + +/** + * \file + * uIP packet forwarding. + * \author Adam Dunkels + * + * This file implements a number of simple functions which do packet + * forwarding over multiple network interfaces with uIP. + * + */ + +#include "uip.h" +#include "uip_arch.h" +#include "uip-fw.h" + +#include /* for memcpy() */ + +/* + * The list of registered network interfaces. + */ +static struct uip_fw_netif *netifs = NULL; + +/* + * A pointer to the default network interface. + */ +static struct uip_fw_netif *defaultnetif = NULL; + +struct tcpip_hdr { + /* IP header. */ + u8_t vhl, + tos; + u16_t len, + ipid, + ipoffset; + u8_t ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; + + /* TCP header. */ + u16_t srcport, + destport; + u8_t seqno[4], + ackno[4], + tcpoffset, + flags, + wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +struct icmpip_hdr { + /* IP header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; + /* ICMP (echo) header. */ + u8_t type, icode; + u16_t icmpchksum; + u16_t id, seqno; + u8_t payload[1]; +}; + +/* ICMP ECHO. */ +#define ICMP_ECHO 8 + +/* ICMP TIME-EXCEEDED. */ +#define ICMP_TE 11 + +/* + * Pointer to the TCP/IP headers of the packet in the uip_buf buffer. + */ +#define BUF ((struct tcpip_hdr *)&uip_buf[UIP_LLH_LEN]) + +/* + * Pointer to the ICMP/IP headers of the packet in the uip_buf buffer. + */ +#define ICMPBUF ((struct icmpip_hdr *)&uip_buf[UIP_LLH_LEN]) + +/* + * Certain fields of an IP packet that are used for identifying + * duplicate packets. + */ +struct fwcache_entry { + u16_t timer; + + u16_t srcipaddr[2]; + u16_t destipaddr[2]; + u16_t ipid; + u8_t proto; + u8_t unused; + +#if notdef + u16_t payload[2]; +#endif + +#if UIP_REASSEMBLY > 0 + u16_t len, offset; +#endif +}; + +/* + * The number of packets to remember when looking for duplicates. + */ +#ifdef UIP_CONF_FWCACHE_SIZE +#define FWCACHE_SIZE UIP_CONF_FWCACHE_SIZE +#else +#define FWCACHE_SIZE 2 +#endif + + +/* + * A cache of packet header fields which are used for + * identifying duplicate packets. + */ +static struct fwcache_entry fwcache[FWCACHE_SIZE]; + +/** + * \internal + * The time that a packet cache is active. + */ +#define FW_TIME 20 + +/*------------------------------------------------------------------------------*/ +/** + * Initialize the uIP packet forwarding module. + */ +/*------------------------------------------------------------------------------*/ +void +uip_fw_init(void) +{ + struct uip_fw_netif *t; + defaultnetif = NULL; + while(netifs != NULL) { + t = netifs; + netifs = netifs->next; + t->next = NULL; + } +} +/*------------------------------------------------------------------------------*/ +/** + * \internal + * Check if an IP address is within the network defined by an IP + * address and a netmask. + * + * \param ipaddr The IP address to be checked. + * \param netipaddr The IP address of the network. + * \param netmask The netmask of the network. + * + * \return Non-zero if IP address is in network, zero otherwise. + */ +/*------------------------------------------------------------------------------*/ +static unsigned char +ipaddr_maskcmp(u16_t *ipaddr, u16_t *netipaddr, u16_t *netmask) +{ + return (ipaddr[0] & netmask [0]) == (netipaddr[0] & netmask[0]) && + (ipaddr[1] & netmask[1]) == (netipaddr[1] & netmask[1]); +} +/*------------------------------------------------------------------------------*/ +/** + * \internal + * Send out an ICMP TIME-EXCEEDED message. + * + * This function replaces the packet in the uip_buf buffer with the + * ICMP packet. + */ +/*------------------------------------------------------------------------------*/ +static void +time_exceeded(void) +{ + u16_t tmp16; + + /* We don't send out ICMP errors for ICMP messages. */ + if(ICMPBUF->proto == UIP_PROTO_ICMP) { + uip_len = 0; + return; + } + /* Copy fields from packet header into payload of this ICMP packet. */ + memcpy(&(ICMPBUF->payload[0]), ICMPBUF, 28); + + /* Set the ICMP type and code. */ + ICMPBUF->type = ICMP_TE; + ICMPBUF->icode = 0; + + /* Calculate the ICMP checksum. */ + ICMPBUF->icmpchksum = 0; + ICMPBUF->icmpchksum = ~uip_chksum((u16_t *)&(ICMPBUF->type), 36); + + /* Set the IP destination address to be the source address of the + original packet. */ + tmp16= BUF->destipaddr[0]; + BUF->destipaddr[0] = BUF->srcipaddr[0]; + BUF->srcipaddr[0] = tmp16; + tmp16 = BUF->destipaddr[1]; + BUF->destipaddr[1] = BUF->srcipaddr[1]; + BUF->srcipaddr[1] = tmp16; + + /* Set our IP address as the source address. */ + BUF->srcipaddr[0] = uip_hostaddr[0]; + BUF->srcipaddr[1] = uip_hostaddr[1]; + + /* The size of the ICMP time exceeded packet is 36 + the size of the + IP header (20) = 56. */ + uip_len = 56; + ICMPBUF->len[0] = 0; + ICMPBUF->len[1] = uip_len; + + /* Fill in the other fields in the IP header. */ + ICMPBUF->vhl = 0x45; + ICMPBUF->tos = 0; + ICMPBUF->ipoffset[0] = ICMPBUF->ipoffset[1] = 0; + ICMPBUF->ttl = UIP_TTL; + ICMPBUF->proto = UIP_PROTO_ICMP; + + /* Calculate IP checksum. */ + ICMPBUF->ipchksum = 0; + ICMPBUF->ipchksum = ~(uip_ipchksum()); + + +} +/*------------------------------------------------------------------------------*/ +/** + * \internal + * Register a packet in the forwarding cache so that it won't be + * forwarded again. + */ +/*------------------------------------------------------------------------------*/ +static void +fwcache_register(void) +{ + struct fwcache_entry *fw; + int i, oldest; + + oldest = FW_TIME; + fw = NULL; + + /* Find the oldest entry in the cache. */ + for(i = 0; i < FWCACHE_SIZE; ++i) { + if(fwcache[i].timer == 0) { + fw = &fwcache[i]; + break; + } else if(fwcache[i].timer <= oldest) { + fw = &fwcache[i]; + oldest = fwcache[i].timer; + } + } + + fw->timer = FW_TIME; + fw->ipid = BUF->ipid; + fw->srcipaddr[0] = BUF->srcipaddr[0]; + fw->srcipaddr[1] = BUF->srcipaddr[1]; + fw->destipaddr[0] = BUF->destipaddr[0]; + fw->destipaddr[1] = BUF->destipaddr[1]; + fw->proto = BUF->proto; +#if notdef + fw->payload[0] = BUF->srcport; + fw->payload[1] = BUF->destport; +#endif +#if UIP_REASSEMBLY > 0 + fw->len = BUF->len; + fw->offset = BUF->ipoffset; +#endif +} +/*------------------------------------------------------------------------------*/ +/** + * \internal + * Find a network interface for the IP packet in uip_buf. + */ +/*------------------------------------------------------------------------------*/ +static struct uip_fw_netif * +find_netif(void) +{ + struct uip_fw_netif *netif; + + /* Walk through every network interface to check for a match. */ + for(netif = netifs; netif != NULL; netif = netif->next) { + if(ipaddr_maskcmp(BUF->destipaddr, netif->ipaddr, + netif->netmask)) { + /* If there was a match, we break the loop. */ + return netif; + } + } + + /* If no matching netif was found, we use default netif. */ + return defaultnetif; +} +/*------------------------------------------------------------------------------*/ +/** + * Output an IP packet on the correct network interface. + * + * The IP packet should be present in the uip_buf buffer and its + * length in the global uip_len variable. + * + * \retval UIP_FW_ZEROLEN Indicates that a zero-length packet + * transmission was attempted and that no packet was sent. + * + * \retval UIP_FW_NOROUTE No suitable network interface could be found + * for the outbound packet, and the packet was not sent. + * + * \return The return value from the actual network interface output + * function is passed unmodified as a return value. + */ +/*------------------------------------------------------------------------------*/ +u8_t +uip_fw_output(void) +{ + struct uip_fw_netif *netif; + + if(uip_len == 0) { + return UIP_FW_ZEROLEN; + } + + fwcache_register(); + +#if UIP_BROADCAST + /* Link local broadcasts go out on all interfaces. */ + if(/*BUF->proto == UIP_PROTO_UDP &&*/ + BUF->destipaddr[0] == 0xffff && + BUF->destipaddr[1] == 0xffff) { + if(defaultnetif != NULL) { + defaultnetif->output(); + } + for(netif = netifs; netif != NULL; netif = netif->next) { + netif->output(); + } + return UIP_FW_OK; + } +#endif /* UIP_BROADCAST */ + + netif = find_netif(); + /* printf("uip_fw_output: netif %p ->output %p len %d\n", netif, + netif->output, + uip_len);*/ + + if(netif == NULL) { + return UIP_FW_NOROUTE; + } + /* If we now have found a suitable network interface, we call its + output function to send out the packet. */ + return netif->output(); +} +/*------------------------------------------------------------------------------*/ +/** + * Forward an IP packet in the uip_buf buffer. + * + * + * + * \return UIP_FW_FORWARDED if the packet was forwarded, UIP_FW_LOCAL if + * the packet should be processed locally. + */ +/*------------------------------------------------------------------------------*/ +u8_t +uip_fw_forward(void) +{ + struct fwcache_entry *fw; + + /* First check if the packet is destined for ourselves and return 0 + to indicate that the packet should be processed locally. */ + if(BUF->destipaddr[0] == uip_hostaddr[0] && + BUF->destipaddr[1] == uip_hostaddr[1]) { + return UIP_FW_LOCAL; + } + + /* If we use ping IP address configuration, and our IP address is + not yet configured, we should intercept all ICMP echo packets. */ +#if UIP_PINGADDRCONF + if((uip_hostaddr[0] | uip_hostaddr[1]) == 0 && + BUF->proto == UIP_PROTO_ICMP && + ICMPBUF->type == ICMP_ECHO) { + return UIP_FW_LOCAL; + } +#endif /* UIP_PINGADDRCONF */ + + /* Check if the packet is in the forwarding cache already, and if so + we drop it. */ + + for(fw = fwcache; fw < &fwcache[FWCACHE_SIZE]; ++fw) { + if(fw->timer != 0 && +#if UIP_REASSEMBLY > 0 + fw->len == BUF->len && + fw->offset == BUF->ipoffset && +#endif + fw->ipid == BUF->ipid && + fw->srcipaddr[0] == BUF->srcipaddr[0] && + fw->srcipaddr[1] == BUF->srcipaddr[1] && + fw->destipaddr[0] == BUF->destipaddr[0] && + fw->destipaddr[1] == BUF->destipaddr[1] && +#if notdef + fw->payload[0] == BUF->srcport && + fw->payload[1] == BUF->destport && +#endif + fw->proto == BUF->proto) { + /* Drop packet. */ + return UIP_FW_FORWARDED; + } + } + + /* If the TTL reaches zero we produce an ICMP time exceeded message + in the uip_buf buffer and forward that packet back to the sender + of the packet. */ + if(BUF->ttl <= 1) { + /* No time exceeded for broadcasts and multicasts! */ + if(BUF->destipaddr[0] == 0xffff && BUF->destipaddr[1] == 0xffff) { + return UIP_FW_LOCAL; + } + time_exceeded(); + } + + /* Decrement the TTL (time-to-live) value in the IP header */ + BUF->ttl = BUF->ttl - 1; + + /* Update the IP checksum. */ + if(BUF->ipchksum >= HTONS(0xffff - 0x0100)) { + BUF->ipchksum = BUF->ipchksum + HTONS(0x0100) + 1; + } else { + BUF->ipchksum = BUF->ipchksum + HTONS(0x0100); + } + + if(uip_len > 0) { + uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_TCPIP_HLEN]; + uip_fw_output(); + } + +#if UIP_BROADCAST + if(BUF->destipaddr[0] == 0xffff && BUF->destipaddr[1] == 0xffff) { + return UIP_FW_LOCAL; + } +#endif /* UIP_BROADCAST */ + + /* Return non-zero to indicate that the packet was forwarded and that no + other processing should be made. */ + return UIP_FW_FORWARDED; +} +/*------------------------------------------------------------------------------*/ +/** + * Register a network interface with the forwarding module. + * + * \param netif A pointer to the network interface that is to be + * registered. + */ +/*------------------------------------------------------------------------------*/ +void +uip_fw_register(struct uip_fw_netif *netif) +{ + netif->next = netifs; + netifs = netif; +} +/*------------------------------------------------------------------------------*/ +/** + * Register a default network interface. + * + * All packets that don't go out on any of the other interfaces will + * be routed to the default interface. + * + * \param netif A pointer to the network interface that is to be + * registered. + */ +/*------------------------------------------------------------------------------*/ +void +uip_fw_default(struct uip_fw_netif *netif) +{ + defaultnetif = netif; +} +/*------------------------------------------------------------------------------*/ +/** + * Perform periodic processing. + */ +/*------------------------------------------------------------------------------*/ +void +uip_fw_periodic(void) +{ + struct fwcache_entry *fw; + for(fw = fwcache; fw < &fwcache[FWCACHE_SIZE]; ++fw) { + if(fw->timer > 0) { + --fw->timer; + } + } +} +/*------------------------------------------------------------------------------*/ diff --git a/brcm_iscsi_uio/src/uip/uip-fw.h b/brcm_iscsi_uio/src/uip/uip-fw.h new file mode 100644 index 0000000..9033850 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip-fw.h @@ -0,0 +1,176 @@ +/** + * \addtogroup uipfw + * @{ + */ + +/** + * \file + * uIP packet forwarding header file. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: uip-fw.h,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ +#ifndef __UIP_FW_H__ +#define __UIP_FW_H__ + +#include "uip.h" + +/** + * Representation of a uIP network interface. + */ +struct uip_fw_netif { + struct uip_fw_netif *next; /**< Pointer to the next interface when + linked in a list. */ + u16_t ipaddr[2]; /**< The IP address of this interface. */ + u16_t netmask[2]; /**< The netmask of the interface. */ + u8_t (* output)(void); + /**< A pointer to the function that + sends a packet. */ +}; + +/** + * Intantiating macro for a uIP network interface. + * + * Example: + \code + struct uip_fw_netif slipnetif = + {UIP_FW_NETIF(192,168,76,1, 255,255,255,0, slip_output)}; + \endcode + * \param ip1,ip2,ip3,ip4 The IP address of the network interface. + * + * \param nm1,nm2,nm3,nm4 The netmask of the network interface. + * + * \param outputfunc A pointer to the output function of the network interface. + * + * \hideinitializer + */ +#define UIP_FW_NETIF(ip1,ip2,ip3,ip4, nm1,nm2,nm3,nm4, outputfunc) \ + NULL, \ + {HTONS((ip1 << 8) | ip2), HTONS((ip3 << 8) | ip4)}, \ + {HTONS((nm1 << 8) | nm2), HTONS((nm3 << 8) | nm4)}, \ + outputfunc + +/** + * Set the IP address of a network interface. + * + * \param netif A pointer to the uip_fw_netif structure for the network interface. + * + * \param addr A pointer to an IP address. + * + * \hideinitializer + */ +#define uip_fw_setipaddr(netif, addr) \ + do { (netif)->ipaddr[0] = ((u16_t *)(addr))[0]; \ + (netif)->ipaddr[1] = ((u16_t *)(addr))[1]; } while(0) +/** + * Set the netmask of a network interface. + * + * \param netif A pointer to the uip_fw_netif structure for the network interface. + * + * \param addr A pointer to an IP address representing the netmask. + * + * \hideinitializer + */ +#define uip_fw_setnetmask(netif, addr) \ + do { (netif)->netmask[0] = ((u16_t *)(addr))[0]; \ + (netif)->netmask[1] = ((u16_t *)(addr))[1]; } while(0) + +void uip_fw_init(void); +u8_t uip_fw_forward(void); +u8_t uip_fw_output(void); +void uip_fw_register(struct uip_fw_netif *netif); +void uip_fw_default(struct uip_fw_netif *netif); +void uip_fw_periodic(void); + + +/** + * A non-error message that indicates that a packet should be + * processed locally. + * + * \hideinitializer + */ +#define UIP_FW_LOCAL 0 + +/** + * A non-error message that indicates that something went OK. + * + * \hideinitializer + */ +#define UIP_FW_OK 0 + +/** + * A non-error message that indicates that a packet was forwarded. + * + * \hideinitializer + */ +#define UIP_FW_FORWARDED 1 + +/** + * A non-error message that indicates that a zero-length packet + * transmission was attempted, and that no packet was sent. + * + * \hideinitializer + */ +#define UIP_FW_ZEROLEN 2 + +/** + * An error message that indicates that a packet that was too large + * for the outbound network interface was detected. + * + * \hideinitializer + */ +#define UIP_FW_TOOLARGE 3 + +/** + * An error message that indicates that no suitable interface could be + * found for an outbound packet. + * + * \hideinitializer + */ +#define UIP_FW_NOROUTE 4 + +/** + * An error message that indicates that a packet that should be + * forwarded or output was dropped. + * + * \hideinitializer + */ +#define UIP_FW_DROPPED 5 + + +#endif /* __UIP_FW_H__ */ + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/uip-neighbor.c b/brcm_iscsi_uio/src/uip/uip-neighbor.c new file mode 100644 index 0000000..c4b11bf --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip-neighbor.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: uip-neighbor.c,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ + +/** + * \file + * Database of link-local neighbors, used by IPv6 code and + * to be used by a future ARP code rewrite. + * \author + * Adam Dunkels + */ + +#include "logger.h" +#include "uip.h" +#include "uip-neighbor.h" + +#include +#include + +#define MAX_TIME 128 + +/*---------------------------------------------------------------------------*/ +void +uip_neighbor_init(struct uip_stack *ustack) +{ + int i; + + for(i = 0; i < UIP_NEIGHBOR_ENTRIES; ++i) { + ustack->neighbor_entries[i].time = MAX_TIME; + } +} +/*---------------------------------------------------------------------------*/ +#if 0 +void +uip_neighbor_periodic(void) +{ + int i; + + for(i = 0; i < UIP_NEIGHBOR_ENTRIES; ++i) { + if(entries[i].time < MAX_TIME) { + entries[i].time++; + } + } +} +#endif +/*---------------------------------------------------------------------------*/ +void +uip_neighbor_add(struct uip_stack *ustack, + uip_ip6addr_t ipaddr, struct uip_eth_addr *addr) +{ + int i, oldest; + u8_t oldest_time; + char buf[128]; + + inet_ntop(AF_INET6, ipaddr, buf, sizeof(buf)); + + LOG_INFO("Adding neighbor %s with link address %02x:%02x:%02x:%02x:%02x:%02x\n", + buf, + addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + + /* Find the first unused entry or the oldest used entry. */ + oldest_time = 0; + oldest = 0; + for(i = 0; i < UIP_NEIGHBOR_ENTRIES; ++i) { + if(ustack->neighbor_entries[i].time == MAX_TIME) { + oldest = i; + break; + } + if(uip_ip6addr_cmp(ustack->neighbor_entries[i].ipaddr, ipaddr)) { + oldest = i; + break; + } + if(ustack->neighbor_entries[i].time > oldest_time) { + oldest = i; + oldest_time = ustack->neighbor_entries[i].time; + } + } + + /* Use the oldest or first free entry (either pointed to by the + "oldest" variable). */ + ustack->neighbor_entries[oldest].time = 0; + uip_ip6addr_copy(ustack->neighbor_entries[oldest].ipaddr, ipaddr); + memcpy(&ustack->neighbor_entries[oldest].addr, + addr, sizeof(struct uip_eth_addr)); +} +/*---------------------------------------------------------------------------*/ +static struct neighbor_entry * +find_entry(struct uip_stack *ustack, + uip_ip6addr_t ipaddr) +{ + int i; + char buf[128]; + inet_ntop(AF_INET6, ipaddr, buf, sizeof(buf)); + + for(i = 0; i < UIP_NEIGHBOR_ENTRIES; ++i) { + if(uip_ip6addr_cmp(ustack->neighbor_entries[i].ipaddr, ipaddr)) { + + LOG_DEBUG("found %s at %02x:%02x:%02x:%02x:%02x:%02x\n", + buf, + ustack->neighbor_entries[i].addr.addr[0], + ustack->neighbor_entries[i].addr.addr[1], + ustack->neighbor_entries[i].addr.addr[2], + ustack->neighbor_entries[i].addr.addr[3], + ustack->neighbor_entries[i].addr.addr[4], + ustack->neighbor_entries[i].addr.addr[5]); + + return &ustack->neighbor_entries[i]; + } + } + + + LOG_WARN("Could not find entry: %s", + buf); + + return NULL; +} +/*---------------------------------------------------------------------------*/ +void +uip_neighbor_update(struct uip_stack *ustack, uip_ip6addr_t ipaddr) +{ + struct neighbor_entry *e; + + e = find_entry(ustack, ipaddr); + if(e != NULL) { + e->time = 0; + } +} +/*---------------------------------------------------------------------------*/ +struct uip_eth_addr * +uip_neighbor_lookup(struct uip_stack *ustack, uip_ip6addr_t ipaddr) +{ + struct neighbor_entry *e; + + e = find_entry(ustack, ipaddr); + if(e != NULL) { + /* printf("Lookup neighbor with link address %02x:%02x:%02x:%02x:%02x:%02x\n", + e->addr.addr.addr[0], e->addr.addr.addr[1], e->addr.addr.addr[2], e->addr.addr.addr[3], + e->addr.addr.addr[4], e->addr.addr.addr[5]);*/ + + return &e->addr; + } + return NULL; +} + +void +uip_neighbor_out(struct uip_stack *ustack) +{ + struct neighbor_entry *e; + struct uip_ipv6_hdr *ipv6_hdr = (struct uip_ipv6_hdr *)IPv6_BUF(ustack); + + /* Find the destination IP address in the neighbor table and construct + the Ethernet header. If the destination IP addres isn't on the + local network, we use the default router's IP address instead. + + If not ARP table entry is found, we overwrite the original IP + packet with an ARP request for the IP address. */ + e = find_entry(ustack, ipv6_hdr->destipaddr); + + if(e == NULL) + /* TODO determine what to do in IPv6 case */ + return; + + memcpy(ETH_BUF(ustack)->dest.addr, &e->addr, 6); + memcpy(ETH_BUF(ustack)->src.addr, ustack->uip_ethaddr.addr, 6); + + ETH_BUF(ustack)->type = htons(UIP_ETHTYPE_IPv6); + + ustack->uip_len += sizeof(struct uip_eth_hdr); +} + +/*---------------------------------------------------------------------------*/ diff --git a/brcm_iscsi_uio/src/uip/uip-neighbor.h b/brcm_iscsi_uio/src/uip/uip-neighbor.h new file mode 100644 index 0000000..6664e11 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip-neighbor.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: uip-neighbor.h,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ + +/** + * \file + * Header file for database of link-local neighbors, used by + * IPv6 code and to be used by future ARP code. + * \author + * Adam Dunkels + */ + +#ifndef __UIP_NEIGHBOR_H__ +#define __UIP_NEIGHBOR_H__ + +#include "uip.h" +#include "uip_eth.h" + +void uip_neighbor_init(struct uip_stack *ustack); +void uip_neighbor_add(struct uip_stack *ustack, + uip_ip6addr_t ipaddr, struct uip_eth_addr *addr); +void uip_neighbor_update(struct uip_stack *ustack, uip_ip6addr_t ipaddr); +struct uip_eth_addr *uip_neighbor_lookup(struct uip_stack *ustack, + uip_ip6addr_t ipaddr); +void uip_neighbor_periodic(void); +void uip_neighbor_out(struct uip_stack *ustack); + + +#endif /* __UIP-NEIGHBOR_H__ */ diff --git a/brcm_iscsi_uio/src/uip/uip-split.c b/brcm_iscsi_uio/src/uip/uip-split.c new file mode 100644 index 0000000..a910ee6 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip-split.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: uip-split.c,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ + +#include + +#include "uip-split.h" +#include "uip.h" +#include "uip-fw.h" +#include "uip_arch.h" + + + +#define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN]) + +/*-----------------------------------------------------------------------------*/ +void +uip_split_output(void) +{ + u16_t tcplen, len1, len2; + + /* We only try to split maximum sized TCP segments. */ + if(BUF->proto == UIP_PROTO_TCP && + uip_len == UIP_BUFSIZE - UIP_LLH_LEN) { + + tcplen = uip_len - UIP_TCPIP_HLEN; + /* Split the segment in two. If the original packet length was + odd, we make the second packet one byte larger. */ + len1 = len2 = tcplen / 2; + if(len1 + len2 < tcplen) { + ++len2; + } + + /* Create the first packet. This is done by altering the length + field of the IP header and updating the checksums. */ + uip_len = len1 + UIP_TCPIP_HLEN; +#if UIP_CONF_IPV6 + /* For IPv6, the IP length field does not include the IPv6 IP header + length. */ + BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); + BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); +#else /* UIP_CONF_IPV6 */ + BUF->len[0] = uip_len >> 8; + BUF->len[1] = uip_len & 0xff; +#endif /* UIP_CONF_IPV6 */ + + /* Recalculate the TCP checksum. */ + BUF->tcpchksum = 0; + BUF->tcpchksum = ~(uip_tcpchksum()); + +#if !UIP_CONF_IPV6 + /* Recalculate the IP checksum. */ + BUF->ipchksum = 0; + BUF->ipchksum = ~(uip_ipchksum()); +#endif /* UIP_CONF_IPV6 */ + + /* Transmit the first packet. */ + /* uip_fw_output();*/ + tcpip_output(); + + /* Now, create the second packet. To do this, it is not enough to + just alter the length field, but we must also update the TCP + sequence number and point the uip_appdata to a new place in + memory. This place is detemined by the length of the first + packet (len1). */ + uip_len = len2 + UIP_TCPIP_HLEN; +#if UIP_CONF_IPV6 + /* For IPv6, the IP length field does not include the IPv6 IP header + length. */ + BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); + BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); +#else /* UIP_CONF_IPV6 */ + BUF->len[0] = uip_len >> 8; + BUF->len[1] = uip_len & 0xff; +#endif /* UIP_CONF_IPV6 */ + + /* uip_appdata += len1;*/ + memcpy(uip_appdata, (u8_t *)uip_appdata + len1, len2); + + uip_add32(BUF->seqno, len1); + BUF->seqno[0] = uip_acc32[0]; + BUF->seqno[1] = uip_acc32[1]; + BUF->seqno[2] = uip_acc32[2]; + BUF->seqno[3] = uip_acc32[3]; + + /* Recalculate the TCP checksum. */ + BUF->tcpchksum = 0; + BUF->tcpchksum = ~(uip_tcpchksum()); + +#if !UIP_CONF_IPV6 + /* Recalculate the IP checksum. */ + BUF->ipchksum = 0; + BUF->ipchksum = ~(uip_ipchksum()); +#endif /* UIP_CONF_IPV6 */ + + /* Transmit the second packet. */ + /* uip_fw_output();*/ + tcpip_output(); + } else { + /* uip_fw_output();*/ + tcpip_output(); + } + +} +/*-----------------------------------------------------------------------------*/ diff --git a/brcm_iscsi_uio/src/uip/uip-split.h b/brcm_iscsi_uio/src/uip/uip-split.h new file mode 100644 index 0000000..c2c1789 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip-split.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2004, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * Author: Adam Dunkels + * + * $Id: uip-split.h,v 1.2 2006/06/12 08:00:30 adam Exp $ + */ +/** + * \addtogroup uip + * @{ + */ + +/** + * \defgroup uipsplit uIP TCP throughput booster hack + * @{ + * + * The basic uIP TCP implementation only allows each TCP connection to + * have a single TCP segment in flight at any given time. Because of + * the delayed ACK algorithm employed by most TCP receivers, uIP's + * limit on the amount of in-flight TCP segments seriously reduces the + * maximum achievable throughput for sending data from uIP. + * + * The uip-split module is a hack which tries to remedy this + * situation. By splitting maximum sized outgoing TCP segments into + * two, the delayed ACK algorithm is not invoked at TCP + * receivers. This improves the throughput when sending data from uIP + * by orders of magnitude. + * + * The uip-split module uses the uip-fw module (uIP IP packet + * forwarding) for sending packets. Therefore, the uip-fw module must + * be set up with the appropriate network interfaces for this module + * to work. + */ + + +/** + * \file + * Module for splitting outbound TCP segments in two to avoid the + * delayed ACK throughput degradation. + * \author + * Adam Dunkels + * + */ + +#ifndef __UIP_SPLIT_H__ +#define __UIP_SPLIT_H__ + +/** + * Handle outgoing packets. + * + * This function inspects an outgoing packet in the uip_buf buffer and + * sends it out using the uip_fw_output() function. If the packet is a + * full-sized TCP segment it will be split into two segments and + * transmitted separately. This function should be called instead of + * the actual device driver output function, or the uip_fw_output() + * function. + * + * The headers of the outgoing packet is assumed to be in the uip_buf + * buffer and the payload is assumed to be wherever uip_appdata + * points. The length of the outgoing packet is assumed to be in the + * uip_len variable. + * + */ +void uip_split_output(void); + +#endif /* __UIP_SPLIT_H__ */ + +/** @} */ +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/uip.c b/brcm_iscsi_uio/src/uip/uip.c new file mode 100644 index 0000000..a702561 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip.c @@ -0,0 +1,2394 @@ +#include +#include "uip.h" +#include "dhcpc.h" +#include "brcm_iscsi.h" + +/** + * \defgroup uip The uIP TCP/IP stack + * @{ + * + * uIP is an implementation of the TCP/IP protocol stack intended for + * small 8-bit and 16-bit microcontrollers. + * + * uIP provides the necessary protocols for Internet communication, + * with a very small code footprint and RAM requirements - the uIP + * code size is on the order of a few kilobytes and RAM usage is on + * the order of a few hundred bytes. + */ + +/** + * \file + * The uIP TCP/IP stack code. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uip.c,v 1.65 2006/06/11 21:46:39 adam Exp $ + * + */ + +/* + * uIP is a small implementation of the IP, UDP and TCP protocols (as + * well as some basic ICMP stuff). The implementation couples the IP, + * UDP, TCP and the application layers very tightly. To keep the size + * of the compiled code down, this code frequently uses the goto + * statement. While it would be possible to break the uip_process() + * function into many smaller functions, this would increase the code + * size because of the overhead of parameter passing and the fact that + * the optimier would not be as efficient. + * + * The principle is that we have a small buffer, called the uip_buf, + * in which the device driver puts an incoming packet. The TCP/IP + * stack parses the headers in the packet, and calls the + * application. If the remote host has sent data to the application, + * this data is present in the uip_buf and the application read the + * data from there. It is up to the application to put this data into + * a byte stream if needed. The application will not be fed with data + * that is out of sequence. + * + * If the application whishes to send data to the peer, it should put + * its data into the uip_buf. The uip_appdata pointer points to the + * first available byte. The TCP/IP stack will calculate the + * checksums, and fill in the necessary header fields and finally send + * the packet back to the peer. +*/ + +#include "logger.h" + +#include "uip.h" +#include "uipopt.h" +#include "uip_arch.h" +#include "uip_eth.h" +#include "uip-neighbor.h" + +#include + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "uip " + +static const uip_ip6addr_t all_ones_addr6 = + {0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff}; +static const uip_ip4addr_t all_ones_addr4 = + {0xffff,0xffff}; + +const uip_ip6addr_t all_zeroes_addr6 = + {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; +const uip_ip4addr_t all_zeroes_addr4 = + {0x0000,0x0000}; + +/* Structures and definitions. */ +#define TCP_FIN 0x01 +#define TCP_SYN 0x02 +#define TCP_RST 0x04 +#define TCP_PSH 0x08 +#define TCP_ACK 0x10 +#define TCP_URG 0x20 +#define TCP_CTL 0x3f + +#define TCP_OPT_END 0 /* End of TCP options list */ +#define TCP_OPT_NOOP 1 /* "No-operation" TCP option */ +#define TCP_OPT_MSS 2 /* Maximum segment size TCP option */ + +#define TCP_OPT_MSS_LEN 4 /* Length of TCP MSS option. */ + +#define ICMP_ECHO_REPLY 0 +#define ICMP_ECHO 8 + +#define ICMP6_ECHO_REPLY 129 +#define ICMP6_ECHO 128 +#define ICMP6_NEIGHBOR_SOLICITATION 135 +#define ICMP6_NEIGHBOR_ADVERTISEMENT 136 + +#define ICMP6_FLAG_S (1 << 6) + +#define ICMP6_OPTION_SOURCE_LINK_ADDRESS 1 +#define ICMP6_OPTION_TARGET_LINK_ADDRESS 2 + + +/* Macros. */ +#define FBUF ((struct uip_tcpip_hdr *)&uip_reassbuf[0]) +#define UDPBUF(ustack) ((struct uip_udpip_hdr *)&ustack->uip_buf[UIP_LLH_LEN]) + +/****************************************************************************** + * Utility Functions + *****************************************************************************/ +int is_ipv6(struct uip_stack *ustack) { + return ntohs(ETH_BUF(ustack)->type) == UIP_ETHTYPE_IPv6 ? 1 : 0; +} + +void uip_sethostaddr4(struct uip_stack *ustack, uip_ip4addr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ip4addr_copy(ustack->hostaddr, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setdraddr4(struct uip_stack *ustack, uip_ip4addr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ip4addr_copy(ustack->default_route_addr, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setnetmask4(struct uip_stack *ustack, uip_ip4addr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ip4addr_copy(ustack->netmask, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setethernetmac(struct uip_stack *ustack, uint8_t *mac) +{ + pthread_mutex_lock(&ustack->lock); + memcpy(ustack->uip_ethaddr.addr, (mac), 6); + pthread_mutex_unlock(&ustack->lock); +} + +void set_uip_stack(struct uip_stack *ustack, + uip_ip4addr_t *ip, + uip_ip4addr_t *netmask, + uip_ip4addr_t *default_route, + uint8_t *mac_addr) +{ + uip_sethostaddr4(ustack, ip); + uip_setnetmask4(ustack, netmask); + uip_setdraddr4(ustack, default_route); + uip_setethernetmac(ustack, mac_addr); + + set_ipv6_link_local_address(ustack); + uip_ip6addr_copy(ustack->hostaddr6, ustack->link_local_addr); + /* resolv_conf(s->dnsaddr); */ + + if(LOG_LEVEL_INFO <= main_log.level) + { + char buf[128]; + + inet_ntop(AF_INET6, ustack->hostaddr6, + buf, sizeof(buf)); + + LOG_INFO(PFX "ustack->uip_hostaddr6 IP: %s", buf); + + inet_ntop(AF_INET6, ustack->link_local_addr, + buf, sizeof(buf)); + + LOG_INFO(PFX "ustack->link_local_addr IP: %s", buf); + } +} + +#if ! UIP_ARCH_ADD32 +void +uip_add32(u8_t *op32, u16_t op16, u8_t *uip_acc32) +{ + uip_acc32[3] = op32[3] + (op16 & 0xff); + uip_acc32[2] = op32[2] + (op16 >> 8); + uip_acc32[1] = op32[1]; + uip_acc32[0] = op32[0]; + + if(uip_acc32[2] < (op16 >> 8)) { + ++uip_acc32[1]; + if(uip_acc32[1] == 0) { + ++uip_acc32[0]; + } + } + + + if(uip_acc32[3] < (op16 & 0xff)) { + ++uip_acc32[2]; + if(uip_acc32[2] == 0) { + ++uip_acc32[1]; + if(uip_acc32[1] == 0) { + ++uip_acc32[0]; + } + } + } +} + +#endif /* UIP_ARCH_ADD32 */ + +#if ! UIP_ARCH_CHKSUM +/*---------------------------------------------------------------------------*/ +static u16_t +chksum(u16_t sum, const u8_t *data, u16_t len) +{ + u16_t t; + const u8_t *dataptr; + const u8_t *last_byte; + + dataptr = data; + last_byte = data + len - 1; + + while(dataptr < last_byte) { /* At least two more bytes */ + t = (dataptr[0] << 8) + dataptr[1]; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + dataptr += 2; + } + + if(dataptr == last_byte) { + t = (dataptr[0] << 8) + 0; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + } + + /* Return sum in host byte order. */ + return sum; +} +/*---------------------------------------------------------------------------*/ +u16_t +uip_chksum(u16_t *data, u16_t len) +{ + return htons(chksum(0, (u8_t *)data, len)); +} +/*---------------------------------------------------------------------------*/ +#ifndef UIP_ARCH_IPCHKSUM +u16_t +uip_ipchksum(struct uip_stack *ustack) +{ + u16_t sum; + u16_t uip_iph_len; + + if(is_ipv6(ustack)) + uip_iph_len = UIP_IPv6_H_LEN; + else + uip_iph_len = UIP_IPv4_H_LEN; + + sum = chksum(0, &ustack->uip_buf[UIP_LLH_LEN], uip_iph_len); + return (sum == 0) ? 0xffff : htons(sum); +} +#endif +/*---------------------------------------------------------------------------*/ +static u16_t +upper_layer_chksum(struct uip_stack *ustack, u8_t proto) +{ + u16_t upper_layer_len; + u16_t sum; + struct uip_tcp_ipv4_hdr *tcp_ipv4_hdr = NULL; + + tcp_ipv4_hdr = (struct uip_tcp_ipv4_hdr *)&ustack->uip_buf[UIP_LLH_LEN]; + + if(is_ipv6(ustack)) + upper_layer_len = (((u16_t)(IPv6_BUF(ustack)->len[0]) << 8) + + IPv6_BUF(ustack)->len[1]); + else + upper_layer_len = (((u16_t)(tcp_ipv4_hdr->len[0]) << 8) + + tcp_ipv4_hdr->len[1]) - UIP_IPv4_H_LEN; + + /* First sum pseudoheader. */ + + /* IP protocol and length fields. This addition cannot carry. */ + sum = upper_layer_len + proto; + + /* Sum IP source and destination addresses. */ + if(is_ipv6(ustack)) { + sum = chksum(sum, (u8_t *)&IPv6_BUF(ustack)->srcipaddr[0], 2 * sizeof(uip_ip6addr_t)); + /* Sum TCP header and data. */ + sum = chksum(sum, &ustack->uip_buf[UIP_IPv6_H_LEN + UIP_LLH_LEN], + upper_layer_len); + + } else { + sum = chksum(sum, (u8_t *)&tcp_ipv4_hdr->srcipaddr[0], 2 * sizeof(uip_ip4addr_t)); + /* Sum TCP header and data. */ + sum = chksum(sum, &ustack->uip_buf[UIP_IPv4_H_LEN + UIP_LLH_LEN], + upper_layer_len); + } + + return (sum == 0) ? 0xffff : htons(sum); +} +/*---------------------------------------------------------------------------*/ + +u16_t +uip_icmp6chksum(struct uip_stack *ustack) +{ + return upper_layer_chksum(ustack, UIP_PROTO_ICMP6); +} + +/*---------------------------------------------------------------------------*/ +u16_t +uip_tcpchksum(struct uip_stack *ustack) +{ + return upper_layer_chksum(ustack, UIP_PROTO_TCP); +} +/*---------------------------------------------------------------------------*/ +#if UIP_UDP_CHECKSUMS +u16_t +uip_udpchksum(struct uip_stack *ustack) +{ + return upper_layer_chksum(ustack, UIP_PROTO_UDP); +} +#endif /* UIP_UDP_CHECKSUMS */ +#endif /* UIP_ARCH_CHKSUM */ +/*---------------------------------------------------------------------------*/ +void +uip_init(struct uip_stack *ustack, uint8_t ipv6_enabled) +{ + u8_t c; + + for(c = 0; c < UIP_LISTENPORTS; ++c) { + ustack->uip_listenports[c] = 0; + } + for(c = 0; c < UIP_CONNS; ++c) { + ustack->uip_conns[c].tcpstateflags = UIP_CLOSED; + } +#if UIP_ACTIVE_OPEN + ustack->lastport = 1024; +#endif /* UIP_ACTIVE_OPEN */ + +#if UIP_UDP + for(c = 0; c < UIP_UDP_CONNS; ++c) { + ustack->uip_udp_conns[c].lport = 0; + } +#endif /* UIP_UDP */ + + + /* IPv4 initialization. */ +#if UIP_FIXEDADDR == 0 + /* uip_hostaddr[0] = uip_hostaddr[1] = 0;*/ +#endif /* UIP_FIXEDADDR */ + + /* zero out the uIP statistics */ + memset(&ustack->stats, 0, sizeof(ustack->stats)); + + /* prepare the uIP lock */ + pthread_mutex_init(&ustack->lock, NULL); + + if(ipv6_enabled) { + ustack->enable_IPv6 = UIP_SUPPORT_IPv6_ENABLED; + uip_neighbor_init(ustack); + }else + ustack->enable_IPv6 = UIP_SUPPORT_IPv6_DISABLED; + + ustack->dhcpc = NULL; +} +void +uip_reset(struct uip_stack *ustack) +{ + /* There was an associated DHCP object, this memory needs to be + * freed */ + if(ustack->dhcpc) + free(ustack->dhcpc); + + memset(ustack, 0, sizeof(*ustack)); +} +/*---------------------------------------------------------------------------*/ +#if UIP_ACTIVE_OPEN +struct uip_conn * +uip_connect(struct uip_stack *ustack, uip_ip4addr_t *ripaddr, u16_t rport) +{ + u8_t c; + register struct uip_conn *conn, *cconn; + + /* Find an unused local port. */ + again: + ++ustack->lastport; + + if(ustack->lastport >= 32000) { + ustack->lastport = 4096; + } + + /* Check if this port is already in use, and if so try to find + another one. */ + for(c = 0; c < UIP_CONNS; ++c) { + conn = &ustack->uip_conns[c]; + if(conn->tcpstateflags != UIP_CLOSED && + conn->lport == htons(ustack->lastport)) { + goto again; + } + } + + conn = 0; + for(c = 0; c < UIP_CONNS; ++c) { + cconn = &ustack->uip_conns[c]; + if(cconn->tcpstateflags == UIP_CLOSED) { + conn = cconn; + break; + } + if(cconn->tcpstateflags == UIP_TIME_WAIT) { + if(conn == 0 || + cconn->timer > conn->timer) { + conn = cconn; + } + } + } + + if(conn == 0) { + return 0; + } + + conn->tcpstateflags = UIP_SYN_SENT; + + conn->snd_nxt[0] = ustack->iss[0]; + conn->snd_nxt[1] = ustack->iss[1]; + conn->snd_nxt[2] = ustack->iss[2]; + conn->snd_nxt[3] = ustack->iss[3]; + + conn->initialmss = conn->mss = UIP_TCP_MSS; + + conn->len = 1; /* TCP length of the SYN is one. */ + conn->nrtx = 0; + conn->timer = 1; /* Send the SYN next time around. */ + conn->rto = UIP_RTO; + conn->sa = 0; + conn->sv = 16; /* Initial value of the RTT variance. */ + conn->lport = htons(ustack->lastport); + conn->rport = rport; + uip_ip4addr_copy(&conn->ripaddr, ripaddr); + + return conn; +} +#endif /* UIP_ACTIVE_OPEN */ +/*---------------------------------------------------------------------------*/ +#if UIP_UDP +struct uip_udp_conn * +uip_udp_new(struct uip_stack *ustack, uip_ip4addr_t *ripaddr, u16_t rport) +{ + u8_t c; + register struct uip_udp_conn *conn; + + /* Find an unused local port. */ + again: + ++ustack->lastport; + + if(ustack->lastport >= 32000) { + ustack->lastport = 4096; + } + + for(c = 0; c < UIP_UDP_CONNS; ++c) { + if(ustack->uip_udp_conns[c].lport == htons(ustack->lastport)) { + goto again; + } + } + + + conn = 0; + for(c = 0; c < UIP_UDP_CONNS; ++c) { + if(ustack->uip_udp_conns[c].lport == 0) { + conn = &ustack->uip_udp_conns[c]; + break; + } + } + + if(conn == 0) { + return 0; + } + + conn->lport = htons(ustack->lastport); + conn->rport = rport; + if(ripaddr == NULL) { + memset(conn->ripaddr, 0, sizeof(uip_ip4addr_t)); + } else { + uip_ip4addr_copy(&conn->ripaddr, ripaddr); + } + conn->ttl = UIP_TTL; + + return conn; +} +#endif /* UIP_UDP */ +/*---------------------------------------------------------------------------*/ +void +uip_unlisten(struct uip_stack *ustack, u16_t port) +{ + u8_t c; + + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(ustack->uip_listenports[c] == port) { + ustack->uip_listenports[c] = 0; + return; + } + } +} +/*---------------------------------------------------------------------------*/ +void +uip_listen(struct uip_stack *ustack, u16_t port) +{ + u8_t c; + + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(ustack->uip_listenports[c] == 0) { + ustack->uip_listenports[c] = port; + return; + } + } +} + + +/** + * Is new incoming data available? + * + * Will reduce to non-zero if there is new data for the application + * present at the uip_appdata pointer. The size of the data is + * avaliable through the uip_len variable. + * + * \hideinitializer + */ +int uip_newdata(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_NEWDATA; +} + +/** + * Has previously sent data been acknowledged? + * + * Will reduce to non-zero if the previously sent data has been + * acknowledged by the remote host. This means that the application + * can send new data. + * + * \hideinitializer + */ +#define uip_acked() (uip_flags & UIP_ACKDATA) + +/** + * Has the connection just been connected? + * + * Reduces to non-zero if the current connection has been connected to + * a remote host. This will happen both if the connection has been + * actively opened (with uip_connect()) or passively opened (with + * uip_listen()). + * + * \hideinitializer + */ +int uip_connected(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_CONNECTED; +} + +/** + * Has the connection been closed by the other end? + * + * Is non-zero if the connection has been closed by the remote + * host. The application may then do the necessary clean-ups. + * + * \hideinitializer + */ +int uip_closed(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_CLOSE; +} + +/** + * Has the connection been aborted by the other end? + * + * Non-zero if the current connection has been aborted (reset) by the + * remote host. + * + * \hideinitializer + */ +int uip_aborted(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_ABORT; +} + +/** + * Has the connection timed out? + * + * Non-zero if the current connection has been aborted due to too many + * retransmissions. + * + * \hideinitializer + */ +int uip_timedout(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_TIMEDOUT; +} + + +/** + * Do we need to retransmit previously data? + * + * Reduces to non-zero if the previously sent data has been lost in + * the network, and the application should retransmit it. The + * application should send the exact same data as it did the last + * time, using the uip_send() function. + * + * \hideinitializer + */ +int uip_rexmit(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_REXMIT; +} + +/** + * Is the connection being polled by uIP? + * + * Is non-zero if the reason the application is invoked is that the + * current connection has been idle for a while and should be + * polled. + * + * The polling event can be used for sending data without having to + * wait for the remote host to send data. + * + * \hideinitializer + */ +int uip_poll(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_POLL; +} + +int uip_initialmss(struct uip_stack *ustack) +{ + return ustack->uip_conn->initialmss; +} + +int uip_mss(struct uip_stack *ustack) +{ + return ustack->uip_conn->mss; +} + +/*---------------------------------------------------------------------------*/ +/* XXX: IP fragment reassembly: not well-tested. */ + +#if UIP_REASSEMBLY && !UIP_CONF_IPV6 +#define UIP_REASS_BUFSIZE (UIP_BUFSIZE - UIP_LLH_LEN) +static u8_t uip_reassbuf[UIP_REASS_BUFSIZE]; +static u8_t uip_reassbitmap[UIP_REASS_BUFSIZE / (8 * 8)]; +static const u8_t bitmap_bits[8] = {0xff, 0x7f, 0x3f, 0x1f, + 0x0f, 0x07, 0x03, 0x01}; +static u16_t uip_reasslen; +static u8_t uip_reassflags; +#define UIP_REASS_FLAG_LASTFRAG 0x01 +static u8_t uip_reasstmr; + +#define IP_MF 0x20 + +static u8_t +uip_reass(void) +{ + u16_t offset, len; + u16_t i; + + /* If ip_reasstmr is zero, no packet is present in the buffer, so we + write the IP header of the fragment into the reassembly + buffer. The timer is updated with the maximum age. */ + if(uip_reasstmr == 0) { + memcpy(uip_reassbuf, &BUF(ustack)->vhl, uip_iph_len); + uip_reasstmr = UIP_REASS_MAXAGE; + uip_reassflags = 0; + /* Clear the bitmap. */ + memset(uip_reassbitmap, 0, sizeof(uip_reassbitmap)); + } + + /* Check if the incoming fragment matches the one currently present + in the reasembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if(BUF(ustack)->srcipaddr[0] == FBUF(ustack)->srcipaddr[0] && + BUF(ustack)->srcipaddr[1] == FBUF(ustack)->srcipaddr[1] && + BUF(ustack)->destipaddr[0] == FBUF(ustack)->destipaddr[0] && + BUF(ustack)->destipaddr[1] == FBUF(ustack)->destipaddr[1] && + BUF(ustack)->ipid[0] == FBUF(ustack)->ipid[0] && + BUF(ustack)->ipid[1] == FBUF(ustack)->ipid[1]) { + + len = (BUF(ustack)->len[0] << 8) + BUF(ustack)->len[1] - (BUF(ustack)->vhl & 0x0f) * 4; + offset = (((BUF(ustack)->ipoffset[0] & 0x3f) << 8) + BUF(ustack)->ipoffset[1]) * 8; + + /* If the offset or the offset + fragment length overflows the + reassembly buffer, we discard the entire packet. */ + if(offset > UIP_REASS_BUFSIZE || + offset + len > UIP_REASS_BUFSIZE) { + uip_reasstmr = 0; + goto nullreturn; + } + + /* Copy the fragment into the reassembly buffer, at the right + offset. */ + memcpy(&uip_reassbuf[uip_iph_len + offset], + (char *)BUF + (int)((BUF(ustack)->vhl & 0x0f) * 4), + len); + + /* Update the bitmap. */ + if(offset / (8 * 8) == (offset + len) / (8 * 8)) { + /* If the two endpoints are in the same byte, we only update + that byte. */ + + uip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8 ) & 7] & + ~bitmap_bits[((offset + len) / 8 ) & 7]; + } else { + /* If the two endpoints are in different bytes, we update the + bytes in the endpoints and fill the stuff inbetween with + 0xff. */ + uip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8 ) & 7]; + for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) { + uip_reassbitmap[i] = 0xff; + } + uip_reassbitmap[(offset + len) / (8 * 8)] |= + ~bitmap_bits[((offset + len) / 8 ) & 7]; + } + + /* If this fragment has the More Fragments flag set to zero, we + know that this is the last fragment, so we can calculate the + size of the entire packet. We also set the + IP_REASS_FLAG_LASTFRAG flag to indicate that we have received + the final fragment. */ + + if((BUF(ustack)->ipoffset[0] & IP_MF) == 0) { + uip_reassflags |= UIP_REASS_FLAG_LASTFRAG; + uip_reasslen = offset + len; + } + + /* Finally, we check if we have a full packet in the buffer. We do + this by checking if we have the last fragment and if all bits + in the bitmap are set. */ + if(uip_reassflags & UIP_REASS_FLAG_LASTFRAG) { + /* Check all bytes up to and including all but the last byte in + the bitmap. */ + for(i = 0; i < uip_reasslen / (8 * 8) - 1; ++i) { + if(uip_reassbitmap[i] != 0xff) { + goto nullreturn; + } + } + /* Check the last byte in the bitmap. It should contain just the + right amount of bits. */ + if(uip_reassbitmap[uip_reasslen / (8 * 8)] != + (u8_t)~bitmap_bits[uip_reasslen / 8 & 7]) { + goto nullreturn; + } + + /* If we have come this far, we have a full packet in the + buffer, so we allocate a pbuf and copy the packet into it. We + also reset the timer. */ + uip_reasstmr = 0; + memcpy(BUF, FBUF, uip_reasslen); + + /* Pretend to be a "normal" (i.e., not fragmented) IP packet + from now on. */ + BUF(ustack)->ipoffset[0] = BUF(ustack)->ipoffset[1] = 0; + BUF(ustack)->len[0] = uip_reasslen >> 8; + BUF(ustack)->len[1] = uip_reasslen & 0xff; + BUF(ustack)->ipchksum = 0; + BUF(ustack)->ipchksum = ~(uip_ipchksum()); + + return uip_reasslen; + } + } + + nullreturn: + return 0; +} +#endif /* UIP_REASSEMBLY */ +/*---------------------------------------------------------------------------*/ +static void +uip_add_rcv_nxt(struct uip_stack *ustack, u16_t n) +{ + u8_t uip_acc32[4]; + + uip_add32(ustack->uip_conn->rcv_nxt, n, uip_acc32); + ustack->uip_conn->rcv_nxt[0] = uip_acc32[0]; + ustack->uip_conn->rcv_nxt[1] = uip_acc32[1]; + ustack->uip_conn->rcv_nxt[2] = uip_acc32[2]; + ustack->uip_conn->rcv_nxt[3] = uip_acc32[3]; +} +/*---------------------------------------------------------------------------*/ + +/** @} */ + +/** + * \defgroup uipdevfunc uIP device driver functions + * @{ + * + * These functions are used by a network device driver for interacting + * with uIP. + */ + +/** + * Process an incoming packet. + * + * This function should be called when the device driver has received + * a packet from the network. The packet from the device driver must + * be present in the uip_buf buffer, and the length of the packet + * should be placed in the uip_len variable. + * + * When the function returns, there may be an outbound packet placed + * in the uip_buf packet buffer. If so, the uip_len variable is set to + * the length of the packet. If no packet is to be sent out, the + * uip_len variable is set to 0. + * + * The usual way of calling the function is presented by the source + * code below. + \code + uip_len = devicedriver_poll(); + if(uip_len > 0) { + uip_input(); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note If you are writing a uIP device driver that needs ARP + * (Address Resolution Protocol), e.g., when running uIP over + * Ethernet, you will need to call the uIP ARP code before calling + * this function: + \code + #define BUF ((struct uip_eth_hdr *)&uip_buf[0]) + uip_len = ethernet_devicedrver_poll(); + if(uip_len > 0) { + if(BUF(ustack)->type == HTONS(UIP_ETHTYPE_IP)) { + uip_arp_ipin(); + uip_input(); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } else if(BUF(ustack)->type == HTONS(UIP_ETHTYPE_ARP)) { + uip_arp_arpin(); + if(uip_len > 0) { + ethernet_devicedriver_send(); + } + } + \endcode + * + * \hideinitializer + */ +void uip_input(struct uip_stack *ustack) +{ + uip_process(ustack, UIP_DATA); +} + +/** + * Periodic processing for a connection identified by its number. + * + * This function does the necessary periodic processing (timers, + * polling) for a uIP TCP conneciton, and should be called when the + * periodic uIP timer goes off. It should be called for every + * connection, regardless of whether they are open of closed. + * + * When the function returns, it may have an outbound packet waiting + * for service in the uIP packet buffer, and if so the uip_len + * variable is set to a value larger than zero. The device driver + * should be called to send out the packet. + * + * The ususal way of calling the function is through a for() loop like + * this: + \code + for(i = 0; i < UIP_CONNS; ++i) { + uip_periodic(i); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note If you are writing a uIP device driver that needs ARP + * (Address Resolution Protocol), e.g., when running uIP over + * Ethernet, you will need to call the uip_arp_out() function before + * calling the device driver: + \code + for(i = 0; i < UIP_CONNS; ++i) { + uip_periodic(i); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } + \endcode + * + * \param conn The number of the connection which is to be periodically polled. + * + * \hideinitializer + */ +void uip_periodic(struct uip_stack *ustack, int conn) +{ + ustack->uip_conn = &ustack->uip_conns[conn]; + uip_process(ustack, UIP_TIMER); +} + +#if UIP_UDP +/** + * Periodic processing for a UDP connection identified by its number. + * + * This function is essentially the same as uip_periodic(), but for + * UDP connections. It is called in a similar fashion as the + * uip_periodic() function: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note As for the uip_periodic() function, special care has to be + * taken when using uIP together with ARP and Ethernet: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } + \endcode + * + * \param conn The number of the UDP connection to be processed. + * + * \hideinitializer + */ +void uip_udp_periodic(struct uip_stack *ustack, int conn) +{ + ustack->uip_udp_conn = &ustack->uip_udp_conns[conn]; + uip_process(ustack, UIP_UDP_TIMER); +} +#endif + + +void +uip_process(struct uip_stack *ustack, u8_t flag) +{ + u8_t c; + u16_t tmp16; + register struct uip_conn *uip_connr = ustack->uip_conn; + + u16_t uip_iph_len = 0; + u16_t uip_ip_udph_len = 0; + u16_t uip_ip_tcph_len = 0; + struct uip_tcp_ipv4_hdr *tcp_ipv4_hdr = NULL; + struct uip_tcp_hdr *tcp_hdr = NULL; + struct uip_icmpv4_hdr *icmpv4_hdr = NULL; + struct uip_icmpv6_hdr *icmpv6_hdr = NULL; + struct uip_udp_hdr *udp_hdr = NULL; + + if(is_ipv6(ustack)) + { + uint8_t *buf; + uip_iph_len = UIP_IPv6_H_LEN; + uip_ip_udph_len = UIP_IPv6_UDPH_LEN; + uip_ip_tcph_len = UIP_IPv6_TCPH_LEN; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv6_hdr); + tcp_hdr = (struct uip_tcp_hdr *) buf; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv6_hdr); + udp_hdr = (struct uip_udp_hdr *) buf; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv6_hdr); + icmpv6_hdr = (struct uip_icmpv6_hdr *) buf; + } + else + { + uint8_t *buf; + + uip_iph_len = UIP_IPv4_H_LEN; + uip_ip_udph_len = UIP_IPv4_UDPH_LEN; + uip_ip_tcph_len = UIP_IPv4_TCPH_LEN; + + tcp_ipv4_hdr = (struct uip_tcp_ipv4_hdr *) ustack->network_layer; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv4_hdr); + tcp_hdr = (struct uip_tcp_hdr *) buf; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv4_hdr); + icmpv4_hdr = (struct uip_icmpv4_hdr *) buf; + + buf = ustack->network_layer; + buf += sizeof(struct uip_ipv4_hdr); + udp_hdr = (struct uip_udp_hdr *) buf; + } + +#if UIP_UDP + if(flag == UIP_UDP_SEND_CONN) { + goto udp_send; + } +#endif /* UIP_UDP */ + + ustack->uip_sappdata = ustack->uip_appdata = &ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN]; + + /* Check if we were invoked because of a poll request for a + particular connection. */ + if(flag == UIP_POLL_REQUEST) { + if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED && + !uip_outstanding(uip_connr)) { + ustack->uip_flags = UIP_POLL; + UIP_APPCALL(ustack); + goto appsend; + } + goto drop; + + /* Check if we were invoked because of the perodic timer fireing. */ + } else if(flag == UIP_TIMER) { +#if UIP_REASSEMBLY + if(uip_reasstmr != 0) { + --uip_reasstmr; + } +#endif /* UIP_REASSEMBLY */ + /* Increase the initial sequence number. */ + if(++ustack->iss[3] == 0) { + if(++ustack->iss[2] == 0) { + if(++ustack->iss[1] == 0) { + ++ustack->iss[0]; + } + } + } + + /* Reset the length variables. */ + ustack->uip_len = 0; + ustack->uip_slen = 0; + + /* Check if the connection is in a state in which we simply wait + for the connection to time out. If so, we increase the + connection's timer and remove the connection if it times + out. */ + if(uip_connr->tcpstateflags == UIP_TIME_WAIT || + uip_connr->tcpstateflags == UIP_FIN_WAIT_2) { + ++(uip_connr->timer); + if(uip_connr->timer == UIP_TIME_WAIT_TIMEOUT) { + uip_connr->tcpstateflags = UIP_CLOSED; + } + } else if(uip_connr->tcpstateflags != UIP_CLOSED) { + /* If the connection has outstanding data, we increase the + connection's timer and see if it has reached the RTO value + in which case we retransmit. */ + if(uip_outstanding(uip_connr)) { + if(uip_connr->timer-- == 0) { + if(uip_connr->nrtx == UIP_MAXRTX || + ((uip_connr->tcpstateflags == UIP_SYN_SENT || + uip_connr->tcpstateflags == UIP_SYN_RCVD) && + uip_connr->nrtx == UIP_MAXSYNRTX)) { + uip_connr->tcpstateflags = UIP_CLOSED; + + /* We call UIP_APPCALL() with uip_flags set to + UIP_TIMEDOUT to inform the application that the + connection has timed out. */ + ustack->uip_flags = UIP_TIMEDOUT; + UIP_APPCALL(ustack); + + /* We also send a reset packet to the remote host. */ + tcp_hdr->flags = TCP_RST | TCP_ACK; + goto tcp_send_nodata; + } + + /* Exponential backoff. */ + uip_connr->timer = UIP_RTO << (uip_connr->nrtx > 4? + 4: + uip_connr->nrtx); + ++(uip_connr->nrtx); + + /* Ok, so we need to retransmit. We do this differently + depending on which state we are in. In ESTABLISHED, we + call upon the application so that it may prepare the + data for the retransmit. In SYN_RCVD, we resend the + SYNACK that we sent earlier and in LAST_ACK we have to + retransmit our FINACK. */ + ++ustack->stats.tcp.rexmit; + switch(uip_connr->tcpstateflags & UIP_TS_MASK) { + case UIP_SYN_RCVD: + /* In the SYN_RCVD state, we should retransmit our + SYNACK. */ + goto tcp_send_synack; + +#if UIP_ACTIVE_OPEN + case UIP_SYN_SENT: + /* In the SYN_SENT state, we retransmit out SYN. */ + tcp_hdr->flags = 0; + goto tcp_send_syn; +#endif /* UIP_ACTIVE_OPEN */ + + case UIP_ESTABLISHED: + /* In the ESTABLISHED state, we call upon the application + to do the actual retransmit after which we jump into + the code for sending out the packet (the apprexmit + label). */ + ustack->uip_flags = UIP_REXMIT; + UIP_APPCALL(ustack); + goto apprexmit; + + case UIP_FIN_WAIT_1: + case UIP_CLOSING: + case UIP_LAST_ACK: + /* In all these states we should retransmit a FINACK. */ + goto tcp_send_finack; + + } + } + } else if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) { + /* If there was no need for a retransmission, we poll the + application for new data. */ + ustack->uip_flags = UIP_POLL; + UIP_APPCALL(ustack); + goto appsend; + } + } + goto drop; + } +#if UIP_UDP + if(flag == UIP_UDP_TIMER) { + if(ustack->uip_udp_conn->lport != 0) { + ustack->uip_conn = NULL; + ustack->uip_sappdata = ustack->uip_appdata = ustack->uip_buf + UIP_LLH_LEN + uip_ip_udph_len; + ustack->uip_len = ustack->uip_slen = 0; + ustack->uip_flags = UIP_POLL; + UIP_UDP_APPCALL(ustack); + goto udp_send; + } else { + goto drop; + } + } +#endif + + /* This is where the input processing starts. */ + ++ustack->stats.ip.recv; + + /* Start of IP input header processing code. */ + + if(is_ipv6(ustack)) { + /* Check validity of the IP header. */ + if((IPv6_BUF(ustack)->vtc & 0xf0) != 0x60) { /* IP version and header length. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.vhlerr; + LOG_ERR(PFX "ipv6: invalid version."); + goto drop; + } + } else { + /* Check validity of the IP header. */ + if(tcp_ipv4_hdr->vhl != 0x45) { /* IP version and header length. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.vhlerr; + LOG_ERR(PFX "ipv4: invalid version or header length."); + goto drop; + } + } + + /* Check the size of the packet. If the size reported to us in + uip_len is smaller the size reported in the IP header, we assume + that the packet has been corrupted in transit. If the size of + uip_len is larger than the size reported in the IP packet header, + the packet has been padded and we set uip_len to the correct + value.. */ + + if(is_ipv6(ustack)) { + if((IPv6_BUF(ustack)->len[0] << 8) + + IPv6_BUF(ustack)->len[1] <= ustack->uip_len) { + ustack->uip_len = (IPv6_BUF(ustack)->len[0] << 8) + + IPv6_BUF(ustack)->len[1]; + ustack->uip_len += 40; /* The length reported in the IPv6 header is the + length of the payload that follows the + header. However, uIP uses the uip_len variable + for holding the size of the entire packet, + including the IP header. For IPv4 this is not a + problem as the length field in the IPv4 header + contains the length of the entire packet. But + for IPv6 we need to add the size of the IPv6 + header (40 bytes). */ + } else { + LOG_WARN(PFX "ip: packet shorter than reported in IP header:" + "IPv6_BUF(ustack)->len: %d ustack->uip_len:%d.", + (IPv6_BUF(ustack)->len[0] << 8) + IPv6_BUF(ustack)->len[1], + ustack->uip_len); + goto drop; + } + } else { + if((tcp_ipv4_hdr->len[0] << 8) + + tcp_ipv4_hdr->len[1] <= ustack->uip_len) { + ustack->uip_len = (tcp_ipv4_hdr->len[0] << 8) + + tcp_ipv4_hdr->len[1]; + } else { + LOG_WARN(PFX "ip: packet shorter than reported in IP header: " + "tcp_ipv4_hdr->len: %d ustack->uip_len:%d.", + (tcp_ipv4_hdr->len[0] << 8) + tcp_ipv4_hdr->len[1], + ustack->uip_len); + goto drop; + } + } + + if(!is_ipv6(ustack)) { + /* Check the fragment flag. */ + if((tcp_ipv4_hdr->ipoffset[0] & 0x3f) != 0 || + tcp_ipv4_hdr->ipoffset[1] != 0) { +#if UIP_REASSEMBLY + uip_len = uip_reass(); + if(uip_len == 0) { + goto drop; + } +#else /* UIP_REASSEMBLY */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.fragerr; + LOG_WARN(PFX "ip: fragment dropped."); + goto drop; +#endif /* UIP_REASSEMBLY */ + } + } + + if(is_ipv6(ustack)) { + if(uip_ip6addr_cmp(ustack->hostaddr6, all_zeroes_addr6)) { + } else { + /* If IP broadcast support is configured, we check for a broadcast + UDP packet, which may be destined to us. */ +#if UIP_BROADCAST + if(IPv6_BUF(ustack)->proto == UIP_PROTO_UDP && + uip_ip4addr_cmp(IPv6_BUF(ustack)->destipaddr, all_ones_addr) + /*&& + uip_ipchksum() == 0xffff*/) { + goto udp_input; + } +#endif + /* For IPv6, packet reception is a little trickier as we need to + make sure that we listen to certain multicast addresses (all + hosts multicast address, and the solicited-node multicast + address) as well. However, we will cheat here and accept all + multicast packets that are sent to the ff02::/16 addresses. */ + if(!uip_ip6addr_cmp(IPv6_BUF(ustack)->destipaddr, ustack->hostaddr6) && + IPv6_BUF(ustack)->destipaddr[0] != htons(0xff02)) { + ++ustack->stats.ip.drop; + goto drop; + } + } + } else { + if(uip_ip4addr_cmp(ustack->hostaddr, all_zeroes_addr4)) { + /* If we are configured to use ping IP address configuration and + hasn't been assigned an IP address yet, we accept all ICMP + packets. */ +#if UIP_PINGADDRCONF && !UIP_CONF_IPV6 + if(tcp_ipv4_hdr->proto == UIP_PROTO_ICMP) { + LOG_WARN(PFX "ip: possible ping config packet received."); + goto icmp_input; + } else { + LOG_WARN(PFX "ip: packet dropped since no address assigned."); + goto drop; + } +#endif /* UIP_PINGADDRCONF */ + + } else { + /* If IP broadcast support is configured, we check for a broadcast + UDP packet, which may be destined to us. */ +#if UIP_BROADCAST + if(tcp_ipv4_hdr->proto == UIP_PROTO_UDP && + uip_ip4addr_cmp(tcp_ipv4_hdr->destipaddr, all_ones_addr) + /*&& + uip_ipchksum() == 0xffff*/) { + goto udp_input; + } +#endif /* UIP_BROADCAST */ + + /* Check if the packet is destined for our IP address. */ + if(!uip_ip4addr_cmp(tcp_ipv4_hdr->destipaddr, + ustack->hostaddr)) { + ++ustack->stats.ip.drop; + goto drop; + } + } + + if(uip_ipchksum(ustack) != 0xffff) { /* Compute and check the IP header + checksum. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.chkerr; + LOG_ERR(PFX "ip: bad checksum."); + goto drop; + } + } + + if( is_ipv6(ustack)) { + if(IPv6_BUF(ustack)->proto == UIP_PROTO_TCP) { /* Check for TCP packet. If so, + proceed with TCP input + processing. */ + goto tcp_input; + } + +#if UIP_UDP + if(IPv6_BUF(ustack)->proto == UIP_PROTO_UDP) { + goto udp_input; + } +#endif /* UIP_UDP */ + + /* This is IPv6 ICMPv6 processing code. */ + LOG_DEBUG(PFX "icmp6_input: length %d\n", ustack->uip_len); + + if(IPv6_BUF(ustack)->proto != UIP_PROTO_ICMP6) { /* We only allow ICMPv6 packets from + here. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.protoerr; + LOG_ERR(PFX "ip: neither tcp nor icmp6."); + goto drop; + } + + ++ustack->stats.icmp.recv; + + /* If we get a neighbor solicitation for our address we should send + a neighbor advertisement message back. */ + if(icmpv6_hdr->type == ICMP6_NEIGHBOR_SOLICITATION) { + if(uip_ip6addr_cmp(icmpv6_hdr->icmp6data, + ustack->hostaddr6)) { + + if(icmpv6_hdr->options[0] == ICMP6_OPTION_SOURCE_LINK_ADDRESS) { + /* Save the sender's address in our neighbor list. */ + + uip_neighbor_add(ustack, + IPv6_BUF(ustack)->srcipaddr, + (struct uip_eth_addr *) &(icmpv6_hdr->options[2])); + } + + /* We should now send a neighbor advertisement back to where the + neighbor solicication came from. */ + icmpv6_hdr->type = ICMP6_NEIGHBOR_ADVERTISEMENT; + icmpv6_hdr->flags = ICMP6_FLAG_S; /* Solicited flag. */ + + icmpv6_hdr->reserved1 = icmpv6_hdr->reserved2 = icmpv6_hdr->reserved3 = 0; + + uip_ip6addr_copy(IPv6_BUF(ustack)->destipaddr, + IPv6_BUF(ustack)->srcipaddr); + uip_ip6addr_copy(IPv6_BUF(ustack)->srcipaddr, + ustack->hostaddr6); + icmpv6_hdr->options[0] = ICMP6_OPTION_TARGET_LINK_ADDRESS; + icmpv6_hdr->options[1] = 1; /* Options length, 1 = 8 bytes. */ + memcpy(&(icmpv6_hdr->options[2]), + &ustack->uip_ethaddr, sizeof(struct uip_eth_addr)); + icmpv6_hdr->icmpchksum = 0; + icmpv6_hdr->icmpchksum = ~uip_icmp6chksum(ustack); + goto send; + + } + goto drop; + } else if(icmpv6_hdr->type == ICMP6_ECHO) { + /* ICMP echo (i.e., ping) processing. This is simple, we only + change the ICMP type from ECHO to ECHO_REPLY and update the + ICMP checksum before we return the packet. */ + + icmpv6_hdr->type = ICMP6_ECHO_REPLY; + + uip_ip6addr_copy(IPv6_BUF(ustack)->destipaddr, + IPv6_BUF(ustack)->srcipaddr); + uip_ip6addr_copy(IPv6_BUF(ustack)->srcipaddr, + ustack->hostaddr6); + icmpv6_hdr->icmpchksum = 0; + icmpv6_hdr->icmpchksum = ~uip_icmp6chksum(ustack); + + ++ustack->stats.icmp.sent; + goto send; + } else { + LOG_DEBUG(PFX "Unknown icmp6 message type %d\n", + icmpv6_hdr->type); + ++ustack->stats.icmp.drop; + ++ustack->stats.icmp.typeerr; + LOG_ERR(PFX "icmpv6: unknown ICMP message."); + goto drop; + } + + /* End of IPv6 ICMP processing. */ + } else { + if(tcp_ipv4_hdr->proto == UIP_PROTO_TCP) { /* Check for TCP packet. If so, + proceed with TCP input + processing. */ + goto tcp_input; + } + +#if UIP_UDP + if(tcp_ipv4_hdr->proto == UIP_PROTO_UDP) { + goto udp_input; + } +#endif /* UIP_UDP */ + + /* ICMPv4 processing code follows. */ + if(tcp_ipv4_hdr->proto != UIP_PROTO_ICMP) { /* We only allow ICMP packets from + here. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.protoerr; + LOG_ERR(PFX "ip: neither tcp nor icmp."); + goto drop; + } + + #if UIP_PINGADDRCONF +icmp_input: + #endif /* UIP_PINGADDRCONF */ + ++ustack->stats.icmp.recv; + + /* ICMP echo (i.e., ping) processing. This is simple, we only change + the ICMP type from ECHO to ECHO_REPLY and adjust the ICMP + checksum before we return the packet. */ + if(icmpv4_hdr->type != ICMP_ECHO) { + ++ustack->stats.icmp.drop; + ++ustack->stats.icmp.typeerr; + LOG_ERR(PFX "icmp: not icmp echo."); + goto drop; + } + + /* If we are configured to use ping IP address assignment, we use + the destination IP address of this ping packet and assign it to + ourself. */ +#if UIP_PINGADDRCONF + if((ustack->hostaddr[0] | ustack->hostaddr[1]) == 0) { + ustack->hostaddr[0] = tcp_ipv4_hdr->destipaddr[0]; + ustack->hostaddr[1] = tcp_ipv4_hdr->destipaddr[1]; + } +#endif /* UIP_PINGADDRCONF */ + + icmpv4_hdr->type = ICMP_ECHO_REPLY; + + if(icmpv4_hdr->icmpchksum >= htons(0xffff - (ICMP_ECHO << 8))) { + icmpv4_hdr->icmpchksum += htons(ICMP_ECHO << 8) + 1; + } else { + icmpv4_hdr->icmpchksum += htons(ICMP_ECHO << 8); + } + + /* Swap IP addresses. */ + uip_ip4addr_copy(tcp_ipv4_hdr->destipaddr, + tcp_ipv4_hdr->srcipaddr); + uip_ip4addr_copy(tcp_ipv4_hdr->srcipaddr, ustack->hostaddr); + + ++ustack->stats.icmp.sent; + goto send; + + /* End of IPv4 input header processing code. */ + + } + +#if UIP_UDP + /* UDP input processing. */ + udp_input: + /* UDP processing is really just a hack. We don't do anything to the + UDP/IP headers, but let the UDP application do all the hard + work. If the application sets uip_slen, it has a packet to + send. */ +#if UIP_UDP_CHECKSUMS + ustack->uip_len = ustack->uip_len - uip_ip_udph_len; + ustack->uip_appdata = &ustack->uip_buf[UIP_LLH_LEN + uip_ip_udph_len]; + if(UDPBUF(ustack)->udpchksum != 0 && uip_udpchksum(ustack) != 0xffff) { + ++ustack->stats.udp.drop; + ++ustack->stats.udp.chkerr; + LOG_ERR(PFX "udp: bad checksum."); + goto drop; + } +#else /* UIP_UDP_CHECKSUMS */ + uip_len = uip_len - uip_ip_udph_len; +#endif /* UIP_UDP_CHECKSUMS */ + + /* Demultiplex this UDP packet between the UDP "connections". */ + for(ustack->uip_udp_conn = &ustack->uip_udp_conns[0]; + ustack->uip_udp_conn < &ustack->uip_udp_conns[UIP_UDP_CONNS]; + ++ustack->uip_udp_conn) { + /* If the local UDP port is non-zero, the connection is considered + to be used. If so, the local port number is checked against the + destination port number in the received packet. If the two port + numbers match, the remote port number is checked if the + connection is bound to a remote port. Finally, if the + connection is bound to a remote IP address, the source IP + address of the packet is checked. */ + + if(is_ipv6(ustack)) { + if(ustack->uip_udp_conn->lport != 0 && + udp_hdr->destport == ustack->uip_udp_conn->lport && + (ustack->uip_udp_conn->rport == 0 || + udp_hdr->srcport == ustack->uip_udp_conn->rport) && + (uip_ip6addr_cmp(ustack->uip_udp_conn->ripaddr, + all_zeroes_addr6) || + uip_ip6addr_cmp(ustack->uip_udp_conn->ripaddr, + all_ones_addr6) || + uip_ip6addr_cmp(IPv6_BUF(ustack)->srcipaddr, + ustack->uip_udp_conn->ripaddr))) { + goto udp_found; + } + } else { + if(ustack->uip_udp_conn->lport != 0 && + UDPBUF(ustack)->destport == ustack->uip_udp_conn->lport && + (ustack->uip_udp_conn->rport == 0 || + UDPBUF(ustack)->srcport == ustack->uip_udp_conn->rport) && + (uip_ip4addr_cmp(ustack->uip_udp_conn->ripaddr, + all_zeroes_addr4) || + uip_ip4addr_cmp(ustack->uip_udp_conn->ripaddr, + all_ones_addr4) || + uip_ip4addr_cmp(tcp_ipv4_hdr->srcipaddr, + ustack->uip_udp_conn->ripaddr))) { + goto udp_found; + } + } + } + LOG_ERR(PFX "udp: no matching connection found"); + goto drop; + + udp_found: + ustack->uip_conn = NULL; + ustack->uip_flags = UIP_NEWDATA; + ustack->uip_sappdata = ustack->uip_appdata = &ustack->uip_buf[UIP_LLH_LEN + uip_ip_udph_len]; + ustack->uip_slen = 0; + UIP_UDP_APPCALL(ustack); + udp_send: + if(ustack->uip_slen == 0) { + goto drop; + } + + ustack->uip_len = ustack->uip_slen + uip_ip_udph_len; + + if(is_ipv6(ustack)) { + /* For IPv6, the IP length field does not include the IPv6 IP header + length. */ + IPv6_BUF(ustack)->len[0] = ((ustack->uip_len - uip_iph_len) >> 8); + IPv6_BUF(ustack)->len[1] = ((ustack->uip_len - uip_iph_len) & 0xff); + IPv6_BUF(ustack)->ttl = ustack->uip_udp_conn->ttl; + IPv6_BUF(ustack)->proto = UIP_PROTO_UDP; + } else { + tcp_ipv4_hdr->len[0] = (ustack->uip_len >> 8); + tcp_ipv4_hdr->len[1] = (ustack->uip_len & 0xff); + tcp_ipv4_hdr->ttl = ustack->uip_udp_conn->ttl; + tcp_ipv4_hdr->proto = UIP_PROTO_UDP; + } + + udp_hdr->udplen = htons(ustack->uip_slen + UIP_UDPH_LEN); + udp_hdr->udpchksum = 0; + + udp_hdr->srcport = ustack->uip_udp_conn->lport; + udp_hdr->destport = ustack->uip_udp_conn->rport; + + if(is_ipv6(ustack)) { + uip_ip6addr_copy(IPv6_BUF(ustack)->srcipaddr, + ustack->hostaddr6); + uip_ip6addr_copy(IPv6_BUF(ustack)->destipaddr, + ustack->uip_udp_conn->ripaddr); + } else { + uip_ip4addr_copy(tcp_ipv4_hdr->srcipaddr, + ustack->hostaddr); + uip_ip4addr_copy(tcp_ipv4_hdr->destipaddr, + ustack->uip_udp_conn->ripaddr); + } + ustack->uip_appdata = &ustack->uip_buf[UIP_LLH_LEN + uip_ip_tcph_len]; + +#if UIP_UDP_CHECKSUMS + /* Calculate UDP checksum. */ + udp_hdr->udpchksum = ~(uip_udpchksum(ustack)); + if(udp_hdr->udpchksum == 0) { + udp_hdr->udpchksum = 0xffff; + } +#endif /* UIP_UDP_CHECKSUMS */ + + goto ip_send_nolen; +#endif /* UIP_UDP */ + + /* TCP input processing. */ + tcp_input: + ++ustack->stats.tcp.recv; + + /* Start of TCP input header processing code. */ + + if(uip_tcpchksum(ustack) != 0xffff) { /* Compute and check the TCP + checksum. */ + ++ustack->stats.tcp.drop; + ++ustack->stats.tcp.chkerr; + LOG_ERR(PFX "tcp: bad checksum."); + goto drop; + } + + if(is_ipv6(ustack)) { + /* Demultiplex this segment. */ + /* First check any active connections. */ + for(uip_connr = &ustack->uip_conns[0]; + uip_connr <= &ustack->uip_conns[UIP_CONNS - 1]; + ++uip_connr) { + if(uip_connr->tcpstateflags != UIP_CLOSED && + tcp_hdr->destport == uip_connr->lport && + tcp_hdr->srcport == uip_connr->rport && + uip_ip6addr_cmp(IPv6_BUF(ustack)->srcipaddr, + uip_connr->ripaddr)) { + goto found; + } + } + } else { + /* Demultiplex this segment. */ + /* First check any active connections. */ + for(uip_connr = &ustack->uip_conns[0]; + uip_connr <= &ustack->uip_conns[UIP_CONNS - 1]; + ++uip_connr) { + if(uip_connr->tcpstateflags != UIP_CLOSED && + tcp_hdr->destport == uip_connr->lport && + tcp_hdr->srcport == uip_connr->rport && + uip_ip4addr_cmp(tcp_ipv4_hdr->srcipaddr, + uip_connr->ripaddr)) { + goto found; + } + } + } + + /* If we didn't find and active connection that expected the packet, + either this packet is an old duplicate, or this is a SYN packet + destined for a connection in LISTEN. If the SYN flag isn't set, + it is an old packet and we send a RST. */ + if((tcp_hdr->flags & TCP_CTL) != TCP_SYN) { + goto reset; + } + + tmp16 = tcp_hdr->destport; + /* Next, check listening connections. */ + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(tmp16 == ustack->uip_listenports[c]) + goto found_listen; + } + + /* No matching connection found, so we send a RST packet. */ + ++ustack->stats.tcp.synrst; + reset: + + /* We do not send resets in response to resets. */ + if(tcp_hdr->flags & TCP_RST) { + goto drop; + } + + ++ustack->stats.tcp.rst; + + tcp_hdr->flags = TCP_RST | TCP_ACK; + ustack->uip_len = uip_ip_tcph_len; + tcp_hdr->tcpoffset = 5 << 4; + + /* Flip the seqno and ackno fields in the TCP header. */ + c = tcp_hdr->seqno[3]; + tcp_hdr->seqno[3] = tcp_hdr->ackno[3]; + tcp_hdr->ackno[3] = c; + + c = tcp_hdr->seqno[2]; + tcp_hdr->seqno[2] = tcp_hdr->ackno[2]; + tcp_hdr->ackno[2] = c; + + c = tcp_hdr->seqno[1]; + tcp_hdr->seqno[1] = tcp_hdr->ackno[1]; + tcp_hdr->ackno[1] = c; + + c = tcp_hdr->seqno[0]; + tcp_hdr->seqno[0] = tcp_hdr->ackno[0]; + tcp_hdr->ackno[0] = c; + + /* We also have to increase the sequence number we are + acknowledging. If the least significant byte overflowed, we need + to propagate the carry to the other bytes as well. */ + if(++tcp_hdr->ackno[3] == 0) { + if(++tcp_hdr->ackno[2] == 0) { + if(++tcp_hdr->ackno[1] == 0) { + ++tcp_hdr->ackno[0]; + } + } + } + + /* Swap port numbers. */ + tmp16 = tcp_hdr->srcport; + tcp_hdr->srcport = tcp_hdr->destport; + tcp_hdr->destport = tmp16; + + /* Swap IP addresses. */ + if(is_ipv6(ustack)) { + uip_ip6addr_copy(IPv6_BUF(ustack)->destipaddr, + IPv6_BUF(ustack)->srcipaddr); + uip_ip6addr_copy(IPv6_BUF(ustack)->srcipaddr, + ustack->hostaddr6); + } else { + uip_ip4addr_copy(tcp_ipv4_hdr->destipaddr, + tcp_ipv4_hdr->srcipaddr); + uip_ip4addr_copy(tcp_ipv4_hdr->srcipaddr, + ustack->hostaddr); + } + + /* And send out the RST packet! */ + goto tcp_send_noconn; + + /* This label will be jumped to if we matched the incoming packet + with a connection in LISTEN. In that case, we should create a new + connection and send a SYNACK in return. */ + found_listen: + /* First we check if there are any connections avaliable. Unused + connections are kept in the same table as used connections, but + unused ones have the tcpstate set to CLOSED. Also, connections in + TIME_WAIT are kept track of and we'll use the oldest one if no + CLOSED connections are found. Thanks to Eddie C. Dost for a very + nice algorithm for the TIME_WAIT search. */ + uip_connr = 0; + for(c = 0; c < UIP_CONNS; ++c) { + if(ustack->uip_conns[c].tcpstateflags == UIP_CLOSED) { + uip_connr = &ustack->uip_conns[c]; + break; + } + if(ustack->uip_conns[c].tcpstateflags == UIP_TIME_WAIT) { + if(uip_connr == 0 || + ustack->uip_conns[c].timer > uip_connr->timer) { + uip_connr = &ustack->uip_conns[c]; + } + } + } + + if(uip_connr == 0) { + /* All connections are used already, we drop packet and hope that + the remote end will retransmit the packet at a time when we + have more spare connections. */ + ++ustack->stats.tcp.syndrop; + LOG_WARN(PFX "tcp: found no unused connections."); + goto drop; + } + ustack->uip_conn = uip_connr; + + /* Fill in the necessary fields for the new connection. */ + uip_connr->rto = uip_connr->timer = UIP_RTO; + uip_connr->sa = 0; + uip_connr->sv = 4; + uip_connr->nrtx = 0; + uip_connr->lport = tcp_hdr->destport; + uip_connr->rport = tcp_hdr->srcport; + if(is_ipv6(ustack)) { + uip_ip6addr_copy(uip_connr->ripaddr, IPv6_BUF(ustack)->srcipaddr); + } else { + uip_ip4addr_copy(uip_connr->ripaddr, tcp_ipv4_hdr->srcipaddr); + } + uip_connr->tcpstateflags = UIP_SYN_RCVD; + + uip_connr->snd_nxt[0] = ustack->iss[0]; + uip_connr->snd_nxt[1] = ustack->iss[1]; + uip_connr->snd_nxt[2] = ustack->iss[2]; + uip_connr->snd_nxt[3] = ustack->iss[3]; + uip_connr->len = 1; + + /* rcv_nxt should be the seqno from the incoming packet + 1. */ + uip_connr->rcv_nxt[3] = tcp_hdr->seqno[3]; + uip_connr->rcv_nxt[2] = tcp_hdr->seqno[2]; + uip_connr->rcv_nxt[1] = tcp_hdr->seqno[1]; + uip_connr->rcv_nxt[0] = tcp_hdr->seqno[0]; + uip_add_rcv_nxt(ustack, 1); + + /* Parse the TCP MSS option, if present. */ + if((tcp_hdr->tcpoffset & 0xf0) > 0x50) { + for(c = 0; c < ((tcp_hdr->tcpoffset >> 4) - 5) << 2 ;) { + ustack->opt = ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + c]; + if(ustack->opt == TCP_OPT_END) { + /* End of options. */ + break; + } else if(ustack->opt == TCP_OPT_NOOP) { + ++c; + /* NOP option. */ + } else if(ustack->opt == TCP_OPT_MSS && + ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { + /* An MSS option with the right option length. */ + tmp16 = ((u16_t)ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 2 + c] << 8) | + (u16_t)ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 3 + c]; + uip_connr->initialmss = uip_connr->mss = + tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; + + /* And we are done processing options. */ + break; + } else { + /* All other options have a length field, so that we easily + can skip past them. */ + if(ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 1 + c] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + c += ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 1 + c]; + } + } + } + + /* Our response will be a SYNACK. */ +#if UIP_ACTIVE_OPEN + tcp_send_synack: + tcp_hdr->flags = TCP_ACK; + + tcp_send_syn: + tcp_hdr->flags |= TCP_SYN; +#else /* UIP_ACTIVE_OPEN */ + tcp_send_synack: + tcp_hdr->flags = TCP_SYN | TCP_ACK; +#endif /* UIP_ACTIVE_OPEN */ + + /* We send out the TCP Maximum Segment Size option with our + SYNACK. */ + tcp_hdr->optdata[0] = TCP_OPT_MSS; + tcp_hdr->optdata[1] = TCP_OPT_MSS_LEN; + tcp_hdr->optdata[2] = (UIP_TCP_MSS) / 256; + tcp_hdr->optdata[3] = (UIP_TCP_MSS) & 255; + ustack->uip_len = uip_ip_tcph_len + TCP_OPT_MSS_LEN; + tcp_hdr->tcpoffset = ((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4) << 4; + goto tcp_send; + + /* This label will be jumped to if we found an active connection. */ + found: + ustack->uip_conn = uip_connr; + ustack->uip_flags = 0; + /* We do a very naive form of TCP reset processing; we just accept + any RST and kill our connection. We should in fact check if the + sequence number of this reset is wihtin our advertised window + before we accept the reset. */ + if(tcp_hdr->flags & TCP_RST) { + uip_connr->tcpstateflags = UIP_CLOSED; + LOG_ERR(PFX "tcp: got reset, aborting connection."); + ustack->uip_flags = UIP_ABORT; + UIP_APPCALL(ustack); + goto drop; + } + /* Calculated the length of the data, if the application has sent + any data to us. */ + c = (tcp_hdr->tcpoffset >> 4) << 2; + /* uip_len will contain the length of the actual TCP data. This is + calculated by subtracing the length of the TCP header (in + c) and the length of the IP header (20 bytes). */ + ustack->uip_len = ustack->uip_len - c - uip_iph_len; + + /* First, check if the sequence number of the incoming packet is + what we're expecting next. If not, we send out an ACK with the + correct numbers in. */ + if(!(((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) && + ((tcp_hdr->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)))) { + if((ustack->uip_len > 0 || ((tcp_hdr->flags & (TCP_SYN | TCP_FIN)) != 0)) && + (tcp_hdr->seqno[0] != uip_connr->rcv_nxt[0] || + tcp_hdr->seqno[1] != uip_connr->rcv_nxt[1] || + tcp_hdr->seqno[2] != uip_connr->rcv_nxt[2] || + tcp_hdr->seqno[3] != uip_connr->rcv_nxt[3])) { + goto tcp_send_ack; + } + } + + { + u8_t uip_acc32[4]; + + /* Next, check if the incoming segment acknowledges any outstanding + data. If so, we update the sequence number, reset the length of + the outstanding data, calculate RTT estimations, and reset the + retransmission timer. */ + if((tcp_hdr->flags & TCP_ACK) && uip_outstanding(uip_connr)) { + uip_add32(uip_connr->snd_nxt, uip_connr->len, uip_acc32); + + if(tcp_hdr->ackno[0] == uip_acc32[0] && + tcp_hdr->ackno[1] == uip_acc32[1] && + tcp_hdr->ackno[2] == uip_acc32[2] && + tcp_hdr->ackno[3] == uip_acc32[3]) { + /* Update sequence number. */ + uip_connr->snd_nxt[0] = uip_acc32[0]; + uip_connr->snd_nxt[1] = uip_acc32[1]; + uip_connr->snd_nxt[2] = uip_acc32[2]; + uip_connr->snd_nxt[3] = uip_acc32[3]; + + + /* Do RTT estimation, unless we have done retransmissions. */ + if(uip_connr->nrtx == 0) { + signed char m; + m = uip_connr->rto - uip_connr->timer; + /* This is taken directly from VJs original code in his paper */ + m = m - (uip_connr->sa >> 3); + uip_connr->sa += m; + if(m < 0) { + m = -m; + } + m = m - (uip_connr->sv >> 2); + uip_connr->sv += m; + uip_connr->rto = (uip_connr->sa >> 3) + uip_connr->sv; + + } + /* Set the acknowledged flag. */ + ustack->uip_flags = UIP_ACKDATA; + /* Reset the retransmission timer. */ + uip_connr->timer = uip_connr->rto; + + /* Reset length of outstanding data. */ + uip_connr->len = 0; + } + + } + + } + + /* Do different things depending on in what state the connection is. */ + switch(uip_connr->tcpstateflags & UIP_TS_MASK) { + /* CLOSED and LISTEN are not handled here. CLOSE_WAIT is not + implemented, since we force the application to close when the + peer sends a FIN (hence the application goes directly from + ESTABLISHED to LAST_ACK). */ + case UIP_SYN_RCVD: + /* In SYN_RCVD we have sent out a SYNACK in response to a SYN, and + we are waiting for an ACK that acknowledges the data we sent + out the last time. Therefore, we want to have the UIP_ACKDATA + flag set. If so, we enter the ESTABLISHED state. */ + if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_ESTABLISHED; + ustack->uip_flags = UIP_CONNECTED; + uip_connr->len = 0; + if(ustack->uip_len > 0) { + ustack->uip_flags |= UIP_NEWDATA; + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + ustack->uip_slen = 0; + UIP_APPCALL(ustack); + goto appsend; + } + goto drop; +#if UIP_ACTIVE_OPEN + case UIP_SYN_SENT: + /* In SYN_SENT, we wait for a SYNACK that is sent in response to + our SYN. The rcv_nxt is set to sequence number in the SYNACK + plus one, and we send an ACK. We move into the ESTABLISHED + state. */ + if((ustack->uip_flags & UIP_ACKDATA) && + (tcp_hdr->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)) { + + /* Parse the TCP MSS option, if present. */ + if((tcp_hdr->tcpoffset & 0xf0) > 0x50) { + for(c = 0; c < ((tcp_hdr->tcpoffset >> 4) - 5) << 2 ;) { + ustack->opt = ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + c]; + if(ustack->opt == TCP_OPT_END) { + /* End of options. */ + break; + } else if(ustack->opt == TCP_OPT_NOOP) { + ++c; + /* NOP option. */ + } else if(ustack->opt == TCP_OPT_MSS && + ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { + /* An MSS option with the right option length. */ + tmp16 = (ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 2 + c] << 8) | + ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 3 + c]; + uip_connr->initialmss = + uip_connr->mss = tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; + + /* And we are done processing options. */ + break; + } else { + /* All other options have a length field, so that we easily + can skip past them. */ + if(ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 1 + c] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + c += ustack->uip_buf[uip_ip_tcph_len + UIP_LLH_LEN + 1 + c]; + } + } + } + uip_connr->tcpstateflags = UIP_ESTABLISHED; + uip_connr->rcv_nxt[0] = tcp_hdr->seqno[0]; + uip_connr->rcv_nxt[1] = tcp_hdr->seqno[1]; + uip_connr->rcv_nxt[2] = tcp_hdr->seqno[2]; + uip_connr->rcv_nxt[3] = tcp_hdr->seqno[3]; + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CONNECTED | UIP_NEWDATA; + uip_connr->len = 0; + ustack->uip_len = 0; + ustack->uip_slen = 0; + UIP_APPCALL(ustack); + goto appsend; + } + /* Inform the application that the connection failed */ + ustack->uip_flags = UIP_ABORT; + UIP_APPCALL(ustack); + /* The connection is closed after we send the RST */ + ustack->uip_conn->tcpstateflags = UIP_CLOSED; + goto reset; +#endif /* UIP_ACTIVE_OPEN */ + + case UIP_ESTABLISHED: + /* In the ESTABLISHED state, we call upon the application to feed + data into the uip_buf. If the UIP_ACKDATA flag is set, the + application should put new data into the buffer, otherwise we are + retransmitting an old segment, and the application should put that + data into the buffer. + + If the incoming packet is a FIN, we should close the connection on + this side as well, and we send out a FIN and enter the LAST_ACK + state. We require that there is no outstanding data; otherwise the + sequence numbers will be screwed up. */ + + if(tcp_hdr->flags & TCP_FIN && !(uip_connr->tcpstateflags & UIP_STOPPED)) { + if(uip_outstanding(uip_connr)) { + goto drop; + } + uip_add_rcv_nxt(ustack, 1 + ustack->uip_len); + ustack->uip_flags |= UIP_CLOSE; + if(ustack->uip_len > 0) { + ustack->uip_flags |= UIP_NEWDATA; + } + UIP_APPCALL(ustack); + uip_connr->len = 1; + uip_connr->tcpstateflags = UIP_LAST_ACK; + uip_connr->nrtx = 0; + tcp_send_finack: + tcp_hdr->flags = TCP_FIN | TCP_ACK; + goto tcp_send_nodata; + } + + /* Check the URG flag. If this is set, the segment carries urgent + data that we must pass to the application. */ + if((tcp_hdr->flags & TCP_URG) != 0) { +#if UIP_URGDATA > 0 + uip_urglen = (tcp_hdr->urgp[0] << 8) | tcp_hdr->urgp[1]; + if(uip_urglen > uip_len) { + /* There is more urgent data in the next segment to come. */ + uip_urglen = uip_len; + } + uip_add_rcv_nxt(uip_urglen); + uip_len -= uip_urglen; + uip_urgdata = uip_appdata; + uip_appdata += uip_urglen; + } else { + uip_urglen = 0; +#else /* UIP_URGDATA > 0 */ + ustack->uip_appdata = ((char *)ustack->uip_appdata) + ((tcp_hdr->urgp[0] << 8) | tcp_hdr->urgp[1]); + ustack->uip_len -= (tcp_hdr->urgp[0] << 8) | tcp_hdr->urgp[1]; +#endif /* UIP_URGDATA > 0 */ + } + + /* If uip_len > 0 we have TCP data in the packet, and we flag this + by setting the UIP_NEWDATA flag and update the sequence number + we acknowledge. If the application has stopped the dataflow + using uip_stop(), we must not accept any data packets from the + remote host. */ + if(ustack->uip_len > 0 && !(uip_connr->tcpstateflags & UIP_STOPPED)) { + ustack->uip_flags |= UIP_NEWDATA; + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + + /* Check if the available buffer space advertised by the other end + is smaller than the initial MSS for this connection. If so, we + set the current MSS to the window size to ensure that the + application does not send more data than the other end can + handle. + + If the remote host advertises a zero window, we set the MSS to + the initial MSS so that the application will send an entire MSS + of data. This data will not be acknowledged by the receiver, + and the application will retransmit it. This is called the + "persistent timer" and uses the retransmission mechanim. + */ + tmp16 = ((u16_t)tcp_hdr->wnd[0] << 8) + (u16_t)tcp_hdr->wnd[1]; + if(tmp16 > uip_connr->initialmss || + tmp16 == 0) { + tmp16 = uip_connr->initialmss; + } + uip_connr->mss = tmp16; + + /* If this packet constitutes an ACK for outstanding data (flagged + by the UIP_ACKDATA flag, we should call the application since it + might want to send more data. If the incoming packet had data + from the peer (as flagged by the UIP_NEWDATA flag), the + application must also be notified. + + When the application is called, the global variable uip_len + contains the length of the incoming data. The application can + access the incoming data through the global pointer + uip_appdata, which usually points uip_ip_tcph_len + UIP_LLH_LEN + bytes into the uip_buf array. + + If the application wishes to send any data, this data should be + put into the uip_appdata and the length of the data should be + put into uip_len. If the application don't have any data to + send, uip_len must be set to 0. */ + if(ustack->uip_flags & (UIP_NEWDATA | UIP_ACKDATA)) { + ustack->uip_slen = 0; + UIP_APPCALL(ustack); + + appsend: + + if(ustack->uip_flags & UIP_ABORT) { + ustack->uip_slen = 0; + uip_connr->tcpstateflags = UIP_CLOSED; + tcp_hdr->flags = TCP_RST | TCP_ACK; + goto tcp_send_nodata; + } + + if(ustack->uip_flags & UIP_CLOSE) { + ustack->uip_slen = 0; + uip_connr->len = 1; + uip_connr->tcpstateflags = UIP_FIN_WAIT_1; + uip_connr->nrtx = 0; + tcp_hdr->flags = TCP_FIN | TCP_ACK; + goto tcp_send_nodata; + } + + /* If uip_slen > 0, the application has data to be sent. */ + if(ustack->uip_slen > 0) { + + /* If the connection has acknowledged data, the contents of + the ->len variable should be discarded. */ + if((ustack->uip_flags & UIP_ACKDATA) != 0) { + uip_connr->len = 0; + } + + /* If the ->len variable is non-zero the connection has + already data in transit and cannot send anymore right + now. */ + if(uip_connr->len == 0) { + + /* The application cannot send more than what is allowed by + the mss (the minumum of the MSS and the available + window). */ + if(ustack->uip_slen > uip_connr->mss) { + ustack->uip_slen = uip_connr->mss; + } + + /* Remember how much data we send out now so that we know + when everything has been acknowledged. */ + uip_connr->len = ustack->uip_slen; + } else { + + /* If the application already had unacknowledged data, we + make sure that the application does not send (i.e., + retransmit) out more than it previously sent out. */ + ustack->uip_slen = uip_connr->len; + } + } + uip_connr->nrtx = 0; + apprexmit: + ustack->uip_appdata = ustack->uip_sappdata; + + /* If the application has data to be sent, or if the incoming + packet had new data in it, we must send out a packet. */ + if(ustack->uip_slen > 0 && uip_connr->len > 0) { + /* Add the length of the IP and TCP headers. */ + ustack->uip_len = uip_connr->len + uip_ip_tcph_len; + /* We always set the ACK flag in response packets. */ + tcp_hdr->flags = TCP_ACK | TCP_PSH; + /* Send the packet. */ + goto tcp_send_noopts; + } + /* If there is no data to send, just send out a pure ACK if + there is newdata. */ + if(ustack->uip_flags & UIP_NEWDATA) { + ustack->uip_len = uip_ip_tcph_len; + tcp_hdr->flags = TCP_ACK; + goto tcp_send_noopts; + } + } + goto drop; + case UIP_LAST_ACK: + /* We can close this connection if the peer has acknowledged our + FIN. This is indicated by the UIP_ACKDATA flag. */ + if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_CLOSED; + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(ustack); + } + break; + + case UIP_FIN_WAIT_1: + /* The application has closed the connection, but the remote host + hasn't closed its end yet. Thus we do nothing but wait for a + FIN from the other side. */ + if(ustack->uip_len > 0) { + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + if(tcp_hdr->flags & TCP_FIN) { + if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + uip_connr->len = 0; + } else { + uip_connr->tcpstateflags = UIP_CLOSING; + } + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(ustack); + goto tcp_send_ack; + } else if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_FIN_WAIT_2; + uip_connr->len = 0; + goto drop; + } + if(ustack->uip_len > 0) { + goto tcp_send_ack; + } + goto drop; + + case UIP_FIN_WAIT_2: + if(ustack->uip_len > 0) { + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + if(tcp_hdr->flags & TCP_FIN) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(ustack); + goto tcp_send_ack; + } + if(ustack->uip_len > 0) { + goto tcp_send_ack; + } + goto drop; + + case UIP_TIME_WAIT: + goto tcp_send_ack; + + case UIP_CLOSING: + if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + } + } + goto drop; + + + /* We jump here when we are ready to send the packet, and just want + to set the appropriate TCP sequence numbers in the TCP header. */ + tcp_send_ack: + tcp_hdr->flags = TCP_ACK; + tcp_send_nodata: + ustack->uip_len = uip_ip_tcph_len; + tcp_send_noopts: + tcp_hdr->tcpoffset = (UIP_TCPH_LEN / 4) << 4; + tcp_send: + /* We're done with the input processing. We are now ready to send a + reply. Our job is to fill in all the fields of the TCP and IP + headers before calculating the checksum and finally send the + packet. */ + tcp_hdr->ackno[0] = uip_connr->rcv_nxt[0]; + tcp_hdr->ackno[1] = uip_connr->rcv_nxt[1]; + tcp_hdr->ackno[2] = uip_connr->rcv_nxt[2]; + tcp_hdr->ackno[3] = uip_connr->rcv_nxt[3]; + + tcp_hdr->seqno[0] = uip_connr->snd_nxt[0]; + tcp_hdr->seqno[1] = uip_connr->snd_nxt[1]; + tcp_hdr->seqno[2] = uip_connr->snd_nxt[2]; + tcp_hdr->seqno[3] = uip_connr->snd_nxt[3]; + + if(is_ipv6(ustack)) { + IPv6_BUF(ustack)->proto = UIP_PROTO_TCP; + uip_ip6addr_copy(IPv6_BUF(ustack)->srcipaddr, ustack->hostaddr6); + uip_ip6addr_copy(IPv6_BUF(ustack)->destipaddr, uip_connr->ripaddr); + } else { + tcp_ipv4_hdr->proto = UIP_PROTO_TCP; + uip_ip4addr_copy(tcp_ipv4_hdr->srcipaddr, ustack->hostaddr); + uip_ip4addr_copy(tcp_ipv4_hdr->destipaddr, uip_connr->ripaddr); + } + + tcp_hdr->srcport = uip_connr->lport; + tcp_hdr->destport = uip_connr->rport; + + if(uip_connr->tcpstateflags & UIP_STOPPED) { + /* If the connection has issued uip_stop(), we advertise a zero + window so that the remote host will stop sending data. */ + tcp_hdr->wnd[0] = tcp_hdr->wnd[1] = 0; + } else { + tcp_hdr->wnd[0] = ((UIP_RECEIVE_WINDOW) >> 8); + tcp_hdr->wnd[1] = ((UIP_RECEIVE_WINDOW) & 0xff); + } + + tcp_send_noconn: + if(is_ipv6(ustack)) { + IPv6_BUF(ustack)->ttl = UIP_TTL; + + /* For IPv6, the IP length field does not include the IPv6 IP header + length. */ + IPv6_BUF(ustack)->len[0] = ((ustack->uip_len - uip_iph_len) >> 8); + IPv6_BUF(ustack)->len[1] = ((ustack->uip_len - uip_iph_len) & 0xff); + } else { + tcp_ipv4_hdr->ttl = UIP_TTL; + tcp_ipv4_hdr->len[0] = (ustack->uip_len >> 8); + tcp_ipv4_hdr->len[1] = (ustack->uip_len & 0xff); + } + + tcp_hdr->urgp[0] = tcp_hdr->urgp[1] = 0; + + /* Calculate TCP checksum. */ + tcp_hdr->tcpchksum = 0; + tcp_hdr->tcpchksum = ~(uip_tcpchksum(ustack)); + + ip_send_nolen: + + if(is_ipv6(ustack)) { + IPv6_BUF(ustack)->vtc = 0x60; + IPv6_BUF(ustack)->tcflow = 0x00; + IPv6_BUF(ustack)->flow = 0x00; + } else { + tcp_ipv4_hdr->vhl = 0x45; + tcp_ipv4_hdr->tos = 0; + tcp_ipv4_hdr->ipoffset[0] = tcp_ipv4_hdr->ipoffset[1] = 0; + ++ustack->ipid; + tcp_ipv4_hdr->ipid[0] = ustack->ipid >> 8; + tcp_ipv4_hdr->ipid[1] = ustack->ipid & 0xff; + /* Calculate IP checksum. */ + tcp_ipv4_hdr->ipchksum = 0; + tcp_ipv4_hdr->ipchksum = ~(uip_ipchksum(ustack)); + } + + ++ustack->stats.tcp.sent; + send: + if(is_ipv6(ustack)) { + LOG_DEBUG(PFX "Sending packet with length %d (%d)", ustack->uip_len, + (IPv6_BUF(ustack)->len[0] << 8) | IPv6_BUF(ustack)->len[1]); + } else { + LOG_DEBUG(PFX "Sending packet with length %d (%d)", ustack->uip_len, + (tcp_ipv4_hdr->len[0] << 8) | tcp_ipv4_hdr->len[1]); + } + + ++ustack->stats.ip.sent; + /* Return and let the caller do the actual transmission. */ + ustack->uip_flags = 0; + return; + drop: + ustack->uip_len = 0; + ustack->uip_flags = 0; + return; +} +#if 0 +/*---------------------------------------------------------------------------*/ +u16_t +htons(u16_t val) +{ + return HTONS(val); +} +#endif +/*---------------------------------------------------------------------------*/ +void +uip_send(struct uip_stack *ustack, const void *data, int len) +{ + if(len > 0) { + ustack->uip_slen = len; + if(data != ustack->uip_sappdata) { + memcpy(ustack->uip_sappdata, (data), ustack->uip_slen); + } + } +} + +u16_t +uip_datalen(struct uip_stack *ustack) +{ + return ustack->uip_len; +} +/** @} */ + +#define IPV6_LEN 16 + +int set_ipv6_link_local_address(struct uip_stack *ustack) +{ + u8_t *link_local_addr = (u8_t *) ustack->link_local_addr; + memset(ustack->link_local_addr, 0, IPV6_LEN); + + link_local_addr[0] = 0xfe; + link_local_addr[1] = 0x80; + + link_local_addr[IPV6_LEN - 8] = ustack->uip_ethaddr.addr[0]; + link_local_addr[IPV6_LEN - 7] = ustack->uip_ethaddr.addr[1]; + link_local_addr[IPV6_LEN - 6] = ustack->uip_ethaddr.addr[2]; + link_local_addr[IPV6_LEN - 5] = 0xff; + link_local_addr[IPV6_LEN - 4] = 0xfe; + link_local_addr[IPV6_LEN - 3] = ustack->uip_ethaddr.addr[3]; + link_local_addr[IPV6_LEN - 2] = ustack->uip_ethaddr.addr[4]; + link_local_addr[IPV6_LEN - 1] = ustack->uip_ethaddr.addr[5]; + + return 0; +} diff --git a/brcm_iscsi_uio/src/uip/uip.h b/brcm_iscsi_uio/src/uip/uip.h new file mode 100644 index 0000000..3db1a08 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip.h @@ -0,0 +1,1643 @@ + +/** + * \addtogroup uip + * @{ + */ + +/** + * \file + * Header file for the uIP TCP/IP stack. + * \author Adam Dunkels + * + * The uIP TCP/IP stack header file contains definitions for a number + * of C macros that are used by uIP programs as well as internal uIP + * structures, TCP/IP header structures and function declarations. + * + */ + + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uip.h,v 1.40 2006/06/08 07:12:07 adam Exp $ + * + */ + +#ifndef __UIP_H__ +#define __UIP_H__ + +#include + +#include "uipopt.h" + +#include "debug.h" + +#include "uip_eth.h" + +/* Forware declaration */ +struct uip_stack; + +/** + * Repressentation of an IP address. + * + */ +typedef u16_t uip_ip4addr_t[2]; +typedef u16_t uip_ip6addr_t[8]; + +const uip_ip6addr_t all_zeroes_addr6; +const uip_ip4addr_t all_zeroes_addr4; + + +#define ETH_BUF(buf) ((struct uip_eth_hdr *)buf) +#define VLAN_ETH_BUF(buf) ((struct uip_vlan_eth_hdr *)buf) +#define IPv4_BUF(buf) ((struct uip_tcp_ipv4_hdr *)buf) +#define IPv6_BUF(buf) ((struct uip_tcp_ipv6_hdr *)buf) + +/*---------------------------------------------------------------------------*/ +/* First, the functions that should be called from the + * system. Initialization, the periodic timer and incoming packets are + * handled by the following three functions. + */ + +/** + * \defgroup uipconffunc uIP configuration functions + * @{ + * + * The uIP configuration functions are used for setting run-time + * parameters in uIP such as IP addresses. + */ + +int is_ipv6(struct uip_stack *ustack); + +/** + * Set the IP address of this host. + * + * The IP address is represented as a 4-byte array where the first + * octet of the IP address is put in the first member of the 4-byte + * array. + * + * Example: + \code + + uip_ipaddr_t addr; + + uip_ipaddr(&addr, 192,168,1,2); + uip_sethostaddr(&addr); + + \endcode + * \param addr A pointer to an IP address of type uip_ipaddr_t; + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_sethostaddr4(struct uip_stack *ustack, uip_ip4addr_t *addr); + +/** + * Set the default router's IP address. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the default router. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setdraddr4(struct uip_stack *ustack, uip_ip4addr_t *addr); + +/** + * Set the netmask. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the netmask. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setnetmask4(struct uip_stack *ustack, uip_ip4addr_t *addr); + +/** + * Set the ethernet MAC address. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the netmask. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setethernetmac(struct uip_stack *ustack, uint8_t *mac); + +/** + * Get the default router's IP address. + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the IP address of the default router. + * + * \hideinitializer + */ +#define uip_getdraddr(addr) uip_ipaddr_copy((addr), uip_draddr) + +/** + * Get the netmask. + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the value of the netmask. + * + * \hideinitializer + */ +#define uip_getnetmask(addr) uip_ipaddr_copy((addr), uip_netmask) + + +void set_uip_stack(struct uip_stack *ustack, + uip_ip4addr_t *ip, + uip_ip4addr_t *netmask, + uip_ip4addr_t *default_route, + uint8_t *mac_addr); + + +/** @} */ + +/** + * \defgroup uipinit uIP initialization functions + * @{ + * + * The uIP initialization functions are used for booting uIP. + */ + +/** + * uIP initialization function. + * + * This function should be called at boot up to initilize the uIP + * TCP/IP stack. + */ +void uip_init(struct uip_stack *ustack, uint8_t enable_ipv6); + + +/** + * uIP reset function. + * + * This function should be called at to reset the uIP TCP/IP stack. + */ +void uip_reset(struct uip_stack *ustack); + +/** + * uIP initialization function. + * + * This function may be used at boot time to set the initial ip_id. + */ +void uip_setipid(u16_t id); + +/** + * + * + */ +#define uip_conn_active(conn) (uip_conns[conn].tcpstateflags != UIP_CLOSED) + +#if UIP_UDP + +#if 0 +/** + * Periodic processing for a UDP connection identified by its number. + * + * This function is essentially the same as uip_periodic(), but for + * UDP connections. It is called in a similar fashion as the + * uip_periodic() function: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note As for the uip_periodic() function, special care has to be + * taken when using uIP together with ARP and Ethernet: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } + \endcode + * + * \param conn The number of the UDP connection to be processed. + * + * \hideinitializer + */ +#define uip_udp_periodic(conn) do { uip_udp_conn = &uip_udp_conns[conn]; \ + uip_process(UIP_UDP_TIMER); } while (0) + +/** + * Periodic processing for a UDP connection identified by a pointer to + * its structure. + * + * Same as uip_udp_periodic() but takes a pointer to the actual + * uip_conn struct instead of an integer as its argument. This + * function can be used to force periodic processing of a specific + * connection. + * + * \param conn A pointer to the uip_udp_conn struct for the connection + * to be processed. + * + * \hideinitializer + */ +#define uip_udp_periodic_conn(conn) do { uip_udp_conn = conn; \ + uip_process(UIP_UDP_TIMER); } while (0) + +#endif + +void uip_udp_periodic(struct uip_stack *ustack, int conn); +#endif /* UIP_UDP */ + +/** + * The uIP packet buffer. + * + * The uip_buf array is used to hold incoming and outgoing + * packets. The device driver should place incoming data into this + * buffer. When sending data, the device driver should read the link + * level headers and the TCP/IP headers from this buffer. The size of + * the link level headers is configured by the UIP_LLH_LEN define. + * + * \note The application data need not be placed in this buffer, so + * the device driver must read it from the place pointed to by the + * uip_appdata pointer as illustrated by the following example: + \code + void + devicedriver_send(void) + { + hwsend(&uip_buf[0], UIP_LLH_LEN); + if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) { + hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN); + } else { + hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN); + hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN); + } + } + \endcode + */ +//extern u8_t uip_buf[UIP_BUFSIZE+2]; + +/** @} */ + +/*---------------------------------------------------------------------------*/ +/* Functions that are used by the uIP application program. Opening and + * closing connections, sending and receiving data, etc. is all + * handled by the functions below. +*/ +/** + * \defgroup uipappfunc uIP application functions + * @{ + * + * Functions used by an application running of top of uIP. + */ + +/** + * Start listening to the specified port. + * + * \note Since this function expects the port number in network byte + * order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_listen(HTONS(80)); + \endcode + * + * \param port A 16-bit port number in network byte order. + */ +void uip_listen(struct uip_stack *ustack, u16_t port); + +/** + * Stop listening to the specified port. + * + * \note Since this function expects the port number in network byte + * order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_unlisten(HTONS(80)); + \endcode + * + * \param port A 16-bit port number in network byte order. + */ +void uip_unlisten(struct uip_stack *ustack, u16_t port); + +/** + * Connect to a remote host using TCP. + * + * This function is used to start a new connection to the specified + * port on the specied host. It allocates a new connection identifier, + * sets the connection to the SYN_SENT state and sets the + * retransmission timer to 0. This will cause a TCP SYN segment to be + * sent out the next time this connection is periodically processed, + * which usually is done within 0.5 seconds after the call to + * uip_connect(). + * + * \note This function is avaliable only if support for active open + * has been configured by defining UIP_ACTIVE_OPEN to 1 in uipopt.h. + * + * \note Since this function requires the port number to be in network + * byte order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_ipaddr_t ipaddr; + + uip_ipaddr(&ipaddr, 192,168,1,2); + uip_connect(&ipaddr, HTONS(80)); + \endcode + * + * \param ripaddr The IP address of the remote hot. + * + * \param port A 16-bit port number in network byte order. + * + * \return A pointer to the uIP connection identifier for the new connection, + * or NULL if no connection could be allocated. + * + */ +struct uip_conn *uip_connect(struct uip_stack *ustack, + uip_ip4addr_t *ripaddr, u16_t port); + + + +/** + * \internal + * + * Check if a connection has outstanding (i.e., unacknowledged) data. + * + * \param conn A pointer to the uip_conn structure for the connection. + * + * \hideinitializer + */ +#define uip_outstanding(conn) ((conn)->len) + +/** + * Send data on the current connection. + * + * This function is used to send out a single segment of TCP + * data. Only applications that have been invoked by uIP for event + * processing can send data. + * + * The amount of data that actually is sent out after a call to this + * funcion is determined by the maximum amount of data TCP allows. uIP + * will automatically crop the data so that only the appropriate + * amount of data is sent. The function uip_mss() can be used to query + * uIP for the amount of data that actually will be sent. + * + * \note This function does not guarantee that the sent data will + * arrive at the destination. If the data is lost in the network, the + * application will be invoked with the uip_rexmit() event being + * set. The application will then have to resend the data using this + * function. + * + * \param data A pointer to the data which is to be sent. + * + * \param len The maximum amount of data bytes to be sent. + * + * \hideinitializer + */ +void uip_send(struct uip_stack *ustack, const void *data, int len); + +/** + * The length of any incoming data that is currently avaliable (if avaliable) + * in the uip_appdata buffer. + * + * The test function uip_data() must first be used to check if there + * is any data available at all. + * + * \hideinitializer + */ +/*void uip_datalen(void);*/ +u16_t uip_datalen(struct uip_stack *ustack); + +/** + * The length of any out-of-band data (urgent data) that has arrived + * on the connection. + * + * \note The configuration parameter UIP_URGDATA must be set for this + * function to be enabled. + * + * \hideinitializer + */ +#define uip_urgdatalen() uip_urglen + +/** + * Close the current connection. + * + * This function will close the current connection in a nice way. + * + * \hideinitializer + */ +#define uip_close() (uip_flags = UIP_CLOSE) + +/** + * Abort the current connection. + * + * This function will abort (reset) the current connection, and is + * usually used when an error has occured that prevents using the + * uip_close() function. + * + * \hideinitializer + */ +#define uip_abort() (uip_flags = UIP_ABORT) + +/** + * Tell the sending host to stop sending data. + * + * This function will close our receiver's window so that we stop + * receiving data for the current connection. + * + * \hideinitializer + */ +#define uip_stop() (uip_conn->tcpstateflags |= UIP_STOPPED) + +/** + * Find out if the current connection has been previously stopped with + * uip_stop(). + * + * \hideinitializer + */ +#define uip_stopped(conn) ((conn)->tcpstateflags & UIP_STOPPED) + +/** + * Restart the current connection, if is has previously been stopped + * with uip_stop(). + * + * This function will open the receiver's window again so that we + * start receiving data for the current connection. + * + * \hideinitializer + */ +#define uip_restart() do { uip_flags |= UIP_NEWDATA; \ + uip_conn->tcpstateflags &= ~UIP_STOPPED; \ + } while(0) + + +/* uIP tests that can be made to determine in what state the current + connection is, and what the application function should do. */ + +/** + * Is the current connection a UDP connection? + * + * This function checks whether the current connection is a UDP connection. + * + * \hideinitializer + * + */ +#define uip_udpconnection() (uip_conn == NULL) + +/** + * Function declarations for hte uip_flags + */ +/** + * Is new incoming data available? + * + * Will reduce to non-zero if there is new data for the application + * present at the uip_appdata pointer. The size of the data is + * avaliable through the uip_len variable. + * + * \hideinitializer + */ +int uip_newdata(struct uip_stack *ustack); + +/** + * Has previously sent data been acknowledged? + * + * Will reduce to non-zero if the previously sent data has been + * acknowledged by the remote host. This means that the application + * can send new data. + * + * \hideinitializer + */ +int uip_acked(struct uip_stack *ustack); + +/** + * Has the connection just been connected? + * + * Reduces to non-zero if the current connection has been connected to + * a remote host. This will happen both if the connection has been + * actively opened (with uip_connect()) or passively opened (with + * uip_listen()). + * + * \hideinitializer + */ +int uip_connected(struct uip_stack *ustack); + +/** + * Has the connection been closed by the other end? + * + * Is non-zero if the connection has been closed by the remote + * host. The application may then do the necessary clean-ups. + * + * \hideinitializer + */ +int uip_closed(struct uip_stack *ustack); + +/** + * Has the connection been aborted by the other end? + * + * Non-zero if the current connection has been aborted (reset) by the + * remote host. + * + * \hideinitializer + */ +int uip_aborted(struct uip_stack *ustack); + +/** + * Has the connection timed out? + * + * Non-zero if the current connection has been aborted due to too many + * retransmissions. + * + * \hideinitializer + */ +int uip_timedout(struct uip_stack *ustack); + +/** + * Do we need to retransmit previously data? + * + * Reduces to non-zero if the previously sent data has been lost in + * the network, and the application should retransmit it. The + * application should send the exact same data as it did the last + * time, using the uip_send() function. + * + * \hideinitializer + */ +int uip_rexmit(struct uip_stack *ustack); + +/** + * Is the connection being polled by uIP? + * + * Is non-zero if the reason the application is invoked is that the + * current connection has been idle for a while and should be + * polled. + * + * The polling event can be used for sending data without having to + * wait for the remote host to send data. + * + * \hideinitializer + */ +int uip_poll(struct uip_stack *ustack); + +/** + * Get the initial maxium segment size (MSS) of the current + * connection. + * + * \hideinitializer + */ +int uip_initialmss(struct uip_stack *ustack); + +/** + * Get the current maxium segment size that can be sent on the current + * connection. + * + * The current maxiumum segment size that can be sent on the + * connection is computed from the receiver's window and the MSS of + * the connection (which also is available by calling + * uip_initialmss()). + * + * \hideinitializer + */ +int uip_mss(struct uip_stack *ustack); + +/** + * Set up a new UDP connection. + * + * This function sets up a new UDP connection. The function will + * automatically allocate an unused local port for the new + * connection. However, another port can be chosen by using the + * uip_udp_bind() call, after the uip_udp_new() function has been + * called. + * + * Example: + \code + uip_ipaddr_t addr; + struct uip_udp_conn *c; + + uip_ipaddr(&addr, 192,168,2,1); + c = uip_udp_new(&addr, HTONS(12345)); + if(c != NULL) { + uip_udp_bind(c, HTONS(12344)); + } + \endcode + * \param ripaddr The IP address of the remote host. + * + * \param rport The remote port number in network byte order. + * + * \return The uip_udp_conn structure for the new connection or NULL + * if no connection could be allocated. + */ +struct uip_udp_conn *uip_udp_new(struct uip_stack *ustack, + uip_ip4addr_t *ripaddr, u16_t rport); + +/** + * Removed a UDP connection. + * + * \param conn A pointer to the uip_udp_conn structure for the connection. + * + * \hideinitializer + */ +#define uip_udp_remove(conn) (conn)->lport = 0 + +/** + * Bind a UDP connection to a local port. + * + * \param conn A pointer to the uip_udp_conn structure for the + * connection. + * + * \param port The local port number, in network byte order. + * + * \hideinitializer + */ +#define uip_udp_bind(conn, port) (conn)->lport = port + +/** + * Send a UDP datagram of length len on the current connection. + * + * This function can only be called in response to a UDP event (poll + * or newdata). The data must be present in the uip_buf buffer, at the + * place pointed to by the uip_appdata pointer. + * + * \param len The length of the data in the uip_buf buffer. + * + * \hideinitializer + */ +#define uip_udp_send(len) uip_send((char *)uip_appdata, len) + +/** @} */ + +/* uIP convenience and converting functions. */ + +/** + * \defgroup uipconvfunc uIP conversion functions + * @{ + * + * These functions can be used for converting between different data + * formats used by uIP. + */ + +/** + * Construct an IP address from four bytes. + * + * This function constructs an IP address of the type that uIP handles + * internally from four bytes. The function is handy for specifying IP + * addresses to use with e.g. the uip_connect() function. + * + * Example: + \code + uip_ipaddr_t ipaddr; + struct uip_conn *c; + + uip_ipaddr(&ipaddr, 192,168,1,2); + c = uip_connect(&ipaddr, HTONS(80)); + \endcode + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the IP address. + * + * \param addr0 The first octet of the IP address. + * \param addr1 The second octet of the IP address. + * \param addr2 The third octet of the IP address. + * \param addr3 The forth octet of the IP address. + * + * \hideinitializer + */ +#define uip_ipaddr(addr, addr0,addr1,addr2,addr3) do { \ + ((u16_t *)(addr))[0] = const_htons(((addr0) << 8) | (addr1)); \ + ((u16_t *)(addr))[1] = const_htons(((addr2) << 8) | (addr3)); \ + } while(0) + +/** + * Construct an IPv6 address from eight 16-bit words. + * + * This function constructs an IPv6 address. + * + * \hideinitializer + */ +#define uip_ip6addr(addr, addr0,addr1,addr2,addr3,addr4,addr5,addr6,addr7) do { \ + ((u16_t *)(addr))[0] = HTONS((addr0)); \ + ((u16_t *)(addr))[1] = HTONS((addr1)); \ + ((u16_t *)(addr))[2] = HTONS((addr2)); \ + ((u16_t *)(addr))[3] = HTONS((addr3)); \ + ((u16_t *)(addr))[4] = HTONS((addr4)); \ + ((u16_t *)(addr))[5] = HTONS((addr5)); \ + ((u16_t *)(addr))[6] = HTONS((addr6)); \ + ((u16_t *)(addr))[7] = HTONS((addr7)); \ + } while(0) + +/** + * Copy an IP address to another IP address. + * + * Copies an IP address from one place to another. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr_copy(&ipaddr2, &ipaddr1); + \endcode + * + * \param dest The destination for the copy. + * \param src The source from where to copy. + * + * \hideinitializer + */ +#define uip_ip4addr_copy(dest, src) memcpy(dest, src, sizeof(uip_ip4addr_t)) +#define uip_ip6addr_copy(dest, src) memcpy(dest, src, sizeof(uip_ip6addr_t)) + +/** + * Compare two IP addresses + * + * Compares two IP addresses. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + if(uip_ipaddr_cmp(&ipaddr2, &ipaddr1)) { + printf("They are the same"); + } + \endcode + * + * \param addr1 The first IP address. + * \param addr2 The second IP address. + * + * \hideinitializer + */ +#define uip_ip4addr_cmp(addr1, addr2) (memcmp(addr1, addr2, sizeof(uip_ip4addr_t)) == 0) +#define uip_ip6addr_cmp(addr1, addr2) (memcmp(addr1, addr2, sizeof(uip_ip6addr_t)) == 0) + +/** + * Compare two IP addresses with netmasks + * + * Compares two IP addresses with netmasks. The masks are used to mask + * out the bits that are to be compared. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2, mask; + + uip_ipaddr(&mask, 255,255,255,0); + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr(&ipaddr2, 192,16,1,3); + if(uip_ipaddr_maskcmp(&ipaddr1, &ipaddr2, &mask)) { + printf("They are the same"); + } + \endcode + * + * \param addr1 The first IP address. + * \param addr2 The second IP address. + * \param mask The netmask. + * + * \hideinitializer + */ +#define uip_ip4addr_maskcmp(addr1, addr2, mask) \ + (((((u16_t *)addr1)[0] & ((u16_t *)mask)[0]) == \ + (((u16_t *)addr2)[0] & ((u16_t *)mask)[0])) && \ + ((((u16_t *)addr1)[1] & ((u16_t *)mask)[1]) == \ + (((u16_t *)addr2)[1] & ((u16_t *)mask)[1]))) + + +/** + * Mask out the network part of an IP address. + * + * Masks out the network part of an IP address, given the address and + * the netmask. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2, netmask; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr(&netmask, 255,255,255,0); + uip_ipaddr_mask(&ipaddr2, &ipaddr1, &netmask); + \endcode + * + * In the example above, the variable "ipaddr2" will contain the IP + * address 192.168.1.0. + * + * \param dest Where the result is to be placed. + * \param src The IP address. + * \param mask The netmask. + * + * \hideinitializer + */ +#define uip_ip4addr_mask(dest, src, mask) do { \ + ((u16_t *)dest)[0] = ((u16_t *)src)[0] & ((u16_t *)mask)[0]; \ + ((u16_t *)dest)[1] = ((u16_t *)src)[1] & ((u16_t *)mask)[1]; \ + } while(0) + +/** + * Pick the first octet of an IP address. + * + * Picks out the first octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr1(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 1. + * + * \hideinitializer + */ +#define uip_ipaddr1(addr) (htons(((u16_t *)(addr))[0]) >> 8) + +/** + * Pick the second octet of an IP address. + * + * Picks out the second octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr2(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 2. + * + * \hideinitializer + */ +#define uip_ipaddr2(addr) (htons(((u16_t *)(addr))[0]) & 0xff) + +/** + * Pick the third octet of an IP address. + * + * Picks out the third octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr3(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 3. + * + * \hideinitializer + */ +#define uip_ipaddr3(addr) (htons(((u16_t *)(addr))[1]) >> 8) + +/** + * Pick the fourth octet of an IP address. + * + * Picks out the fourth octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr4(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 4. + * + * \hideinitializer + */ +#define uip_ipaddr4(addr) (htons(((u16_t *)(addr))[1]) & 0xff) + +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This macro is primarily used for converting constants from host + * byte order to network byte order. For converting variables to + * network byte order, use the htons() function instead. + * + * \hideinitializer + */ +#if 0 +#ifndef HTONS +# if UIP_BYTE_ORDER == UIP_BIG_ENDIAN +# define HTONS(n) (n) +# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +# define HTONS(n) (u16_t)((((u16_t) (n)) << 8) | (((u16_t) (n)) >> 8)) +# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +#else +#error "HTONS already defined!" +#endif /* HTONS */ +#endif + +#if UIP_BYTE_ORDER == UIP_BIG_ENDIAN +# error "Should not be here" +# define const_htons(n) (n) +# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +# define const_htons(n) (u16_t)((((u16_t) (n)) << 8) | (((u16_t) (n)) >> 8)) +# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ + +/* BWL */ +#if 0 +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This function is primarily used for converting variables from host + * byte order to network byte order. For converting constants to + * network byte order, use the HTONS() macro instead. + */ +#ifndef htons +u16_t htons(u16_t val); +#endif /* htons */ +#ifndef ntohs +#define ntohs htons +#endif +#endif + +/** @} */ + +/** + * Pointer to the application data in the packet buffer. + * + * This pointer points to the application data when the application is + * called. If the application wishes to send data, the application may + * use this space to write the data into before calling uip_send(). + */ +//extern void *uip_appdata; + +#if UIP_URGDATA > 0 +/* u8_t *uip_urgdata: + * + * This pointer points to any urgent data that has been received. Only + * present if compiled with support for urgent data (UIP_URGDATA). + */ +extern void *uip_urgdata; +#endif /* UIP_URGDATA > 0 */ + + +/** + * \defgroup uipdrivervars Variables used in uIP device drivers + * @{ + * + * uIP has a few global variables that are used in device drivers for + * uIP. + */ + +/** + * The length of the packet in the uip_buf buffer. + * + * The global variable uip_len holds the length of the packet in the + * uip_buf buffer. + * + * When the network device driver calls the uIP input function, + * uip_len should be set to the length of the packet in the uip_buf + * buffer. + * + * When sending packets, the device driver should use the contents of + * the uip_len variable to determine the length of the outgoing + * packet. + * + */ +//extern u16_t uip_len; + +/** @} */ + +#if UIP_URGDATA > 0 +extern u16_t uip_urglen, uip_surglen; +#endif /* UIP_URGDATA > 0 */ + + +/** + * Representation of a uIP TCP connection. + * + * The uip_conn structure is used for identifying a connection. All + * but one field in the structure are to be considered read-only by an + * application. The only exception is the appstate field whos purpose + * is to let the application store application-specific state (e.g., + * file pointers) for the connection. The type of this field is + * configured in the "uipopt.h" header file. + */ +struct __attribute__ ((__packed__)) uip_conn +{ + uip_ip4addr_t ripaddr; /**< The IP address of the remote host. */ + + u16_t lport; /**< The local TCP port, in network byte order. */ + u16_t rport; /**< The local remote TCP port, in network byte + order. */ + + u8_t rcv_nxt[4]; /**< The sequence number that we expect to + receive next. */ + u8_t snd_nxt[4]; /**< The sequence number that was last sent by + us. */ + u16_t len; /**< Length of the data that was previously sent. */ + u16_t mss; /**< Current maximum segment size for the + connection. */ + u16_t initialmss; /**< Initial maximum segment size for the + connection. */ + u8_t sa; /**< Retransmission time-out calculation state + variable. */ + u8_t sv; /**< Retransmission time-out calculation state + variable. */ + u8_t rto; /**< Retransmission time-out. */ + u8_t tcpstateflags; /**< TCP state and flags. */ + u8_t timer; /**< The retransmission timer. */ + u8_t nrtx; /**< The number of retransmissions for the last + segment sent. */ + + /** The application state. */ +/* BWL */ +// uip_tcp_appstate_t appstate; +}; + + +/** + * \addtogroup uiparch + * @{ + */ + +/** + * 4-byte array used for the 32-bit sequence number calculations. + */ +extern u8_t uip_acc32[4]; + +/** @} */ + + +#if UIP_UDP +/** + * Representation of a uIP UDP connection. + */ +struct uip_udp_conn { + uip_ip4addr_t ripaddr; /**< The IP address of the remote peer. */ + u16_t lport; /**< The local port number in network byte order. */ + u16_t rport; /**< The remote port number in network byte order. */ + u8_t ttl; /**< Default time-to-live. */ + + /** The application state. */ +// uip_udp_appstate_t appstate; +}; + +#endif /* UIP_UDP */ + +/** + * The structure holding the TCP/IP statistics that are gathered if + * UIP_STATISTICS is set to 1. + * + */ +struct uip_stats { + struct { + uip_stats_t drop; /**< Number of dropped packets at the IP + layer. */ + uip_stats_t recv; /**< Number of received packets at the IP + layer. */ + uip_stats_t sent; /**< Number of sent packets at the IP + layer. */ + uip_stats_t vhlerr; /**< Number of packets dropped due to wrong + IP version or header length. */ + uip_stats_t hblenerr; /**< Number of packets dropped due to wrong + IP length, high byte. */ + uip_stats_t lblenerr; /**< Number of packets dropped due to wrong + IP length, low byte. */ + uip_stats_t fragerr; /**< Number of packets dropped since they + were IP fragments. */ + uip_stats_t chkerr; /**< Number of packets dropped due to IP + checksum errors. */ + uip_stats_t protoerr; /**< Number of packets dropped since they + were neither ICMP, UDP nor TCP. */ + } ip; /**< IP statistics. */ + struct { + uip_stats_t drop; /**< Number of dropped ICMP packets. */ + uip_stats_t recv; /**< Number of received ICMP packets. */ + uip_stats_t sent; /**< Number of sent ICMP packets. */ + uip_stats_t typeerr; /**< Number of ICMP packets with a wrong + type. */ + } icmp; /**< ICMP statistics. */ + struct { + uip_stats_t drop; /**< Number of dropped TCP segments. */ + uip_stats_t recv; /**< Number of recived TCP segments. */ + uip_stats_t sent; /**< Number of sent TCP segments. */ + uip_stats_t chkerr; /**< Number of TCP segments with a bad + checksum. */ + uip_stats_t ackerr; /**< Number of TCP segments with a bad ACK + number. */ + uip_stats_t rst; /**< Number of recevied TCP RST (reset) segments. */ + uip_stats_t rexmit; /**< Number of retransmitted TCP segments. */ + uip_stats_t syndrop; /**< Number of dropped SYNs due to too few + connections was avaliable. */ + uip_stats_t synrst; /**< Number of SYNs for closed ports, + triggering a RST. */ + } tcp; /**< TCP statistics. */ +#if UIP_UDP + struct { + uip_stats_t drop; /**< Number of dropped UDP segments. */ + uip_stats_t recv; /**< Number of recived UDP segments. */ + uip_stats_t sent; /**< Number of sent UDP segments. */ + uip_stats_t chkerr; /**< Number of UDP segments with a bad + checksum. */ + } udp; /**< UDP statistics. */ +#endif /* UIP_UDP */ +}; + + +/*---------------------------------------------------------------------------*/ +/* All the stuff below this point is internal to uIP and should not be + * used directly by an application or by a device driver. + */ +/*---------------------------------------------------------------------------*/ +/* u8_t uip_flags: + * + * When the application is called, uip_flags will contain the flags + * that are defined in this file. Please read below for more + * infomation. + */ +//extern u8_t uip_flags; + +/* The following flags may be set in the global variable uip_flags + before calling the application callback. The UIP_ACKDATA, + UIP_NEWDATA, and UIP_CLOSE flags may both be set at the same time, + whereas the others are mutualy exclusive. Note that these flags + should *NOT* be accessed directly, but only through the uIP + functions/macros. */ + +#define UIP_ACKDATA 1 /* Signifies that the outstanding data was + acked and the application should send + out new data instead of retransmitting + the last data. */ +#define UIP_NEWDATA 2 /* Flags the fact that the peer has sent + us new data. */ +#define UIP_REXMIT 4 /* Tells the application to retransmit the + data that was last sent. */ +#define UIP_POLL 8 /* Used for polling the application, to + check if the application has data that + it wants to send. */ +#define UIP_CLOSE 16 /* The remote host has closed the + connection, thus the connection has + gone away. Or the application signals + that it wants to close the + connection. */ +#define UIP_ABORT 32 /* The remote host has aborted the + connection, thus the connection has + gone away. Or the application signals + that it wants to abort the + connection. */ +#define UIP_CONNECTED 64 /* We have got a connection from a remote + host and have set up a new connection + for it, or an active connection has + been successfully established. */ + +#define UIP_TIMEDOUT 128 /* The connection has been aborted due to + too many retransmissions. */ + +void uip_input(struct uip_stack *ustack); +void uip_periodic(struct uip_stack *ustack, int conn); + +/* uip_process(flag): + * + * The actual uIP function which does all the work. + */ +void uip_process(struct uip_stack *ustack, u8_t flag); + +/* The following flags are passed as an argument to the uip_process() + function. They are used to distinguish between the two cases where + uip_process() is called. It can be called either because we have + incoming data that should be processed, or because the periodic + timer has fired. These values are never used directly, but only in + the macrose defined in this file. */ + +#define UIP_DATA 1 /* Tells uIP that there is incoming + data in the uip_buf buffer. The + length of the data is stored in the + global variable uip_len. */ +#define UIP_TIMER 2 /* Tells uIP that the periodic timer + has fired. */ +#define UIP_POLL_REQUEST 3 /* Tells uIP that a connection should + be polled. */ +#define UIP_UDP_SEND_CONN 4 /* Tells uIP that a UDP datagram + should be constructed in the + uip_buf buffer. */ +#if UIP_UDP +#define UIP_UDP_TIMER 5 +#endif /* UIP_UDP */ + +/* The TCP states used in the uip_conn->tcpstateflags. */ +#define UIP_CLOSED 0 +#define UIP_SYN_RCVD 1 +#define UIP_SYN_SENT 2 +#define UIP_ESTABLISHED 3 +#define UIP_FIN_WAIT_1 4 +#define UIP_FIN_WAIT_2 5 +#define UIP_CLOSING 6 +#define UIP_TIME_WAIT 7 +#define UIP_LAST_ACK 8 +#define UIP_TS_MASK 15 + +#define UIP_STOPPED 16 + + +struct __attribute__ ((__packed__)) uip_tcp_hdr { + /* TCP header. */ + u16_t srcport, + destport; + u8_t seqno[4], + ackno[4], + tcpoffset, + flags, + wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +struct __attribute__ ((__packed__)) uip_ipv4_hdr { + /* IPv4 header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; +}; + +struct __attribute__ ((__packed__)) uip_ipv6_hdr { + /* IPv6 header. */ + u8_t vtc, + tcflow; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; +}; + +/* The TCP and IPv4 headers. */ +struct __attribute__ ((__packed__)) uip_tcp_ipv4_hdr { + /* IPv4 header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; + + /* TCP header. */ + u16_t srcport, + destport; + u8_t seqno[4], + ackno[4], + tcpoffset, + flags, + wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +/* The TCP and IP headers. */ +struct __attribute__ ((__packed__)) uip_tcp_ipv6_hdr { + /* IPv6 header. */ + u8_t vtc, + tcflow; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; + + /* TCP header. */ + u16_t srcport, + destport; + u8_t seqno[4], + ackno[4], + tcpoffset, + flags, + wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +/* The ICMPv4 */ +struct __attribute__ ((__packed__)) uip_icmpv4_hdr { + /* ICMP (echo) header. */ + u8_t type, icode; + u16_t icmpchksum; + u16_t id, seqno; +}; + +/* The ICMPv6 */ +struct __attribute__ ((__packed__)) uip_icmpv6_hdr { + /* ICMP (echo) header. */ + u8_t type, icode; + u16_t icmpchksum; + u8_t flags, reserved1, reserved2, reserved3; + u8_t icmp6data[16]; + u8_t options[1]; +}; + +/* The ICMP and IP headers. */ +struct __attribute__ ((__packed__)) uip_icmpip_hdr { +#if UIP_CONF_IPV6 + /* IPv6 header. */ + u8_t vtc, + tcf; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; +#else /* UIP_CONF_IPV6 */ + /* IPv4 header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; +#endif /* UIP_CONF_IPV6 */ + + /* ICMP (echo) header. */ + u8_t type, icode; + u16_t icmpchksum; +#if !UIP_CONF_IPV6 + u16_t id, seqno; +#else /* !UIP_CONF_IPV6 */ + u8_t flags, reserved1, reserved2, reserved3; + u8_t icmp6data[16]; + u8_t options[1]; +#endif /* !UIP_CONF_IPV6 */ +}; + +/* The UDP */ +struct __attribute__ ((__packed__)) uip_udp_hdr { + /* UDP header. */ + u16_t srcport, + destport; + u16_t udplen; + u16_t udpchksum; +}; + + +/* The UDP and IP headers. */ +struct __attribute__ ((__packed__)) uip_udpip_hdr { +#if UIP_CONF_IPV6 + /* IPv6 header. */ + u8_t vtc, + tcf; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; +#else /* UIP_CONF_IPV6 */ + /* IP header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; +#endif /* UIP_CONF_IPV6 */ + + /* UDP header. */ + u16_t srcport, + destport; + u16_t udplen; + u16_t udpchksum; +}; + + + +/** + * The buffer size available for user data in the \ref uip_buf buffer. + * + * This macro holds the available size for user data in the \ref + * uip_buf buffer. The macro is intended to be used for checking + * bounds of available user data. + * + * Example: + \code + snprintf(uip_appdata, UIP_APPDATA_SIZE, "%u\n", i); + \endcode + * + * \hideinitializer + */ +#define UIP_APPDATA_SIZE (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) + + +#define UIP_PROTO_ICMP 1 +#define UIP_PROTO_TCP 6 +#define UIP_PROTO_UDP 17 +#define UIP_PROTO_ICMP6 58 + +/* Header sizes. */ +#define UIP_IPv6_H_LEN 40 /* Size of IPv6 header */ +#define UIP_IPv4_H_LEN 20 /* Size of IPv4 header */ + +#define UIP_UDPH_LEN 8 /* Size of UDP header */ +#define UIP_TCPH_LEN 20 /* Size of TCP header */ + +#define UIP_IPv4_UDPH_LEN (UIP_UDPH_LEN + UIP_IPv4_H_LEN) /* Size of IPv4 + + UDP + header */ +#define UIP_IPv4_TCPH_LEN (UIP_TCPH_LEN + UIP_IPv4_H_LEN) /* Size of IPv4 + + TCP + header */ +#define UIP_TCP_IPv4_HLEN UIP_IPv4_TCPH_LEN + +#define UIP_IPv6_UDPH_LEN (UIP_UDPH_LEN + UIP_IPv6_H_LEN) /* Size of IPv6 + + UDP + header */ +#define UIP_IPv6_TCPH_LEN (UIP_TCPH_LEN + UIP_IPv6_H_LEN) /* Size of IPv6 + + TCP + header */ +#define UIP_TCP_IPv6_HLEN UIP_IPv6_TCPH_LEN + + +/** + * Calculate the Internet checksum over a buffer. + * + * The Internet checksum is the one's complement of the one's + * complement sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * \param buf A pointer to the buffer over which the checksum is to be + * computed. + * + * \param len The length of the buffer over which the checksum is to + * be computed. + * + * \return The Internet checksum of the buffer. + */ +u16_t uip_chksum(u16_t *buf, u16_t len); + +/** + * Calculate the IP header checksum of the packet header in uip_buf. + * + * The IP header checksum is the Internet checksum of the 20 bytes of + * the IP header. + * + * \return The IP header checksum of the IP header in the uip_buf + * buffer. + */ +u16_t uip_ipchksum(struct uip_stack *ustack); + +/** + * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. + * + * The TCP checksum is the Internet checksum of data contents of the + * TCP segment, and a pseudo-header as defined in RFC793. + * + * \return The TCP checksum of the TCP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_tcpchksum(struct uip_stack *ustack); + +/** + * Calculate the UDP checksum of the packet in uip_buf and uip_appdata. + * + * The UDP checksum is the Internet checksum of data contents of the + * UDP segment, and a pseudo-header as defined in RFC768. + * + * \return The UDP checksum of the UDP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_udpchksum(struct uip_stack *ustack); + +struct neighbor_entry { + uip_ip6addr_t ipaddr; + struct uip_eth_addr addr; + u8_t time; +}; + +#define UIP_NEIGHBOR_ENTRIES 8 + +struct uip_stack { + struct uip_eth_addr uip_ethaddr; + + u8_t *uip_buf; + + uint8_t *data_link_layer; /* Pointer to the data link layer */ + uint8_t *network_layer; /* Pointer to the network layer */ + void *uip_appdata; /* The uip_appdata pointer points to + application data. */ + void *uip_sappdata; /* The uip_appdata pointer points to + the application data which is to + be sent. */ +#if UIP_URGDATA > 0 + void *uip_urgdata; /* The uip_urgdata pointer points to + urgent data (out-of-band data), if + present. */ + u16_t uip_urglen, uip_surglen; +#endif /* UIP_URGDATA > 0 */ + + u16_t uip_len, uip_slen; /* The uip_len is either 8 or 16 bits, + depending on the maximum packet + size. */ + u8_t uip_flags; /* The uip_flags variable is used for + communication between the TCP/IP stack + and the application program. */ + struct uip_conn *uip_conn; /* uip_conn always points to the current + connection. */ + + struct uip_conn uip_conns[UIP_CONNS]; + /* The uip_conns array holds all TCP + connections. */ + u16_t uip_listenports[UIP_LISTENPORTS]; + /* The uip_listenports list all currently + listning ports. */ +#if UIP_UDP + struct uip_udp_conn *uip_udp_conn; + struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; +#endif /* UIP_UDP */ + + u16_t ipid; /* This ipid variable is an increasing + number that is used for the IP ID + field. */ + + u8_t iss[4]; /* The iss variable is used for the TCP + initial sequence number. */ + +#if UIP_ACTIVE_OPEN + u16_t lastport; /* Keeps track of the last port used for + a new connection. */ +#endif /* UIP_ACTIVE_OPEN */ + +#define IP_CONFIG_STATIC 0x01 +#define IP_CONFIG_DHCP 0x02 + u8_t ip_config; + + uip_ip4addr_t hostaddr, netmask, default_route_addr; + uip_ip6addr_t hostaddr6; + + uip_ip6addr_t link_local_addr; + + struct uip_stats stats; + + u8_t opt; + + pthread_mutex_t lock; + + /* IPv6 support */ + +#define UIP_SUPPORT_IPv6_ENABLED 0x01 +#define UIP_SUPPORT_IPv6_DISABLED 0x02 + u8_t enable_IPv6; + + struct neighbor_entry neighbor_entries[UIP_NEIGHBOR_ENTRIES]; + + /* DHCPC client attached */ + void *dhcpc; +}; + +/******************************************************************************* + * IPv6 Support + ******************************************************************************/ +int set_ipv6_link_local_address(struct uip_stack *ustack); + + + +#endif /* __UIP_H__ */ + + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/uip_arch.h b/brcm_iscsi_uio/src/uip/uip_arch.h new file mode 100644 index 0000000..f8f34e2 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip_arch.h @@ -0,0 +1,138 @@ +/** + * \addtogroup uip + * {@ + */ + +/** + * \defgroup uiparch Architecture specific uIP functions + * @{ + * + * The functions in the architecture specific module implement the IP + * check sum and 32-bit additions. + * + * The IP checksum calculation is the most computationally expensive + * operation in the TCP/IP stack and it therefore pays off to + * implement this in efficient assembler. The purpose of the uip-arch + * module is to let the checksum functions to be implemented in + * architecture specific assembler. + * + */ + +/** + * \file + * Declarations of architecture specific functions. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2001, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uip_arch.h,v 1.2 2006/06/07 09:15:19 adam Exp $ + * + */ + +#ifndef __UIP_ARCH_H__ +#define __UIP_ARCH_H__ + +#include "uip.h" + +/** + * Carry out a 32-bit addition. + * + * Because not all architectures for which uIP is intended has native + * 32-bit arithmetic, uIP uses an external C function for doing the + * required 32-bit additions in the TCP protocol processing. This + * function should add the two arguments and place the result in the + * global variable uip_acc32. + * + * \note The 32-bit integer pointed to by the op32 parameter and the + * result in the uip_acc32 variable are in network byte order (big + * endian). + * + * \param op32 A pointer to a 4-byte array representing a 32-bit + * integer in network byte order (big endian). + * + * \param op16 A 16-bit integer in host byte order. + */ +void uip_add32(u8_t *op32, u16_t op16, u8_t *uip_add32); + +/** + * Calculate the Internet checksum over a buffer. + * + * The Internet checksum is the one's complement of the one's + * complement sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * \note This function is not called in the current version of uIP, + * but future versions might make use of it. + * + * \param buf A pointer to the buffer over which the checksum is to be + * computed. + * + * \param len The length of the buffer over which the checksum is to + * be computed. + * + * \return The Internet checksum of the buffer. + */ +u16_t uip_chksum(u16_t *buf, u16_t len); + +/** + * Calculate the IP header checksum of the packet header in uip_buf. + * + * The IP header checksum is the Internet checksum of the 20 bytes of + * the IP header. + * + * \return The IP header checksum of the IP header in the uip_buf + * buffer. + */ +u16_t uip_ipchksum(struct uip_stack *ustack); + +/** + * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. + * + * The TCP checksum is the Internet checksum of data contents of the + * TCP segment, and a pseudo-header as defined in RFC793. + * + * \note The uip_appdata pointer that points to the packet data may + * point anywhere in memory, so it is not possible to simply calculate + * the Internet checksum of the contents of the uip_buf buffer. + * + * \return The TCP checksum of the TCP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_tcpchksum(struct uip_stack *ustack); + +u16_t uip_udpchksum(struct uip_stack *ustack); + +/** @} */ +/** @} */ + +#endif /* __UIP_ARCH_H__ */ diff --git a/brcm_iscsi_uio/src/uip/uip_arp.c b/brcm_iscsi_uio/src/uip/uip_arp.c new file mode 100644 index 0000000..a894d1b --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip_arp.c @@ -0,0 +1,462 @@ +#include +//#include + +#include +#include + +#include "logger.h" +#include "packet.h" + +/** + * \addtogroup uip + * @{ + */ + +/** + * \defgroup uiparp uIP Address Resolution Protocol + * @{ + * + * The Address Resolution Protocol ARP is used for mapping between IP + * addresses and link level addresses such as the Ethernet MAC + * addresses. ARP uses broadcast queries to ask for the link level + * address of a known IP address and the host which is configured with + * the IP address for which the query was meant, will respond with its + * link level address. + * + * \note This ARP implementation only supports Ethernet. + */ + +/** + * \file + * Implementation of the ARP Address Resolution Protocol. + * \author Adam Dunkels + * + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uip_arp.c,v 1.8 2006/06/02 23:36:21 adam Exp $ + * + */ + + +#include "uip_arp.h" +#include "uip_eth.h" + +#include +#include + +static const struct uip_eth_addr broadcast_ethaddr = + {{0xff,0xff,0xff,0xff,0xff,0xff}}; +static const u16_t broadcast_ipaddr[2] = {0xffff,0xffff}; + +pthread_mutex_t arp_table_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct arp_entry arp_table[UIP_ARPTAB_SIZE]; + +static u8_t arptime; + +/** + * Initialize the ARP module. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +uip_arp_init(void) +{ + u8_t i; + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + memset(arp_table[i].ipaddr, 0, 4); + } + + pthread_mutex_init(&arp_table_mutex, NULL); +} +/*-----------------------------------------------------------------------------------*/ +/** + * Periodic ARP processing function. + * + * This function performs periodic timer processing in the ARP module + * and should be called at regular intervals. The recommended interval + * is 10 seconds between the calls. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +uip_arp_timer(void) +{ + u8_t i; + struct arp_entry *tabptr; + + ++arptime; + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + tabptr = &arp_table[i]; + if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 && + arptime - tabptr->time >= UIP_ARP_MAXAGE) { + memset(tabptr->ipaddr, 0, 4); + } + } + +} +/*-----------------------------------------------------------------------------------*/ +static void +uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr) +{ + u8_t i; + register struct arp_entry *tabptr; + /* Walk through the ARP mapping table and try to find an entry to + update. If none is found, the IP -> MAC address mapping is + inserted in the ARP table. */ + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + + tabptr = &arp_table[i]; + /* Only check those entries that are actually in use. */ + if(tabptr->ipaddr[0] != 0 && + tabptr->ipaddr[1] != 0) { + + /* Check if the source IP address of the incoming packet matches + the IP address in this ARP table entry. */ + if(ipaddr[0] == tabptr->ipaddr[0] && + ipaddr[1] == tabptr->ipaddr[1]) { + + /* An old entry found, update this and return. */ + memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); + tabptr->time = arptime; + + return; + } + } + } + + /* If we get here, no existing ARP table entry was found, so we + create one. */ + + /* First, we try to find an unused entry in the ARP table. */ + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + tabptr = &arp_table[i]; + if(tabptr->ipaddr[0] == 0 && + tabptr->ipaddr[1] == 0) { + break; + } + } + + /* If no unused entry is found, we try to find the oldest entry and + throw it away. */ + if(i == UIP_ARPTAB_SIZE) { + u8_t c; + u8_t tmpage = 0; + c = 0; + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + tabptr = &arp_table[i]; + if(arptime - tabptr->time > tmpage) { + tmpage = arptime - tabptr->time; + c = i; + } + } + i = c; + tabptr = &arp_table[i]; + } + + /* Now, i is the ARP table entry which we will fill with the new + information. */ + memcpy(tabptr->ipaddr, ipaddr, 4); + memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6); + tabptr->time = arptime; +} + +/** + * ARP processing for incoming ARP packets. + * + * This function should be called by the device driver when an ARP + * packet has been received. The function will act differently + * depending on the ARP packet type: if it is a reply for a request + * that we previously sent out, the ARP cache will be filled in with + * the values from the ARP reply. If the incoming ARP packet is an ARP + * request for our IP address, an ARP reply packet is created and put + * into the uip_buf[] buffer. + * + * When the function returns, the value of the global variable uip_len + * indicates whether the device driver should send out a packet or + * not. If uip_len is zero, no packet should be sent. If uip_len is + * non-zero, it contains the length of the outbound packet that is + * present in the uip_buf[] buffer. + * + * This function expects an ARP packet with a prepended Ethernet + * header in the uip_buf[] buffer, and the length of the packet in the + * global variable uip_len. + */ + +void +uip_arp_arpin(struct uip_stack *ustack, packet_t *pkt) +{ + struct arp_hdr *arp; + struct uip_eth_hdr *eth; + + if(pkt->buf_size < sizeof(struct arp_hdr)) { + pkt->buf_size = 0; + return; + } + pkt->buf_size = 0; + + eth = (struct uip_eth_hdr *)pkt->data_link_layer; + arp = (struct arp_hdr *)pkt->network_layer; + + switch(arp->opcode) { + case const_htons(ARP_REQUEST): + /* ARP request. If it asked for our address, we send out a + reply. */ + if(uip_ip4addr_cmp(arp->dipaddr, ustack->hostaddr)) { + /* First, we register the one who made the request in our ARP + table, since it is likely that we will do more communication + with this host in the future. */ + uip_arp_update(arp->sipaddr, &arp->shwaddr); + + /* The reply opcode is 2. */ + arp->opcode = htons(2); + + memcpy(arp->dhwaddr.addr, arp->shwaddr.addr, 6); + memcpy(arp->shwaddr.addr, ustack->uip_ethaddr.addr, 6); + memcpy(eth->src.addr, ustack->uip_ethaddr.addr, 6); + memcpy(eth->dest.addr, arp->dhwaddr.addr, 6); + + arp->dipaddr[0] = arp->sipaddr[0]; + arp->dipaddr[1] = arp->sipaddr[1]; + arp->sipaddr[0] = ustack->hostaddr[0]; + arp->sipaddr[1] = ustack->hostaddr[1]; + + eth->type = htons(UIP_ETHTYPE_ARP); + pkt->buf_size = sizeof(*arp) + sizeof(*eth); + } + break; + case const_htons(ARP_REPLY): + uip_arp_update(arp->sipaddr, &arp->shwaddr); + break; + default: + LOG_WARN("Unknown ARP opcode: %d", ntohs(arp->opcode)); + break; + } + + return; +} + +/** + * Prepend Ethernet header to an outbound IP packet and see if we need + * to send out an ARP request. + * + * This function should be called before sending out an IP packet. The + * function checks the destination IP address of the IP packet to see + * what Ethernet MAC address that should be used as a destination MAC + * address on the Ethernet. + * + * If the destination IP address is in the local network (determined + * by logical ANDing of netmask and our IP address), the function + * checks the ARP cache to see if an entry for the destination IP + * address is found. If so, an Ethernet header is prepended and the + * function returns. If no ARP cache entry is found for the + * destination IP address, the packet in the uip_buf[] is replaced by + * an ARP request packet for the IP address. The IP packet is dropped + * and it is assumed that they higher level protocols (e.g., TCP) + * eventually will retransmit the dropped packet. + * + * If the destination IP address is not on the local network, the IP + * address of the default router is used instead. + * + * When the function returns, a packet is present in the uip_buf[] + * buffer, and the length of the packet is in the global variable + * uip_len. + */ + +dest_ipv4_addr_t +uip_determine_dest_ipv4_addr(struct uip_stack *ustack, + u16_t *ipaddr) +{ + struct arp_hdr *arp; + struct ethip_hdr *ethip_buf; + struct uip_eth_hdr *eth; + + arp = (struct arp_hdr *)ustack->network_layer; + ethip_buf = (struct ethip_hdr *)ustack->uip_buf; + eth = (struct uip_eth_hdr *) ustack->data_link_layer; + + /* Find the destination IP address in the ARP table and construct + the Ethernet header. If the destination IP addres isn't on the + local network, we use the default router's IP address instead. + + If not ARP table entry is found, we overwrite the original IP + packet with an ARP request for the IP address. */ + + /* First check if destination is a local broadcast. */ + if(uip_ip4addr_cmp(ethip_buf->destipaddr, broadcast_ipaddr)) { + memcpy(ethip_buf->ethhdr.dest.addr, broadcast_ethaddr.addr, 6); + + return LOCAL_BROADCAST; + } else { + /* Check if the destination address is on the local network. */ + if(!uip_ip4addr_maskcmp(ethip_buf->destipaddr, + ustack->hostaddr, + ustack->netmask)) { + /* Destination address was not on the local network, so we need to + use the default router's IP address instead of the destination + address when determining the MAC address. */ + uip_ip4addr_copy(ipaddr, ustack->default_route_addr); + } else { + /* Else, we use the destination IP address. */ + uip_ip4addr_copy(ipaddr, ethip_buf->destipaddr); + } + + return NONLOCAL_BROADCAST; + } +} + +arp_out_t +is_in_arp_table(u16_t *ipaddr, + struct arp_entry **tabptr) +{ + u8_t i; + + pthread_mutex_lock(&arp_table_mutex); + + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + if(uip_ip4addr_cmp(ipaddr, arp_table[i].ipaddr)) { + *tabptr = &arp_table[i]; + break; + } + } + + pthread_mutex_unlock(&arp_table_mutex); + + if(i == UIP_ARPTAB_SIZE) { + return NOT_IN_ARP_TABLE; + } else { + return IS_IN_ARP_TABLE; + } +} + +void +uip_build_arp_request(struct uip_stack *ustack, + u16_t *ipaddr) +{ + struct arp_hdr *arp; + struct uip_eth_hdr *eth; + + arp = (struct arp_hdr *)ustack->network_layer; + eth = (struct uip_eth_hdr *) ustack->data_link_layer; + + /* The destination address was not in our ARP table, so we + overwrite the IP packet with an ARP request. */ + + memset(eth->dest.addr, 0xff, 6); + memset(arp->dhwaddr.addr, 0x00, 6); + memcpy(eth->src.addr, ustack->uip_ethaddr.addr, 6); + memcpy(arp->shwaddr.addr, ustack->uip_ethaddr.addr, 6); + + uip_ip4addr_copy(arp->dipaddr, ipaddr); + uip_ip4addr_copy(arp->sipaddr, ustack->hostaddr); + arp->opcode = const_htons(ARP_REQUEST); /* ARP request. */ + arp->hwtype = const_htons(ARP_HWTYPE_ETH); + arp->protocol = const_htons(UIP_ETHTYPE_IPv4); + arp->hwlen = 6; + arp->protolen = 4; + eth->type = const_htons(UIP_ETHTYPE_ARP); + + ustack->uip_appdata = &ustack->uip_buf[UIP_TCP_IPv4_HLEN + UIP_LLH_LEN]; + + ustack->uip_len = sizeof(*arp) + sizeof(*eth); +} + +void +uip_build_eth_header(struct uip_stack *ustack, + u16_t *ipaddr, + struct arp_entry *tabptr, + struct packet *pkt) +{ + struct uip_ipv4_hdr *ip_buf; + struct uip_eth_hdr *eth; + + ip_buf = (struct uip_ipv4_hdr *)ustack->network_layer; + eth = (struct uip_eth_hdr *) ustack->data_link_layer; + + /* First check if destination is a local broadcast. */ + if(uip_ip4addr_cmp(ip_buf->destipaddr, broadcast_ipaddr)) { + memcpy(eth->dest.addr, broadcast_ethaddr.addr, 6); + } else { + /* Build an ethernet header. */ + memcpy(eth->dest.addr, tabptr->ethaddr.addr, 6); + } + memcpy(eth->src.addr, ustack->uip_ethaddr.addr, 6); + + eth->type = htons(UIP_ETHTYPE_IPv4); + + ustack->uip_len += sizeof(struct uip_eth_hdr); + pkt->buf_size += sizeof(struct uip_eth_hdr); +} + +static struct arp_entry * +uip_get_arp_entry(int index) +{ + return &arp_table[index]; +} + +int uip_lookup_arp_entry(uint32_t ip_addr, uint8_t *mac_addr) +{ + int i; + int rc = -EINVAL; + + pthread_mutex_lock(&arp_table_mutex); + + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + struct arp_entry *entry = uip_get_arp_entry(i); + + if( ((entry->ipaddr[1] << 16) == (ip_addr & 0xffff0000)) && + ((entry->ipaddr[0]) == (ip_addr & 0x0000ffff))) { + struct in_addr addr; + char * addr_str; + + addr.s_addr = ip_addr; + addr_str = inet_ntoa(addr); + + memcpy(mac_addr, entry->ethaddr.addr, 6); + + LOG_INFO("Found %s at %02x:%02x:%02x:%02x:%02x:%02x", + addr_str, + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + rc = 0; + break; + } + } + + pthread_mutex_unlock(&arp_table_mutex); + return rc; +} + +/*-----------------------------------------------------------------------------------*/ + +/** @} */ +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/uip_arp.h b/brcm_iscsi_uio/src/uip/uip_arp.h new file mode 100644 index 0000000..f9e1047 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip_arp.h @@ -0,0 +1,203 @@ +/** + * \addtogroup uip + * @{ + */ + +/** + * \addtogroup uiparp + * @{ + */ + +/** + * \file + * Macros and definitions for the ARP module. + * \author Adam Dunkels + */ + + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uip_arp.h,v 1.5 2006/06/11 21:46:39 adam Exp $ + * + */ + +#ifndef __UIP_ARP_H__ +#define __UIP_ARP_H__ + +#include "packet.h" +#include "uip.h" +#include "uip_eth.h" + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +#define ARP_HWTYPE_ETH 1 + +struct __attribute__ ((__packed__)) arp_hdr { + u16_t hwtype; + u16_t protocol; + u8_t hwlen; + u8_t protolen; + u16_t opcode; + struct uip_eth_addr shwaddr; + u16_t sipaddr[2]; + struct uip_eth_addr dhwaddr; + u16_t dipaddr[2]; +}; + +struct __attribute__ ((__packed__)) ethip_hdr { + struct uip_eth_hdr ethhdr; + /* IP header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; +}; + + +struct arp_entry { + u16_t ipaddr[2]; + struct uip_eth_addr ethaddr; + u8_t time; +}; + + +/* The uip_arp_init() function must be called before any of the other + ARP functions. */ +void uip_arp_init(void); + +/* The uip_arp_ipin() function should be called whenever an IP packet + arrives from the Ethernet. This function refreshes the ARP table or + inserts a new mapping if none exists. The function assumes that an + IP packet with an Ethernet header is present in the uip_buf buffer + and that the length of the packet is in the uip_len variable. */ +/*void uip_arp_ipin(void);*/ +#define uip_arp_ipin() + +/* The uip_arp_arpin() should be called when an ARP packet is received + by the Ethernet driver. This function also assumes that the + Ethernet frame is present in the uip_buf buffer. When the + uip_arp_arpin() function returns, the contents of the uip_buf + buffer should be sent out on the Ethernet if the uip_len variable + is > 0. */ +void uip_arp_arpin(struct uip_stack *ustack, struct packet *pkt); + +typedef enum { + ARP_SENT = 1, + ETH_HEADER_APPEDEND = 2, +} arp_out_t; + +typedef enum { + LOCAL_BROADCAST = 1, + NONLOCAL_BROADCAST = 2, +} dest_ipv4_addr_t; + +typedef enum { + IS_IN_ARP_TABLE = 1, + NOT_IN_ARP_TABLE = 2, +} arp_table_query_t; + +dest_ipv4_addr_t +uip_determine_dest_ipv4_addr(struct uip_stack *ustack, + u16_t *ipaddr); +arp_out_t +is_in_arp_table(u16_t *ipaddr, + struct arp_entry **tabptr); + +void +uip_build_arp_request(struct uip_stack *ustack, + u16_t *ipaddr); + +void +uip_build_eth_header(struct uip_stack *ustack, + u16_t *ipaddr, + struct arp_entry *tabptr, + struct packet *pkt); + +/* The uip_arp_out() function should be called when an IP packet + should be sent out on the Ethernet. This function creates an + Ethernet header before the IP header in the uip_buf buffer. The + Ethernet header will have the correct Ethernet MAC destination + address filled in if an ARP table entry for the destination IP + address (or the IP address of the default router) is present. If no + such table entry is found, the IP packet is overwritten with an ARP + request and we rely on TCP to retransmit the packet that was + overwritten. In any case, the uip_len variable holds the length of + the Ethernet frame that should be transmitted. */ +arp_out_t uip_arp_out(struct uip_stack *ustack); + +/* The uip_arp_timer() function should be called every ten seconds. It + is responsible for flushing old entries in the ARP table. */ +void uip_arp_timer(void); + +int uip_lookup_arp_entry(uint32_t ip_addr, uint8_t *mac_addr); + +/** @} */ + +/** + * \addtogroup uipconffunc + * @{ + */ + + +/** + * Specifiy the Ethernet MAC address. + * + * The ARP code needs to know the MAC address of the Ethernet card in + * order to be able to respond to ARP queries and to generate working + * Ethernet headers. + * + * \note This macro only specifies the Ethernet MAC address to the ARP + * code. It cannot be used to change the MAC address of the Ethernet + * card. + * + * \param eaddr A pointer to a struct uip_eth_addr containing the + * Ethernet MAC address of the Ethernet card. + * + * \hideinitializer + */ +#define uip_setethaddr(eaddr) do {uip_ethaddr.addr[0] = eaddr.addr[0]; \ + uip_ethaddr.addr[1] = eaddr.addr[1];\ + uip_ethaddr.addr[2] = eaddr.addr[2];\ + uip_ethaddr.addr[3] = eaddr.addr[3];\ + uip_ethaddr.addr[4] = eaddr.addr[4];\ + uip_ethaddr.addr[5] = eaddr.addr[5];} while(0) + +/** @} */ +/** @} */ + +#endif /* __UIP_ARP_H__ */ diff --git a/brcm_iscsi_uio/src/uip/uip_eth.c b/brcm_iscsi_uio/src/uip/uip_eth.c new file mode 100644 index 0000000..5d410bf --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip_eth.c @@ -0,0 +1,24 @@ +/* uip_eth.c: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include "uip.h" +#include "uip_eth.h" + +int is_vlan_packet(struct uip_vlan_eth_hdr *hdr) +{ + /* The TPID field in a 802.1Q Header must be 0x8100 */ + if(hdr->tpid == const_htons(UIP_ETHTYPE_8021Q)) + { + return 1; + } + + return 0; +} diff --git a/brcm_iscsi_uio/src/uip/uip_eth.h b/brcm_iscsi_uio/src/uip/uip_eth.h new file mode 100644 index 0000000..1138e2e --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip_eth.h @@ -0,0 +1,43 @@ +#ifndef __UIP_ETH_H__ +#define __UIP_ETH_H__ + +#include "uipopt.h" + +/******************************************************************************* + * Ether types + ******************************************************************************/ +#define UIP_ETHTYPE_ARP 0x0806 +#define UIP_ETHTYPE_IPv4 0x0800 +#define UIP_ETHTYPE_8021Q 0x8100 +#define UIP_ETHTYPE_IPv6 0x86dd + +/** + * Representation of a 48-bit Ethernet address. + */ +struct uip_eth_addr { + u8_t addr[6]; +}; + +/** + * The Ethernet header. + */ +struct __attribute__ ((__packed__)) uip_eth_hdr { + struct uip_eth_addr dest; + struct uip_eth_addr src; + u16_t type; +}; + +/** + * The 802.1Q Ethernet header (VLAN). + */ +struct __attribute__ ((__packed__)) uip_vlan_eth_hdr { + struct uip_eth_addr dest; + struct uip_eth_addr src; + u16_t tpid; + u16_t vid; + u16_t type; +}; + +int is_vlan_packet(struct uip_vlan_eth_hdr *hdr); + +#endif /* __UIP_ETH_H__ */ diff --git a/brcm_iscsi_uio/src/uip/uip_ipv6.c b/brcm_iscsi_uio/src/uip/uip_ipv6.c new file mode 100644 index 0000000..b673b3a --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip_ipv6.c @@ -0,0 +1,2315 @@ +#include +#include "uip.h" +#include "dhcpc.h" +#include "brcm-iscsi.h" + +#define DEBUG_PRINTF(...) /*printf(__VA_ARGS__)*/ + +/** + * \defgroup uip The uIP TCP/IP stack + * @{ + * + * uIP is an implementation of the TCP/IP protocol stack intended for + * small 8-bit and 16-bit microcontrollers. + * + * uIP provides the necessary protocols for Internet communication, + * with a very small code footprint and RAM requirements - the uIP + * code size is on the order of a few kilobytes and RAM usage is on + * the order of a few hundred bytes. + */ + +/** + * \file + * The uIP TCP/IP stack code. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uip.c,v 1.65 2006/06/11 21:46:39 adam Exp $ + * + */ + +/* + * uIP is a small implementation of the IP, UDP and TCP protocols (as + * well as some basic ICMP stuff). The implementation couples the IP, + * UDP, TCP and the application layers very tightly. To keep the size + * of the compiled code down, this code frequently uses the goto + * statement. While it would be possible to break the uip_process() + * function into many smaller functions, this would increase the code + * size because of the overhead of parameter passing and the fact that + * the optimier would not be as efficient. + * + * The principle is that we have a small buffer, called the uip_buf, + * in which the device driver puts an incoming packet. The TCP/IP + * stack parses the headers in the packet, and calls the + * application. If the remote host has sent data to the application, + * this data is present in the uip_buf and the application read the + * data from there. It is up to the application to put this data into + * a byte stream if needed. The application will not be fed with data + * that is out of sequence. + * + * If the application whishes to send data to the peer, it should put + * its data into the uip_buf. The uip_appdata pointer points to the + * first available byte. The TCP/IP stack will calculate the + * checksums, and fill in the necessary header fields and finally send + * the packet back to the peer. +*/ + +#include "logger.h" + +#include "uip.h" +#include "uipopt.h" +#include "uip_arch.h" + +#if UIP_CONF_IPV6 +#include "uip-neighbor.h" +#endif /* UIP_CONF_IPV6 */ + +#include + +/*---------------------------------------------------------------------------*/ +/* Variable definitions. */ + + +static const uip_ipaddr_t all_ones_addr = +#if UIP_CONF_IPV6 + {0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff}; +#else /* UIP_CONF_IPV6 */ + {0xffff,0xffff}; +#endif /* UIP_CONF_IPV6 */ +static const uip_ipaddr_t all_zeroes_addr = +#if UIP_CONF_IPV6 + {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; +#else /* UIP_CONF_IPV6 */ + {0x0000,0x0000}; +#endif /* UIP_CONF_IPV6 */ + +#if 0 +#if UIP_FIXEDETHADDR +const struct uip_eth_addr uip_ethaddr = {{UIP_ETHADDR0, + UIP_ETHADDR1, + UIP_ETHADDR2, + UIP_ETHADDR3, + UIP_ETHADDR4, + UIP_ETHADDR5}}; +#else +#endif +#endif +//struct uip_eth_addr uip_ethaddr = {{0,0,0,0,0,0}}; +#if 0 +#endif + +#if 0 +#ifndef UIP_CONF_EXTERNAL_BUFFER +u8_t uip_buf[UIP_BUFSIZE + 2]; /* The packet buffer that contains + incoming packets. */ +#endif /* UIP_CONF_EXTERNAL_BUFFER */ + +void *uip_appdata; /* The uip_appdata pointer points to + application data. */ +void *uip_sappdata; /* The uip_appdata pointer points to + the application data which is to + be sent. */ +#if UIP_URGDATA > 0 +void *uip_urgdata; /* The uip_urgdata pointer points to + urgent data (out-of-band data), if + present. */ +u16_t uip_urglen, uip_surglen; +#endif /* UIP_URGDATA > 0 */ + +u16_t uip_len, uip_slen; + /* The uip_len is either 8 or 16 bits, + depending on the maximum packet + size. */ + +u8_t uip_flags; /* The uip_flags variable is used for + communication between the TCP/IP stack + and the application program. */ +struct uip_conn *uip_conn; /* uip_conn always points to the current + connection. */ + +struct uip_conn uip_conns[UIP_CONNS]; + /* The uip_conns array holds all TCP + connections. */ +u16_t uip_listenports[UIP_LISTENPORTS]; + /* The uip_listenports list all currently + listning ports. */ +#if UIP_UDP +struct uip_udp_conn *uip_udp_conn; +struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; +#endif /* UIP_UDP */ + +static u16_t ipid; /* Ths ipid variable is an increasing + number that is used for the IP ID + field. */ + +void uip_setipid(u16_t id) { ipid = id; } + +static u8_t iss[4]; /* The iss variable is used for the TCP + initial sequence number. */ + +#if UIP_ACTIVE_OPEN +static u16_t lastport; /* Keeps track of the last port used for + a new connection. */ +#endif /* UIP_ACTIVE_OPEN */ + +/* Temporary variables. */ +u8_t uip_acc32[4]; +static u8_t c, opt; +static u16_t tmp16; +#endif + +/* Structures and definitions. */ +#define TCP_FIN 0x01 +#define TCP_SYN 0x02 +#define TCP_RST 0x04 +#define TCP_PSH 0x08 +#define TCP_ACK 0x10 +#define TCP_URG 0x20 +#define TCP_CTL 0x3f + +#define TCP_OPT_END 0 /* End of TCP options list */ +#define TCP_OPT_NOOP 1 /* "No-operation" TCP option */ +#define TCP_OPT_MSS 2 /* Maximum segment size TCP option */ + +#define TCP_OPT_MSS_LEN 4 /* Length of TCP MSS option. */ + +#define ICMP_ECHO_REPLY 0 +#define ICMP_ECHO 8 + +#define ICMP6_ECHO_REPLY 129 +#define ICMP6_ECHO 128 +#define ICMP6_NEIGHBOR_SOLICITATION 135 +#define ICMP6_NEIGHBOR_ADVERTISEMENT 136 + +#define ICMP6_FLAG_S (1 << 6) + +#define ICMP6_OPTION_SOURCE_LINK_ADDRESS 1 +#define ICMP6_OPTION_TARGET_LINK_ADDRESS 2 + + +/* Macros. */ +#define BUF(ustack) ((struct uip_tcpip_hdr *)&ustack->uip_buf[UIP_LLH_LEN]) +#define FBUF ((struct uip_tcpip_hdr *)&uip_reassbuf[0]) +#define ICMPBUF(ustack) ((struct uip_icmpip_hdr *)&ustack->uip_buf[UIP_LLH_LEN]) +#define UDPBUF(ustack) ((struct uip_udpip_hdr *)&ustack->uip_buf[UIP_LLH_LEN]) + + +void uip_sethostaddr(struct uip_stack *ustack, uip_ipaddr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ipaddr_copy(ustack->uip_hostaddr, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setdraddr(struct uip_stack *ustack, uip_ipaddr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ipaddr_copy(ustack->uip_draddr, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setnetmask(struct uip_stack *ustack, uip_ipaddr_t *addr) +{ + pthread_mutex_lock(&ustack->lock); + uip_ipaddr_copy(ustack->uip_netmask, (addr)); + pthread_mutex_unlock(&ustack->lock); +} + +void uip_setethernetmac(struct uip_stack *ustack, uint8_t *mac) +{ + pthread_mutex_lock(&ustack->lock); + memcpy(ustack->uip_ethaddr.addr, (mac), 6); + pthread_mutex_unlock(&ustack->lock); +} + + +#if ! UIP_ARCH_ADD32 +void +uip_add32(u8_t *op32, u16_t op16, u8_t *uip_acc32) +{ + uip_acc32[3] = op32[3] + (op16 & 0xff); + uip_acc32[2] = op32[2] + (op16 >> 8); + uip_acc32[1] = op32[1]; + uip_acc32[0] = op32[0]; + + if(uip_acc32[2] < (op16 >> 8)) { + ++uip_acc32[1]; + if(uip_acc32[1] == 0) { + ++uip_acc32[0]; + } + } + + + if(uip_acc32[3] < (op16 & 0xff)) { + ++uip_acc32[2]; + if(uip_acc32[2] == 0) { + ++uip_acc32[1]; + if(uip_acc32[1] == 0) { + ++uip_acc32[0]; + } + } + } +} + +#endif /* UIP_ARCH_ADD32 */ + +#if ! UIP_ARCH_CHKSUM +/*---------------------------------------------------------------------------*/ +static u16_t +chksum(u16_t sum, const u8_t *data, u16_t len) +{ + u16_t t; + const u8_t *dataptr; + const u8_t *last_byte; + + dataptr = data; + last_byte = data + len - 1; + + while(dataptr < last_byte) { /* At least two more bytes */ + t = (dataptr[0] << 8) + dataptr[1]; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + dataptr += 2; + } + + if(dataptr == last_byte) { + t = (dataptr[0] << 8) + 0; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + } + + /* Return sum in host byte order. */ + return sum; +} +/*---------------------------------------------------------------------------*/ +u16_t +uip_chksum(u16_t *data, u16_t len) +{ + return htons(chksum(0, (u8_t *)data, len)); +} +/*---------------------------------------------------------------------------*/ +#ifndef UIP_ARCH_IPCHKSUM +u16_t +uip_ipchksum(struct uip_stack *ustack) +{ + u16_t sum; + + sum = chksum(0, &ustack->uip_buf[UIP_LLH_LEN], UIP_IPH_LEN); + DEBUG_PRINTF("uip_ipchksum: sum 0x%04x\n", sum); + return (sum == 0) ? 0xffff : htons(sum); +} +#endif +/*---------------------------------------------------------------------------*/ +static u16_t +upper_layer_chksum(struct uip_stack *ustack, u8_t proto) +{ + u16_t upper_layer_len; + u16_t sum; + +#if UIP_CONF_IPV6 + upper_layer_len = (((u16_t)(BUF(ustack)->len[0]) << 8) + BUF(ustack)->len[1]); +#else /* UIP_CONF_IPV6 */ + upper_layer_len = (((u16_t)(BUF(ustack)->len[0]) << 8) + BUF(ustack)->len[1]) - UIP_IPH_LEN; +#endif /* UIP_CONF_IPV6 */ + + /* First sum pseudoheader. */ + + /* IP protocol and length fields. This addition cannot carry. */ + sum = upper_layer_len + proto; + /* Sum IP source and destination addresses. */ + sum = chksum(sum, (u8_t *)&BUF(ustack)->srcipaddr[0], 2 * sizeof(uip_ipaddr_t)); + + /* Sum TCP header and data. */ + sum = chksum(sum, &ustack->uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], + upper_layer_len); + + return (sum == 0) ? 0xffff : htons(sum); +} +/*---------------------------------------------------------------------------*/ +#if UIP_CONF_IPV6 +u16_t +uip_icmp6chksum(void) +{ + return upper_layer_chksum(UIP_PROTO_ICMP6); + +} +#endif /* UIP_CONF_IPV6 */ +/*---------------------------------------------------------------------------*/ +u16_t +uip_tcpchksum(struct uip_stack *ustack) +{ + return upper_layer_chksum(ustack, UIP_PROTO_TCP); +} +/*---------------------------------------------------------------------------*/ +#if UIP_UDP_CHECKSUMS +u16_t +uip_udpchksum(struct uip_stack *ustack) +{ + return upper_layer_chksum(ustack, UIP_PROTO_UDP); +} +#endif /* UIP_UDP_CHECKSUMS */ +#endif /* UIP_ARCH_CHKSUM */ +/*---------------------------------------------------------------------------*/ +void +uip_init(struct uip_stack *ustack, uint8_t ipv6_enabled) +{ + u8_t c; + + for(c = 0; c < UIP_LISTENPORTS; ++c) { + ustack->uip_listenports[c] = 0; + } + for(c = 0; c < UIP_CONNS; ++c) { + ustack->uip_conns[c].tcpstateflags = UIP_CLOSED; + } +#if UIP_ACTIVE_OPEN + ustack->lastport = 1024; +#endif /* UIP_ACTIVE_OPEN */ + +#if UIP_UDP + for(c = 0; c < UIP_UDP_CONNS; ++c) { + ustack->uip_udp_conns[c].lport = 0; + } +#endif /* UIP_UDP */ + + + /* IPv4 initialization. */ +#if UIP_FIXEDADDR == 0 + /* uip_hostaddr[0] = uip_hostaddr[1] = 0;*/ +#endif /* UIP_FIXEDADDR */ + + /* zero out the uIP statistics */ + memset(&ustack->stats, 0, sizeof(ustack->stats)); + + /* prepare the uIP lock */ + pthread_mutex_init(&ustack->lock, NULL); + + if(ipv6_enabled) + ustack->enable_IPv6 = UIP_SUPPORT_IPv6_ENABLED; + else + ustack->enable_IPv6 = UIP_SUPPORT_IPv6_DISABLED; +} +/*---------------------------------------------------------------------------*/ +#if UIP_ACTIVE_OPEN +struct uip_conn * +uip_connect(struct uip_stack *ustack, uip_ipaddr_t *ripaddr, u16_t rport) +{ + u8_t c; + register struct uip_conn *conn, *cconn; + + /* Find an unused local port. */ + again: + ++ustack->lastport; + + if(ustack->lastport >= 32000) { + ustack->lastport = 4096; + } + + /* Check if this port is already in use, and if so try to find + another one. */ + for(c = 0; c < UIP_CONNS; ++c) { + conn = &ustack->uip_conns[c]; + if(conn->tcpstateflags != UIP_CLOSED && + conn->lport == htons(ustack->lastport)) { + goto again; + } + } + + conn = 0; + for(c = 0; c < UIP_CONNS; ++c) { + cconn = &ustack->uip_conns[c]; + if(cconn->tcpstateflags == UIP_CLOSED) { + conn = cconn; + break; + } + if(cconn->tcpstateflags == UIP_TIME_WAIT) { + if(conn == 0 || + cconn->timer > conn->timer) { + conn = cconn; + } + } + } + + if(conn == 0) { + return 0; + } + + conn->tcpstateflags = UIP_SYN_SENT; + + conn->snd_nxt[0] = ustack->iss[0]; + conn->snd_nxt[1] = ustack->iss[1]; + conn->snd_nxt[2] = ustack->iss[2]; + conn->snd_nxt[3] = ustack->iss[3]; + + conn->initialmss = conn->mss = UIP_TCP_MSS; + + conn->len = 1; /* TCP length of the SYN is one. */ + conn->nrtx = 0; + conn->timer = 1; /* Send the SYN next time around. */ + conn->rto = UIP_RTO; + conn->sa = 0; + conn->sv = 16; /* Initial value of the RTT variance. */ + conn->lport = htons(ustack->lastport); + conn->rport = rport; + uip_ipaddr_copy(&conn->ripaddr, ripaddr); + + return conn; +} +#endif /* UIP_ACTIVE_OPEN */ +/*---------------------------------------------------------------------------*/ +#if UIP_UDP +struct uip_udp_conn * +uip_udp_new(struct uip_stack *ustack, uip_ipaddr_t *ripaddr, u16_t rport) +{ + u8_t c; + register struct uip_udp_conn *conn; + + /* Find an unused local port. */ + again: + ++ustack->lastport; + + if(ustack->lastport >= 32000) { + ustack->lastport = 4096; + } + + for(c = 0; c < UIP_UDP_CONNS; ++c) { + if(ustack->uip_udp_conns[c].lport == htons(ustack->lastport)) { + goto again; + } + } + + + conn = 0; + for(c = 0; c < UIP_UDP_CONNS; ++c) { + if(ustack->uip_udp_conns[c].lport == 0) { + conn = &ustack->uip_udp_conns[c]; + break; + } + } + + if(conn == 0) { + return 0; + } + + conn->lport = htons(ustack->lastport); + conn->rport = rport; + if(ripaddr == NULL) { + memset(conn->ripaddr, 0, sizeof(uip_ipaddr_t)); + } else { + uip_ipaddr_copy(&conn->ripaddr, ripaddr); + } + conn->ttl = UIP_TTL; + + return conn; +} +#endif /* UIP_UDP */ +/*---------------------------------------------------------------------------*/ +void +uip_unlisten(struct uip_stack *ustack, u16_t port) +{ + u8_t c; + + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(ustack->uip_listenports[c] == port) { + ustack->uip_listenports[c] = 0; + return; + } + } +} +/*---------------------------------------------------------------------------*/ +void +uip_listen(struct uip_stack *ustack, u16_t port) +{ + u8_t c; + + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(ustack->uip_listenports[c] == 0) { + ustack->uip_listenports[c] = port; + return; + } + } +} + + +/** + * Is new incoming data available? + * + * Will reduce to non-zero if there is new data for the application + * present at the uip_appdata pointer. The size of the data is + * avaliable through the uip_len variable. + * + * \hideinitializer + */ +int uip_newdata(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_NEWDATA; +} + +/** + * Has previously sent data been acknowledged? + * + * Will reduce to non-zero if the previously sent data has been + * acknowledged by the remote host. This means that the application + * can send new data. + * + * \hideinitializer + */ +#define uip_acked() (uip_flags & UIP_ACKDATA) + +/** + * Has the connection just been connected? + * + * Reduces to non-zero if the current connection has been connected to + * a remote host. This will happen both if the connection has been + * actively opened (with uip_connect()) or passively opened (with + * uip_listen()). + * + * \hideinitializer + */ +int uip_connected(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_CONNECTED; +} + +/** + * Has the connection been closed by the other end? + * + * Is non-zero if the connection has been closed by the remote + * host. The application may then do the necessary clean-ups. + * + * \hideinitializer + */ +int uip_closed(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_CLOSE; +} + +/** + * Has the connection been aborted by the other end? + * + * Non-zero if the current connection has been aborted (reset) by the + * remote host. + * + * \hideinitializer + */ +int uip_aborted(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_ABORT; +} + +/** + * Has the connection timed out? + * + * Non-zero if the current connection has been aborted due to too many + * retransmissions. + * + * \hideinitializer + */ +int uip_timedout(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_TIMEDOUT; +} + + +/** + * Do we need to retransmit previously data? + * + * Reduces to non-zero if the previously sent data has been lost in + * the network, and the application should retransmit it. The + * application should send the exact same data as it did the last + * time, using the uip_send() function. + * + * \hideinitializer + */ +int uip_rexmit(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_REXMIT; +} + +/** + * Is the connection being polled by uIP? + * + * Is non-zero if the reason the application is invoked is that the + * current connection has been idle for a while and should be + * polled. + * + * The polling event can be used for sending data without having to + * wait for the remote host to send data. + * + * \hideinitializer + */ +int uip_poll(struct uip_stack *ustack) +{ + return ustack->uip_flags & UIP_POLL; +} + +int uip_initialmss(struct uip_stack *ustack) +{ + return ustack->uip_conn->initialmss; +} + +int uip_mss(struct uip_stack *ustack) +{ + return ustack->uip_conn->mss; +} + +/*---------------------------------------------------------------------------*/ +/* XXX: IP fragment reassembly: not well-tested. */ + +#if UIP_REASSEMBLY && !UIP_CONF_IPV6 +#define UIP_REASS_BUFSIZE (UIP_BUFSIZE - UIP_LLH_LEN) +static u8_t uip_reassbuf[UIP_REASS_BUFSIZE]; +static u8_t uip_reassbitmap[UIP_REASS_BUFSIZE / (8 * 8)]; +static const u8_t bitmap_bits[8] = {0xff, 0x7f, 0x3f, 0x1f, + 0x0f, 0x07, 0x03, 0x01}; +static u16_t uip_reasslen; +static u8_t uip_reassflags; +#define UIP_REASS_FLAG_LASTFRAG 0x01 +static u8_t uip_reasstmr; + +#define IP_MF 0x20 + +static u8_t +uip_reass(void) +{ + u16_t offset, len; + u16_t i; + + /* If ip_reasstmr is zero, no packet is present in the buffer, so we + write the IP header of the fragment into the reassembly + buffer. The timer is updated with the maximum age. */ + if(uip_reasstmr == 0) { + memcpy(uip_reassbuf, &BUF(ustack)->vhl, UIP_IPH_LEN); + uip_reasstmr = UIP_REASS_MAXAGE; + uip_reassflags = 0; + /* Clear the bitmap. */ + memset(uip_reassbitmap, 0, sizeof(uip_reassbitmap)); + } + + /* Check if the incoming fragment matches the one currently present + in the reasembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if(BUF(ustack)->srcipaddr[0] == FBUF(ustack)->srcipaddr[0] && + BUF(ustack)->srcipaddr[1] == FBUF(ustack)->srcipaddr[1] && + BUF(ustack)->destipaddr[0] == FBUF(ustack)->destipaddr[0] && + BUF(ustack)->destipaddr[1] == FBUF(ustack)->destipaddr[1] && + BUF(ustack)->ipid[0] == FBUF(ustack)->ipid[0] && + BUF(ustack)->ipid[1] == FBUF(ustack)->ipid[1]) { + + len = (BUF(ustack)->len[0] << 8) + BUF(ustack)->len[1] - (BUF(ustack)->vhl & 0x0f) * 4; + offset = (((BUF(ustack)->ipoffset[0] & 0x3f) << 8) + BUF(ustack)->ipoffset[1]) * 8; + + /* If the offset or the offset + fragment length overflows the + reassembly buffer, we discard the entire packet. */ + if(offset > UIP_REASS_BUFSIZE || + offset + len > UIP_REASS_BUFSIZE) { + uip_reasstmr = 0; + goto nullreturn; + } + + /* Copy the fragment into the reassembly buffer, at the right + offset. */ + memcpy(&uip_reassbuf[UIP_IPH_LEN + offset], + (char *)BUF + (int)((BUF(ustack)->vhl & 0x0f) * 4), + len); + + /* Update the bitmap. */ + if(offset / (8 * 8) == (offset + len) / (8 * 8)) { + /* If the two endpoints are in the same byte, we only update + that byte. */ + + uip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8 ) & 7] & + ~bitmap_bits[((offset + len) / 8 ) & 7]; + } else { + /* If the two endpoints are in different bytes, we update the + bytes in the endpoints and fill the stuff inbetween with + 0xff. */ + uip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8 ) & 7]; + for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) { + uip_reassbitmap[i] = 0xff; + } + uip_reassbitmap[(offset + len) / (8 * 8)] |= + ~bitmap_bits[((offset + len) / 8 ) & 7]; + } + + /* If this fragment has the More Fragments flag set to zero, we + know that this is the last fragment, so we can calculate the + size of the entire packet. We also set the + IP_REASS_FLAG_LASTFRAG flag to indicate that we have received + the final fragment. */ + + if((BUF(ustack)->ipoffset[0] & IP_MF) == 0) { + uip_reassflags |= UIP_REASS_FLAG_LASTFRAG; + uip_reasslen = offset + len; + } + + /* Finally, we check if we have a full packet in the buffer. We do + this by checking if we have the last fragment and if all bits + in the bitmap are set. */ + if(uip_reassflags & UIP_REASS_FLAG_LASTFRAG) { + /* Check all bytes up to and including all but the last byte in + the bitmap. */ + for(i = 0; i < uip_reasslen / (8 * 8) - 1; ++i) { + if(uip_reassbitmap[i] != 0xff) { + goto nullreturn; + } + } + /* Check the last byte in the bitmap. It should contain just the + right amount of bits. */ + if(uip_reassbitmap[uip_reasslen / (8 * 8)] != + (u8_t)~bitmap_bits[uip_reasslen / 8 & 7]) { + goto nullreturn; + } + + /* If we have come this far, we have a full packet in the + buffer, so we allocate a pbuf and copy the packet into it. We + also reset the timer. */ + uip_reasstmr = 0; + memcpy(BUF, FBUF, uip_reasslen); + + /* Pretend to be a "normal" (i.e., not fragmented) IP packet + from now on. */ + BUF(ustack)->ipoffset[0] = BUF(ustack)->ipoffset[1] = 0; + BUF(ustack)->len[0] = uip_reasslen >> 8; + BUF(ustack)->len[1] = uip_reasslen & 0xff; + BUF(ustack)->ipchksum = 0; + BUF(ustack)->ipchksum = ~(uip_ipchksum()); + + return uip_reasslen; + } + } + + nullreturn: + return 0; +} +#endif /* UIP_REASSEMBLY */ +/*---------------------------------------------------------------------------*/ +static void +uip_add_rcv_nxt(struct uip_stack *ustack, u16_t n) +{ + u8_t uip_acc32[4]; + + uip_add32(ustack->uip_conn->rcv_nxt, n, uip_acc32); + ustack->uip_conn->rcv_nxt[0] = uip_acc32[0]; + ustack->uip_conn->rcv_nxt[1] = uip_acc32[1]; + ustack->uip_conn->rcv_nxt[2] = uip_acc32[2]; + ustack->uip_conn->rcv_nxt[3] = uip_acc32[3]; +} +/*---------------------------------------------------------------------------*/ + +/** @} */ + +/** + * \defgroup uipdevfunc uIP device driver functions + * @{ + * + * These functions are used by a network device driver for interacting + * with uIP. + */ + +/** + * Process an incoming packet. + * + * This function should be called when the device driver has received + * a packet from the network. The packet from the device driver must + * be present in the uip_buf buffer, and the length of the packet + * should be placed in the uip_len variable. + * + * When the function returns, there may be an outbound packet placed + * in the uip_buf packet buffer. If so, the uip_len variable is set to + * the length of the packet. If no packet is to be sent out, the + * uip_len variable is set to 0. + * + * The usual way of calling the function is presented by the source + * code below. + \code + uip_len = devicedriver_poll(); + if(uip_len > 0) { + uip_input(); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note If you are writing a uIP device driver that needs ARP + * (Address Resolution Protocol), e.g., when running uIP over + * Ethernet, you will need to call the uIP ARP code before calling + * this function: + \code + #define BUF ((struct uip_eth_hdr *)&uip_buf[0]) + uip_len = ethernet_devicedrver_poll(); + if(uip_len > 0) { + if(BUF(ustack)->type == HTONS(UIP_ETHTYPE_IP)) { + uip_arp_ipin(); + uip_input(); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } else if(BUF(ustack)->type == HTONS(UIP_ETHTYPE_ARP)) { + uip_arp_arpin(); + if(uip_len > 0) { + ethernet_devicedriver_send(); + } + } + \endcode + * + * \hideinitializer + */ +void uip_input(struct uip_stack *ustack) +{ + uip_process(ustack, UIP_DATA); +} + +/** + * Periodic processing for a connection identified by its number. + * + * This function does the necessary periodic processing (timers, + * polling) for a uIP TCP conneciton, and should be called when the + * periodic uIP timer goes off. It should be called for every + * connection, regardless of whether they are open of closed. + * + * When the function returns, it may have an outbound packet waiting + * for service in the uIP packet buffer, and if so the uip_len + * variable is set to a value larger than zero. The device driver + * should be called to send out the packet. + * + * The ususal way of calling the function is through a for() loop like + * this: + \code + for(i = 0; i < UIP_CONNS; ++i) { + uip_periodic(i); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note If you are writing a uIP device driver that needs ARP + * (Address Resolution Protocol), e.g., when running uIP over + * Ethernet, you will need to call the uip_arp_out() function before + * calling the device driver: + \code + for(i = 0; i < UIP_CONNS; ++i) { + uip_periodic(i); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } + \endcode + * + * \param conn The number of the connection which is to be periodically polled. + * + * \hideinitializer + */ +void uip_periodic(struct uip_stack *ustack, int conn) +{ + ustack->uip_conn = &ustack->uip_conns[conn]; + uip_process(ustack, UIP_TIMER); +} + +#if 0 +/** + * Perform periodic processing for a connection identified by a pointer + * to its structure. + * + * Same as uip_periodic() but takes a pointer to the actual uip_conn + * struct instead of an integer as its argument. This function can be + * used to force periodic processing of a specific connection. + * + * \param conn A pointer to the uip_conn struct for the connection to + * be processed. + * + * \hideinitializer + */ +void uip_periodic_conn(struct uip_stack *ustack, conn) +{ + ustack->uip_conn = conn; + uip_process(ustack, UIP_TIMER); +} + +/** + * Reuqest that a particular connection should be polled. + * + * Similar to uip_periodic_conn() but does not perform any timer + * processing. The application is polled for new data. + * + * \param conn A pointer to the uip_conn struct for the connection to + * be processed. + * + * \hideinitializer + */ +void uip_poll_conn(struct uip_stack *ustack, conn) +{ + uip_conn = conn; + uip_process(ustack, UIP_POLL_REQUEST); +} +#endif + +#if UIP_UDP +/** + * Periodic processing for a UDP connection identified by its number. + * + * This function is essentially the same as uip_periodic(), but for + * UDP connections. It is called in a similar fashion as the + * uip_periodic() function: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note As for the uip_periodic() function, special care has to be + * taken when using uIP together with ARP and Ethernet: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } + \endcode + * + * \param conn The number of the UDP connection to be processed. + * + * \hideinitializer + */ +void uip_udp_periodic(struct uip_stack *ustack, int conn) +{ + ustack->uip_udp_conn = &ustack->uip_udp_conns[conn]; + uip_process(ustack, UIP_UDP_TIMER); +} + +#if 0 +/** + * Periodic processing for a UDP connection identified by a pointer to + * its structure. + * + * Same as uip_udp_periodic() but takes a pointer to the actual + * uip_conn struct instead of an integer as its argument. This + * function can be used to force periodic processing of a specific + * connection. + * + * \param conn A pointer to the uip_udp_conn struct for the connection + * to be processed. + * + * \hideinitializer + */ +void uip_udp_periodic_conna(struct uip_stack *ustack, int conn) do { uip_udp_conn = conn; \ + uip_process(UIP_UDP_TIMER); } while (0) + +#endif + +#endif /* UIP_UDP */ + + + +void +uip_process(struct uip_stack *ustack, u8_t flag) +{ + u8_t c; + u16_t tmp16; + register struct uip_conn *uip_connr = ustack->uip_conn; + +#if UIP_UDP + if(flag == UIP_UDP_SEND_CONN) { + goto udp_send; + } +#endif /* UIP_UDP */ + + ustack->uip_sappdata = ustack->uip_appdata = &ustack->uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN]; + + /* Check if we were invoked because of a poll request for a + particular connection. */ + if(flag == UIP_POLL_REQUEST) { + if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED && + !uip_outstanding(uip_connr)) { + ustack->uip_flags = UIP_POLL; + UIP_APPCALL(); + goto appsend; + } + goto drop; + + /* Check if we were invoked because of the perodic timer fireing. */ + } else if(flag == UIP_TIMER) { +#if UIP_REASSEMBLY + if(uip_reasstmr != 0) { + --uip_reasstmr; + } +#endif /* UIP_REASSEMBLY */ + /* Increase the initial sequence number. */ + if(++ustack->iss[3] == 0) { + if(++ustack->iss[2] == 0) { + if(++ustack->iss[1] == 0) { + ++ustack->iss[0]; + } + } + } + + /* Reset the length variables. */ + ustack->uip_len = 0; + ustack->uip_slen = 0; + + /* Check if the connection is in a state in which we simply wait + for the connection to time out. If so, we increase the + connection's timer and remove the connection if it times + out. */ + if(uip_connr->tcpstateflags == UIP_TIME_WAIT || + uip_connr->tcpstateflags == UIP_FIN_WAIT_2) { + ++(uip_connr->timer); + if(uip_connr->timer == UIP_TIME_WAIT_TIMEOUT) { + uip_connr->tcpstateflags = UIP_CLOSED; + } + } else if(uip_connr->tcpstateflags != UIP_CLOSED) { + /* If the connection has outstanding data, we increase the + connection's timer and see if it has reached the RTO value + in which case we retransmit. */ + if(uip_outstanding(uip_connr)) { + if(uip_connr->timer-- == 0) { + if(uip_connr->nrtx == UIP_MAXRTX || + ((uip_connr->tcpstateflags == UIP_SYN_SENT || + uip_connr->tcpstateflags == UIP_SYN_RCVD) && + uip_connr->nrtx == UIP_MAXSYNRTX)) { + uip_connr->tcpstateflags = UIP_CLOSED; + + /* We call UIP_APPCALL() with uip_flags set to + UIP_TIMEDOUT to inform the application that the + connection has timed out. */ + ustack->uip_flags = UIP_TIMEDOUT; + UIP_APPCALL(); + + /* We also send a reset packet to the remote host. */ + BUF(ustack)->flags = TCP_RST | TCP_ACK; + goto tcp_send_nodata; + } + + /* Exponential backoff. */ + uip_connr->timer = UIP_RTO << (uip_connr->nrtx > 4? + 4: + uip_connr->nrtx); + ++(uip_connr->nrtx); + + /* Ok, so we need to retransmit. We do this differently + depending on which state we are in. In ESTABLISHED, we + call upon the application so that it may prepare the + data for the retransmit. In SYN_RCVD, we resend the + SYNACK that we sent earlier and in LAST_ACK we have to + retransmit our FINACK. */ + ++ustack->stats.tcp.rexmit; + switch(uip_connr->tcpstateflags & UIP_TS_MASK) { + case UIP_SYN_RCVD: + /* In the SYN_RCVD state, we should retransmit our + SYNACK. */ + goto tcp_send_synack; + +#if UIP_ACTIVE_OPEN + case UIP_SYN_SENT: + /* In the SYN_SENT state, we retransmit out SYN. */ + BUF(ustack)->flags = 0; + goto tcp_send_syn; +#endif /* UIP_ACTIVE_OPEN */ + + case UIP_ESTABLISHED: + /* In the ESTABLISHED state, we call upon the application + to do the actual retransmit after which we jump into + the code for sending out the packet (the apprexmit + label). */ + ustack->uip_flags = UIP_REXMIT; + UIP_APPCALL(); + goto apprexmit; + + case UIP_FIN_WAIT_1: + case UIP_CLOSING: + case UIP_LAST_ACK: + /* In all these states we should retransmit a FINACK. */ + goto tcp_send_finack; + + } + } + } else if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) { + /* If there was no need for a retransmission, we poll the + application for new data. */ + ustack->uip_flags = UIP_POLL; + UIP_APPCALL(); + goto appsend; + } + } + goto drop; + } +#if UIP_UDP + if(flag == UIP_UDP_TIMER) { + if(ustack->uip_udp_conn->lport != 0) { + ustack->uip_conn = NULL; + ustack->uip_sappdata = ustack->uip_appdata = &ustack->uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; + ustack->uip_len = ustack->uip_slen = 0; + ustack->uip_flags = UIP_POLL; + UIP_UDP_APPCALL(); + goto udp_send; + } else { + goto drop; + } + } +#endif + + /* This is where the input processing starts. */ + ++ustack->stats.ip.recv; + + /* Start of IP input header processing code. */ + +#if UIP_CONF_IPV6 + /* Check validity of the IP header. */ + if((BUF(ustack)->vtc & 0xf0) != 0x60) { /* IP version and header length. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.vhlerr; + LOG_ERR("ipv6: invalid version."); + goto drop; + } +#else /* UIP_CONF_IPV6 */ + /* Check validity of the IP header. */ + if(BUF(ustack)->vhl != 0x45) { /* IP version and header length. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.vhlerr; + LOG_ERR("ip: invalid version or header length."); + goto drop; + } +#endif /* UIP_CONF_IPV6 */ + + /* Check the size of the packet. If the size reported to us in + uip_len is smaller the size reported in the IP header, we assume + that the packet has been corrupted in transit. If the size of + uip_len is larger than the size reported in the IP packet header, + the packet has been padded and we set uip_len to the correct + value.. */ + + if((BUF(ustack)->len[0] << 8) + BUF(ustack)->len[1] <= ustack->uip_len) { + ustack->uip_len = (BUF(ustack)->len[0] << 8) + BUF(ustack)->len[1]; +#if UIP_CONF_IPV6 + ustack->uip_len += 40; /* The length reported in the IPv6 header is the + length of the payload that follows the + header. However, uIP uses the uip_len variable + for holding the size of the entire packet, + including the IP header. For IPv4 this is not a + problem as the length field in the IPv4 header + contains the length of the entire packet. But + for IPv6 we need to add the size of the IPv6 + header (40 bytes). */ +#endif /* UIP_CONF_IPV6 */ + } else { + LOG_WARN("ip: packet shorter than reported in IP header: BUF(ustack)->len: %d ustack->uip_len:%d.", + (BUF(ustack)->len[0] << 8) + BUF(ustack)->len[1], ustack->uip_len); + goto drop; + } + +#if !UIP_CONF_IPV6 + /* Check the fragment flag. */ + if((BUF(ustack)->ipoffset[0] & 0x3f) != 0 || + BUF(ustack)->ipoffset[1] != 0) { +#if UIP_REASSEMBLY + uip_len = uip_reass(); + if(uip_len == 0) { + goto drop; + } +#else /* UIP_REASSEMBLY */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.fragerr; + LOG_WARN("ip: fragment dropped."); + goto drop; +#endif /* UIP_REASSEMBLY */ + } +#endif /* UIP_CONF_IPV6 */ + + if(uip_ipaddr_cmp(ustack->uip_hostaddr, all_zeroes_addr)) { + /* If we are configured to use ping IP address configuration and + hasn't been assigned an IP address yet, we accept all ICMP + packets. */ +#if UIP_PINGADDRCONF && !UIP_CONF_IPV6 + if(BUF(ustack)->proto == UIP_PROTO_ICMP) { + LOG_WARN("ip: possible ping config packet received."); + goto icmp_input; + } else { + LOG_WARN("ip: packet dropped since no address assigned."); + goto drop; + } +#endif /* UIP_PINGADDRCONF */ + + } else { + /* If IP broadcast support is configured, we check for a broadcast + UDP packet, which may be destined to us. */ +#if UIP_BROADCAST + DEBUG_PRINTF("UDP IP checksum 0x%04x\n", uip_ipchksum()); + if(BUF(ustack)->proto == UIP_PROTO_UDP && + uip_ipaddr_cmp(BUF(ustack)->destipaddr, all_ones_addr) + /*&& + uip_ipchksum() == 0xffff*/) { + goto udp_input; + } +#endif /* UIP_BROADCAST */ + + /* Check if the packet is destined for our IP address. */ +#if !UIP_CONF_IPV6 + if(!uip_ipaddr_cmp(BUF(ustack)->destipaddr, ustack->uip_hostaddr)) { + ++ustack->stats.ip.drop; + goto drop; + } +#else /* UIP_CONF_IPV6 */ + /* For IPv6, packet reception is a little trickier as we need to + make sure that we listen to certain multicast addresses (all + hosts multicast address, and the solicited-node multicast + address) as well. However, we will cheat here and accept all + multicast packets that are sent to the ff02::/16 addresses. */ + if(!uip_ipaddr_cmp(BUF(ustack)->destipaddr, ustack->uip_hostaddr) && + BUF(ustack)->destipaddr[0] != HTONS(0xff02)) { + ++ustack->stats.ip.drop; + goto drop; + } +#endif /* UIP_CONF_IPV6 */ + } + +#if !UIP_CONF_IPV6 + if(uip_ipchksum(ustack) != 0xffff) { /* Compute and check the IP header + checksum. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.chkerr; + LOG_ERR("ip: bad checksum."); + goto drop; + } +#endif /* UIP_CONF_IPV6 */ + + if(BUF(ustack)->proto == UIP_PROTO_TCP) { /* Check for TCP packet. If so, + proceed with TCP input + processing. */ + goto tcp_input; + } + +#if UIP_UDP + if(BUF(ustack)->proto == UIP_PROTO_UDP) { + goto udp_input; + } +#endif /* UIP_UDP */ + +#if !UIP_CONF_IPV6 + /* ICMPv4 processing code follows. */ + if(BUF(ustack)->proto != UIP_PROTO_ICMP) { /* We only allow ICMP packets from + here. */ + ++ustack->stats.ip.drop; + ++ustack->stats.ip.protoerr; + LOG_ERR("ip: neither tcp nor icmp."); + goto drop; + } + +#if UIP_PINGADDRCONF + icmp_input: +#endif /* UIP_PINGADDRCONF */ + ++ustack->stats.icmp.recv; + + /* ICMP echo (i.e., ping) processing. This is simple, we only change + the ICMP type from ECHO to ECHO_REPLY and adjust the ICMP + checksum before we return the packet. */ + if(ICMPBUF(ustack)->type != ICMP_ECHO) { + ++ustack->stats.icmp.drop; + ++ustack->stats.icmp.typeerr; + LOG_ERR("icmp: not icmp echo."); + goto drop; + } + + /* If we are configured to use ping IP address assignment, we use + the destination IP address of this ping packet and assign it to + ourself. */ +#if UIP_PINGADDRCONF + if((ustack->uip_hostaddr[0] | ustack->uip_hostaddr[1]) == 0) { + ustack->uip_hostaddr[0] = BUF(ustack)->destipaddr[0]; + ustack->uip_hostaddr[1] = BUF(ustack)->destipaddr[1]; + } +#endif /* UIP_PINGADDRCONF */ + + ICMPBUF(ustack)->type = ICMP_ECHO_REPLY; + + if(ICMPBUF(ustack)->icmpchksum >= htons(0xffff - (ICMP_ECHO << 8))) { + ICMPBUF(ustack)->icmpchksum += htons(ICMP_ECHO << 8) + 1; + } else { + ICMPBUF(ustack)->icmpchksum += htons(ICMP_ECHO << 8); + } + + /* Swap IP addresses. */ + uip_ipaddr_copy(BUF(ustack)->destipaddr, BUF(ustack)->srcipaddr); + uip_ipaddr_copy(BUF(ustack)->srcipaddr, ustack->uip_hostaddr); + + ++ustack->stats.icmp.sent; + goto send; + + /* End of IPv4 input header processing code. */ +#else /* !UIP_CONF_IPV6 */ + + /* This is IPv6 ICMPv6 processing code. */ + DEBUG_PRINTF("icmp6_input: length %d\n", uip_len); + + if(BUF(ustack)->proto != UIP_PROTO_ICMP6) { /* We only allow ICMPv6 packets from + here. */ + ++uip_stat.ip.drop; + ++uip_stat.ip.protoerr; + LOG_ERR("ip: neither tcp nor icmp6."); + goto drop; + } + + ++uip_stat.icmp.recv; + + /* If we get a neighbor solicitation for our address we should send + a neighbor advertisement message back. */ + if(ICMPBUF(ustack)->type == ICMP6_NEIGHBOR_SOLICITATION) { + if(uip_ipaddr_cmp(ICMPBUF(ustack)->icmp6data, ustack->uip_hostaddr)) { + + if(ICMPBUF(ustack)->options[0] == ICMP6_OPTION_SOURCE_LINK_ADDRESS) { + /* Save the sender's address in our neighbor list. */ + +/* FIXME !!!! */ +// uip_neighbor_add(ICMPBUF(ustack)->srcipaddr, &(ICMPBUF(ustack)->options[2])); + } + + /* We should now send a neighbor advertisement back to where the + neighbor solicication came from. */ + ICMPBUF(ustack)->type = ICMP6_NEIGHBOR_ADVERTISEMENT; + ICMPBUF(ustack)->flags = ICMP6_FLAG_S; /* Solicited flag. */ + + ICMPBUF(ustack)->reserved1 = ICMPBUF(ustack)->reserved2 = ICMPBUF(ustack)->reserved3 = 0; + + uip_ipaddr_copy(ICMPBUF(ustack)->destipaddr, ICMPBUF(ustack)->srcipaddr); + uip_ipaddr_copy(ICMPBUF(ustack)->srcipaddr, ustack->uip_hostaddr); + ICMPBUF(ustack)->options[0] = ICMP6_OPTION_TARGET_LINK_ADDRESS; + ICMPBUF(ustack)->options[1] = 1; /* Options length, 1 = 8 bytes. */ + memcpy(&(ICMPBUF(ustack)->options[2]), &uip_ethaddr, sizeof(uip_ethaddr)); + ICMPBUF(ustack)->icmpchksum = 0; + ICMPBUF(ustack)->icmpchksum = ~uip_icmp6chksum(); + goto send; + + } + goto drop; + } else if(ICMPBUF(ustack)->type == ICMP6_ECHO) { + /* ICMP echo (i.e., ping) processing. This is simple, we only + change the ICMP type from ECHO to ECHO_REPLY and update the + ICMP checksum before we return the packet. */ + + ICMPBUF(ustack)->type = ICMP6_ECHO_REPLY; + + uip_ipaddr_copy(BUF(ustack)->destipaddr, BUF(ustack)->srcipaddr); + uip_ipaddr_copy(BUF(ustack)->srcipaddr, ustack->uip_hostaddr); + ICMPBUF(ustack)->icmpchksum = 0; + ICMPBUF(ustack)->icmpchksum = ~uip_icmp6chksum(); + + ++ustack->stats.icmp.sent; + goto send; + } else { + DEBUG_PRINTF("Unknown icmp6 message type %d\n", ICMPBUF(ustack)->type); + ++ustack->stats.icmp.drop; + ++ustack->stats.icmp.typeerr; + LOG_ERR("icmp: unknown ICMP message."); + goto drop; + } + + /* End of IPv6 ICMP processing. */ + +#endif /* !UIP_CONF_IPV6 */ + +#if UIP_UDP + /* UDP input processing. */ + udp_input: + /* UDP processing is really just a hack. We don't do anything to the + UDP/IP headers, but let the UDP application do all the hard + work. If the application sets uip_slen, it has a packet to + send. */ +#if UIP_UDP_CHECKSUMS + ustack->uip_len = ustack->uip_len - UIP_IPUDPH_LEN; + ustack->uip_appdata = &ustack->uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; + if(UDPBUF(ustack)->udpchksum != 0 && uip_udpchksum(ustack) != 0xffff) { + ++ustack->stats.udp.drop; + ++ustack->stats.udp.chkerr; + LOG_ERR("udp: bad checksum."); + goto drop; + } +#else /* UIP_UDP_CHECKSUMS */ + uip_len = uip_len - UIP_IPUDPH_LEN; +#endif /* UIP_UDP_CHECKSUMS */ + + /* Demultiplex this UDP packet between the UDP "connections". */ + for(ustack->uip_udp_conn = &ustack->uip_udp_conns[0]; + ustack->uip_udp_conn < &ustack->uip_udp_conns[UIP_UDP_CONNS]; + ++ustack->uip_udp_conn) { + /* If the local UDP port is non-zero, the connection is considered + to be used. If so, the local port number is checked against the + destination port number in the received packet. If the two port + numbers match, the remote port number is checked if the + connection is bound to a remote port. Finally, if the + connection is bound to a remote IP address, the source IP + address of the packet is checked. */ + +/* BWL */ + LOG_INFO("Looking for UDP source:%d->%d dest:%d->%d", UDPBUF(ustack)->destport, ustack->uip_udp_conn->lport, UDPBUF(ustack)->srcport, ustack->uip_udp_conn->rport); + + if(ustack->uip_udp_conn->lport != 0 && + UDPBUF(ustack)->destport == ustack->uip_udp_conn->lport && + (ustack->uip_udp_conn->rport == 0 || + UDPBUF(ustack)->srcport == ustack->uip_udp_conn->rport) && + (uip_ipaddr_cmp(ustack->uip_udp_conn->ripaddr, all_zeroes_addr) || + uip_ipaddr_cmp(ustack->uip_udp_conn->ripaddr, all_ones_addr) || + uip_ipaddr_cmp(BUF(ustack)->srcipaddr, ustack->uip_udp_conn->ripaddr))) { + goto udp_found; + } + } + LOG_ERR("udp: no matching connection found"); + goto drop; + + udp_found: + ustack->uip_conn = NULL; + ustack->uip_flags = UIP_NEWDATA; + ustack->uip_sappdata = ustack->uip_appdata = &ustack->uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; + ustack->uip_slen = 0; + UIP_UDP_APPCALL(); + udp_send: + if(ustack->uip_slen == 0) { + goto drop; + } + + ustack->uip_len = ustack->uip_slen + UIP_IPUDPH_LEN; +#if UIP_CONF_IPV6 + /* For IPv6, the IP length field does not include the IPv6 IP header + length. */ + BUF(ustack)->len[0] = ((ustack->uip_len - UIP_IPH_LEN) >> 8); + BUF(ustack)->len[1] = ((ustack->uip_len - UIP_IPH_LEN) & 0xff); +#else /* UIP_CONF_IPV6 */ + BUF(ustack)->len[0] = (ustack->uip_len >> 8); + BUF(ustack)->len[1] = (ustack->uip_len & 0xff); +#endif /* UIP_CONF_IPV6 */ + + + BUF(ustack)->ttl = ustack->uip_udp_conn->ttl; + BUF(ustack)->proto = UIP_PROTO_UDP; + + UDPBUF(ustack)->udplen = htons(ustack->uip_slen + UIP_UDPH_LEN); +LOG_INFO("Sending udp lenth: %d (raw: %d)\n", ntohs(UDPBUF(ustack)->udplen), UDPBUF(ustack)->udplen); + + UDPBUF(ustack)->udpchksum = 0; + + BUF(ustack)->srcport = ustack->uip_udp_conn->lport; + BUF(ustack)->destport = ustack->uip_udp_conn->rport; + +LOG_INFO("Sending src port: %d (raw: %d)\n", ntohs(BUF(ustack)->srcport), BUF(ustack)->srcport); +LOG_INFO("Sending dest port: %d (raw: %d)\n", ntohs(BUF(ustack)->destport), BUF(ustack)->destport); + + uip_ipaddr_copy(BUF(ustack)->srcipaddr, ustack->uip_hostaddr); + uip_ipaddr_copy(BUF(ustack)->destipaddr, ustack->uip_udp_conn->ripaddr); + + ustack->uip_appdata = &ustack->uip_buf[UIP_LLH_LEN + UIP_IPTCPH_LEN]; + +#if UIP_UDP_CHECKSUMS + /* Calculate UDP checksum. */ + UDPBUF(ustack)->udpchksum = ~(uip_udpchksum(ustack)); + if(UDPBUF(ustack)->udpchksum == 0) { + UDPBUF(ustack)->udpchksum = 0xffff; + } +#endif /* UIP_UDP_CHECKSUMS */ + + goto ip_send_nolen; +#endif /* UIP_UDP */ + + /* TCP input processing. */ + tcp_input: + ++ustack->stats.tcp.recv; + + /* Start of TCP input header processing code. */ + + if(uip_tcpchksum(ustack) != 0xffff) { /* Compute and check the TCP + checksum. */ + ++ustack->stats.tcp.drop; + ++ustack->stats.tcp.chkerr; + LOG_ERR("tcp: bad checksum."); + goto drop; + } + + + /* Demultiplex this segment. */ + /* First check any active connections. */ + for(uip_connr = &ustack->uip_conns[0]; + uip_connr <= &ustack->uip_conns[UIP_CONNS - 1]; + ++uip_connr) { + if(uip_connr->tcpstateflags != UIP_CLOSED && + BUF(ustack)->destport == uip_connr->lport && + BUF(ustack)->srcport == uip_connr->rport && + uip_ipaddr_cmp(BUF(ustack)->srcipaddr, uip_connr->ripaddr)) { + goto found; + } + } + + /* If we didn't find and active connection that expected the packet, + either this packet is an old duplicate, or this is a SYN packet + destined for a connection in LISTEN. If the SYN flag isn't set, + it is an old packet and we send a RST. */ + if((BUF(ustack)->flags & TCP_CTL) != TCP_SYN) { + goto reset; + } + + tmp16 = BUF(ustack)->destport; + /* Next, check listening connections. */ + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(tmp16 == ustack->uip_listenports[c]) + goto found_listen; + } + + /* No matching connection found, so we send a RST packet. */ + ++ustack->stats.tcp.synrst; + reset: + + /* We do not send resets in response to resets. */ + if(BUF(ustack)->flags & TCP_RST) { + goto drop; + } + + ++ustack->stats.tcp.rst; + + BUF(ustack)->flags = TCP_RST | TCP_ACK; + ustack->uip_len = UIP_IPTCPH_LEN; + BUF(ustack)->tcpoffset = 5 << 4; + + /* Flip the seqno and ackno fields in the TCP header. */ + c = BUF(ustack)->seqno[3]; + BUF(ustack)->seqno[3] = BUF(ustack)->ackno[3]; + BUF(ustack)->ackno[3] = c; + + c = BUF(ustack)->seqno[2]; + BUF(ustack)->seqno[2] = BUF(ustack)->ackno[2]; + BUF(ustack)->ackno[2] = c; + + c = BUF(ustack)->seqno[1]; + BUF(ustack)->seqno[1] = BUF(ustack)->ackno[1]; + BUF(ustack)->ackno[1] = c; + + c = BUF(ustack)->seqno[0]; + BUF(ustack)->seqno[0] = BUF(ustack)->ackno[0]; + BUF(ustack)->ackno[0] = c; + + /* We also have to increase the sequence number we are + acknowledging. If the least significant byte overflowed, we need + to propagate the carry to the other bytes as well. */ + if(++BUF(ustack)->ackno[3] == 0) { + if(++BUF(ustack)->ackno[2] == 0) { + if(++BUF(ustack)->ackno[1] == 0) { + ++BUF(ustack)->ackno[0]; + } + } + } + + /* Swap port numbers. */ + tmp16 = BUF(ustack)->srcport; + BUF(ustack)->srcport = BUF(ustack)->destport; + BUF(ustack)->destport = tmp16; + + /* Swap IP addresses. */ + uip_ipaddr_copy(BUF(ustack)->destipaddr, BUF(ustack)->srcipaddr); + uip_ipaddr_copy(BUF(ustack)->srcipaddr, ustack->uip_hostaddr); + + /* And send out the RST packet! */ + goto tcp_send_noconn; + + /* This label will be jumped to if we matched the incoming packet + with a connection in LISTEN. In that case, we should create a new + connection and send a SYNACK in return. */ + found_listen: + /* First we check if there are any connections avaliable. Unused + connections are kept in the same table as used connections, but + unused ones have the tcpstate set to CLOSED. Also, connections in + TIME_WAIT are kept track of and we'll use the oldest one if no + CLOSED connections are found. Thanks to Eddie C. Dost for a very + nice algorithm for the TIME_WAIT search. */ + uip_connr = 0; + for(c = 0; c < UIP_CONNS; ++c) { + if(ustack->uip_conns[c].tcpstateflags == UIP_CLOSED) { + uip_connr = &ustack->uip_conns[c]; + break; + } + if(ustack->uip_conns[c].tcpstateflags == UIP_TIME_WAIT) { + if(uip_connr == 0 || + ustack->uip_conns[c].timer > uip_connr->timer) { + uip_connr = &ustack->uip_conns[c]; + } + } + } + + if(uip_connr == 0) { + /* All connections are used already, we drop packet and hope that + the remote end will retransmit the packet at a time when we + have more spare connections. */ + ++ustack->stats.tcp.syndrop; + LOG_WARN("tcp: found no unused connections."); + goto drop; + } + ustack->uip_conn = uip_connr; + + /* Fill in the necessary fields for the new connection. */ + uip_connr->rto = uip_connr->timer = UIP_RTO; + uip_connr->sa = 0; + uip_connr->sv = 4; + uip_connr->nrtx = 0; + uip_connr->lport = BUF(ustack)->destport; + uip_connr->rport = BUF(ustack)->srcport; + uip_ipaddr_copy(uip_connr->ripaddr, BUF(ustack)->srcipaddr); + uip_connr->tcpstateflags = UIP_SYN_RCVD; + + uip_connr->snd_nxt[0] = ustack->iss[0]; + uip_connr->snd_nxt[1] = ustack->iss[1]; + uip_connr->snd_nxt[2] = ustack->iss[2]; + uip_connr->snd_nxt[3] = ustack->iss[3]; + uip_connr->len = 1; + + /* rcv_nxt should be the seqno from the incoming packet + 1. */ + uip_connr->rcv_nxt[3] = BUF(ustack)->seqno[3]; + uip_connr->rcv_nxt[2] = BUF(ustack)->seqno[2]; + uip_connr->rcv_nxt[1] = BUF(ustack)->seqno[1]; + uip_connr->rcv_nxt[0] = BUF(ustack)->seqno[0]; + uip_add_rcv_nxt(ustack, 1); + + /* Parse the TCP MSS option, if present. */ + if((BUF(ustack)->tcpoffset & 0xf0) > 0x50) { + for(c = 0; c < ((BUF(ustack)->tcpoffset >> 4) - 5) << 2 ;) { + ustack->opt = ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + c]; + if(ustack->opt == TCP_OPT_END) { + /* End of options. */ + break; + } else if(ustack->opt == TCP_OPT_NOOP) { + ++c; + /* NOP option. */ + } else if(ustack->opt == TCP_OPT_MSS && + ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { + /* An MSS option with the right option length. */ + tmp16 = ((u16_t)ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) | + (u16_t)ustack->uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + 3 + c]; + uip_connr->initialmss = uip_connr->mss = + tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; + + /* And we are done processing options. */ + break; + } else { + /* All other options have a length field, so that we easily + can skip past them. */ + if(ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + c += ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c]; + } + } + } + + /* Our response will be a SYNACK. */ +#if UIP_ACTIVE_OPEN + tcp_send_synack: + BUF(ustack)->flags = TCP_ACK; + + tcp_send_syn: + BUF(ustack)->flags |= TCP_SYN; +#else /* UIP_ACTIVE_OPEN */ + tcp_send_synack: + BUF(ustack)->flags = TCP_SYN | TCP_ACK; +#endif /* UIP_ACTIVE_OPEN */ + + /* We send out the TCP Maximum Segment Size option with our + SYNACK. */ + BUF(ustack)->optdata[0] = TCP_OPT_MSS; + BUF(ustack)->optdata[1] = TCP_OPT_MSS_LEN; + BUF(ustack)->optdata[2] = (UIP_TCP_MSS) / 256; + BUF(ustack)->optdata[3] = (UIP_TCP_MSS) & 255; + ustack->uip_len = UIP_IPTCPH_LEN + TCP_OPT_MSS_LEN; + BUF(ustack)->tcpoffset = ((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4) << 4; + goto tcp_send; + + /* This label will be jumped to if we found an active connection. */ + found: + ustack->uip_conn = uip_connr; + ustack->uip_flags = 0; + /* We do a very naive form of TCP reset processing; we just accept + any RST and kill our connection. We should in fact check if the + sequence number of this reset is wihtin our advertised window + before we accept the reset. */ + if(BUF(ustack)->flags & TCP_RST) { + uip_connr->tcpstateflags = UIP_CLOSED; + LOG_ERR("tcp: got reset, aborting connection."); + ustack->uip_flags = UIP_ABORT; + UIP_APPCALL(); + goto drop; + } + /* Calculated the length of the data, if the application has sent + any data to us. */ + c = (BUF(ustack)->tcpoffset >> 4) << 2; + /* uip_len will contain the length of the actual TCP data. This is + calculated by subtracing the length of the TCP header (in + c) and the length of the IP header (20 bytes). */ + ustack->uip_len = ustack->uip_len - c - UIP_IPH_LEN; + + /* First, check if the sequence number of the incoming packet is + what we're expecting next. If not, we send out an ACK with the + correct numbers in. */ + if(!(((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) && + ((BUF(ustack)->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)))) { + if((ustack->uip_len > 0 || ((BUF(ustack)->flags & (TCP_SYN | TCP_FIN)) != 0)) && + (BUF(ustack)->seqno[0] != uip_connr->rcv_nxt[0] || + BUF(ustack)->seqno[1] != uip_connr->rcv_nxt[1] || + BUF(ustack)->seqno[2] != uip_connr->rcv_nxt[2] || + BUF(ustack)->seqno[3] != uip_connr->rcv_nxt[3])) { + goto tcp_send_ack; + } + } + + { + u8_t uip_acc32[4]; + + /* Next, check if the incoming segment acknowledges any outstanding + data. If so, we update the sequence number, reset the length of + the outstanding data, calculate RTT estimations, and reset the + retransmission timer. */ + if((BUF(ustack)->flags & TCP_ACK) && uip_outstanding(uip_connr)) { + uip_add32(uip_connr->snd_nxt, uip_connr->len, uip_acc32); + + if(BUF(ustack)->ackno[0] == uip_acc32[0] && + BUF(ustack)->ackno[1] == uip_acc32[1] && + BUF(ustack)->ackno[2] == uip_acc32[2] && + BUF(ustack)->ackno[3] == uip_acc32[3]) { + /* Update sequence number. */ + uip_connr->snd_nxt[0] = uip_acc32[0]; + uip_connr->snd_nxt[1] = uip_acc32[1]; + uip_connr->snd_nxt[2] = uip_acc32[2]; + uip_connr->snd_nxt[3] = uip_acc32[3]; + + + /* Do RTT estimation, unless we have done retransmissions. */ + if(uip_connr->nrtx == 0) { + signed char m; + m = uip_connr->rto - uip_connr->timer; + /* This is taken directly from VJs original code in his paper */ + m = m - (uip_connr->sa >> 3); + uip_connr->sa += m; + if(m < 0) { + m = -m; + } + m = m - (uip_connr->sv >> 2); + uip_connr->sv += m; + uip_connr->rto = (uip_connr->sa >> 3) + uip_connr->sv; + + } + /* Set the acknowledged flag. */ + ustack->uip_flags = UIP_ACKDATA; + /* Reset the retransmission timer. */ + uip_connr->timer = uip_connr->rto; + + /* Reset length of outstanding data. */ + uip_connr->len = 0; + } + + } + + } + + /* Do different things depending on in what state the connection is. */ + switch(uip_connr->tcpstateflags & UIP_TS_MASK) { + /* CLOSED and LISTEN are not handled here. CLOSE_WAIT is not + implemented, since we force the application to close when the + peer sends a FIN (hence the application goes directly from + ESTABLISHED to LAST_ACK). */ + case UIP_SYN_RCVD: + /* In SYN_RCVD we have sent out a SYNACK in response to a SYN, and + we are waiting for an ACK that acknowledges the data we sent + out the last time. Therefore, we want to have the UIP_ACKDATA + flag set. If so, we enter the ESTABLISHED state. */ + if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_ESTABLISHED; + ustack->uip_flags = UIP_CONNECTED; + uip_connr->len = 0; + if(ustack->uip_len > 0) { + ustack->uip_flags |= UIP_NEWDATA; + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + ustack->uip_slen = 0; + UIP_APPCALL(); + goto appsend; + } + goto drop; +#if UIP_ACTIVE_OPEN + case UIP_SYN_SENT: + /* In SYN_SENT, we wait for a SYNACK that is sent in response to + our SYN. The rcv_nxt is set to sequence number in the SYNACK + plus one, and we send an ACK. We move into the ESTABLISHED + state. */ + if((ustack->uip_flags & UIP_ACKDATA) && + (BUF(ustack)->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)) { + + /* Parse the TCP MSS option, if present. */ + if((BUF(ustack)->tcpoffset & 0xf0) > 0x50) { + for(c = 0; c < ((BUF(ustack)->tcpoffset >> 4) - 5) << 2 ;) { + ustack->opt = ustack->uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + c]; + if(ustack->opt == TCP_OPT_END) { + /* End of options. */ + break; + } else if(ustack->opt == TCP_OPT_NOOP) { + ++c; + /* NOP option. */ + } else if(ustack->opt == TCP_OPT_MSS && + ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { + /* An MSS option with the right option length. */ + tmp16 = (ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) | + ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 3 + c]; + uip_connr->initialmss = + uip_connr->mss = tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; + + /* And we are done processing options. */ + break; + } else { + /* All other options have a length field, so that we easily + can skip past them. */ + if(ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + c += ustack->uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c]; + } + } + } + uip_connr->tcpstateflags = UIP_ESTABLISHED; + uip_connr->rcv_nxt[0] = BUF(ustack)->seqno[0]; + uip_connr->rcv_nxt[1] = BUF(ustack)->seqno[1]; + uip_connr->rcv_nxt[2] = BUF(ustack)->seqno[2]; + uip_connr->rcv_nxt[3] = BUF(ustack)->seqno[3]; + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CONNECTED | UIP_NEWDATA; + uip_connr->len = 0; + ustack->uip_len = 0; + ustack->uip_slen = 0; + UIP_APPCALL(); + goto appsend; + } + /* Inform the application that the connection failed */ + ustack->uip_flags = UIP_ABORT; + UIP_APPCALL(); + /* The connection is closed after we send the RST */ + ustack->uip_conn->tcpstateflags = UIP_CLOSED; + goto reset; +#endif /* UIP_ACTIVE_OPEN */ + + case UIP_ESTABLISHED: + /* In the ESTABLISHED state, we call upon the application to feed + data into the uip_buf. If the UIP_ACKDATA flag is set, the + application should put new data into the buffer, otherwise we are + retransmitting an old segment, and the application should put that + data into the buffer. + + If the incoming packet is a FIN, we should close the connection on + this side as well, and we send out a FIN and enter the LAST_ACK + state. We require that there is no outstanding data; otherwise the + sequence numbers will be screwed up. */ + + if(BUF(ustack)->flags & TCP_FIN && !(uip_connr->tcpstateflags & UIP_STOPPED)) { + if(uip_outstanding(uip_connr)) { + goto drop; + } + uip_add_rcv_nxt(ustack, 1 + ustack->uip_len); + ustack->uip_flags |= UIP_CLOSE; + if(ustack->uip_len > 0) { + ustack->uip_flags |= UIP_NEWDATA; + } + UIP_APPCALL(); + uip_connr->len = 1; + uip_connr->tcpstateflags = UIP_LAST_ACK; + uip_connr->nrtx = 0; + tcp_send_finack: + BUF(ustack)->flags = TCP_FIN | TCP_ACK; + goto tcp_send_nodata; + } + + /* Check the URG flag. If this is set, the segment carries urgent + data that we must pass to the application. */ + if((BUF(ustack)->flags & TCP_URG) != 0) { +#if UIP_URGDATA > 0 + uip_urglen = (BUF(ustack)->urgp[0] << 8) | BUF(ustack)->urgp[1]; + if(uip_urglen > uip_len) { + /* There is more urgent data in the next segment to come. */ + uip_urglen = uip_len; + } + uip_add_rcv_nxt(uip_urglen); + uip_len -= uip_urglen; + uip_urgdata = uip_appdata; + uip_appdata += uip_urglen; + } else { + uip_urglen = 0; +#else /* UIP_URGDATA > 0 */ + ustack->uip_appdata = ((char *)ustack->uip_appdata) + ((BUF(ustack)->urgp[0] << 8) | BUF(ustack)->urgp[1]); + ustack->uip_len -= (BUF(ustack)->urgp[0] << 8) | BUF(ustack)->urgp[1]; +#endif /* UIP_URGDATA > 0 */ + } + + /* If uip_len > 0 we have TCP data in the packet, and we flag this + by setting the UIP_NEWDATA flag and update the sequence number + we acknowledge. If the application has stopped the dataflow + using uip_stop(), we must not accept any data packets from the + remote host. */ + if(ustack->uip_len > 0 && !(uip_connr->tcpstateflags & UIP_STOPPED)) { + ustack->uip_flags |= UIP_NEWDATA; + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + + /* Check if the available buffer space advertised by the other end + is smaller than the initial MSS for this connection. If so, we + set the current MSS to the window size to ensure that the + application does not send more data than the other end can + handle. + + If the remote host advertises a zero window, we set the MSS to + the initial MSS so that the application will send an entire MSS + of data. This data will not be acknowledged by the receiver, + and the application will retransmit it. This is called the + "persistent timer" and uses the retransmission mechanim. + */ + tmp16 = ((u16_t)BUF(ustack)->wnd[0] << 8) + (u16_t)BUF(ustack)->wnd[1]; + if(tmp16 > uip_connr->initialmss || + tmp16 == 0) { + tmp16 = uip_connr->initialmss; + } + uip_connr->mss = tmp16; + + /* If this packet constitutes an ACK for outstanding data (flagged + by the UIP_ACKDATA flag, we should call the application since it + might want to send more data. If the incoming packet had data + from the peer (as flagged by the UIP_NEWDATA flag), the + application must also be notified. + + When the application is called, the global variable uip_len + contains the length of the incoming data. The application can + access the incoming data through the global pointer + uip_appdata, which usually points UIP_IPTCPH_LEN + UIP_LLH_LEN + bytes into the uip_buf array. + + If the application wishes to send any data, this data should be + put into the uip_appdata and the length of the data should be + put into uip_len. If the application don't have any data to + send, uip_len must be set to 0. */ + if(ustack->uip_flags & (UIP_NEWDATA | UIP_ACKDATA)) { + ustack->uip_slen = 0; + UIP_APPCALL(); + + appsend: + + if(ustack->uip_flags & UIP_ABORT) { + ustack->uip_slen = 0; + uip_connr->tcpstateflags = UIP_CLOSED; + BUF(ustack)->flags = TCP_RST | TCP_ACK; + goto tcp_send_nodata; + } + + if(ustack->uip_flags & UIP_CLOSE) { + ustack->uip_slen = 0; + uip_connr->len = 1; + uip_connr->tcpstateflags = UIP_FIN_WAIT_1; + uip_connr->nrtx = 0; + BUF(ustack)->flags = TCP_FIN | TCP_ACK; + goto tcp_send_nodata; + } + + /* If uip_slen > 0, the application has data to be sent. */ + if(ustack->uip_slen > 0) { + + /* If the connection has acknowledged data, the contents of + the ->len variable should be discarded. */ + if((ustack->uip_flags & UIP_ACKDATA) != 0) { + uip_connr->len = 0; + } + + /* If the ->len variable is non-zero the connection has + already data in transit and cannot send anymore right + now. */ + if(uip_connr->len == 0) { + + /* The application cannot send more than what is allowed by + the mss (the minumum of the MSS and the available + window). */ + if(ustack->uip_slen > uip_connr->mss) { + ustack->uip_slen = uip_connr->mss; + } + + /* Remember how much data we send out now so that we know + when everything has been acknowledged. */ + uip_connr->len = ustack->uip_slen; + } else { + + /* If the application already had unacknowledged data, we + make sure that the application does not send (i.e., + retransmit) out more than it previously sent out. */ + ustack->uip_slen = uip_connr->len; + } + } + uip_connr->nrtx = 0; + apprexmit: + ustack->uip_appdata = ustack->uip_sappdata; + + /* If the application has data to be sent, or if the incoming + packet had new data in it, we must send out a packet. */ + if(ustack->uip_slen > 0 && uip_connr->len > 0) { + /* Add the length of the IP and TCP headers. */ + ustack->uip_len = uip_connr->len + UIP_TCPIP_HLEN; + /* We always set the ACK flag in response packets. */ + BUF(ustack)->flags = TCP_ACK | TCP_PSH; + /* Send the packet. */ + goto tcp_send_noopts; + } + /* If there is no data to send, just send out a pure ACK if + there is newdata. */ + if(ustack->uip_flags & UIP_NEWDATA) { + ustack->uip_len = UIP_TCPIP_HLEN; + BUF(ustack)->flags = TCP_ACK; + goto tcp_send_noopts; + } + } + goto drop; + case UIP_LAST_ACK: + /* We can close this connection if the peer has acknowledged our + FIN. This is indicated by the UIP_ACKDATA flag. */ + if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_CLOSED; + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(); + } + break; + + case UIP_FIN_WAIT_1: + /* The application has closed the connection, but the remote host + hasn't closed its end yet. Thus we do nothing but wait for a + FIN from the other side. */ + if(ustack->uip_len > 0) { + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + if(BUF(ustack)->flags & TCP_FIN) { + if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + uip_connr->len = 0; + } else { + uip_connr->tcpstateflags = UIP_CLOSING; + } + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(); + goto tcp_send_ack; + } else if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_FIN_WAIT_2; + uip_connr->len = 0; + goto drop; + } + if(ustack->uip_len > 0) { + goto tcp_send_ack; + } + goto drop; + + case UIP_FIN_WAIT_2: + if(ustack->uip_len > 0) { + uip_add_rcv_nxt(ustack, ustack->uip_len); + } + if(BUF(ustack)->flags & TCP_FIN) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + uip_add_rcv_nxt(ustack, 1); + ustack->uip_flags = UIP_CLOSE; + UIP_APPCALL(); + goto tcp_send_ack; + } + if(ustack->uip_len > 0) { + goto tcp_send_ack; + } + goto drop; + + case UIP_TIME_WAIT: + goto tcp_send_ack; + + case UIP_CLOSING: + if(ustack->uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + } + } + goto drop; + + + /* We jump here when we are ready to send the packet, and just want + to set the appropriate TCP sequence numbers in the TCP header. */ + tcp_send_ack: + BUF(ustack)->flags = TCP_ACK; + tcp_send_nodata: + ustack->uip_len = UIP_IPTCPH_LEN; + tcp_send_noopts: + BUF(ustack)->tcpoffset = (UIP_TCPH_LEN / 4) << 4; + tcp_send: + /* We're done with the input processing. We are now ready to send a + reply. Our job is to fill in all the fields of the TCP and IP + headers before calculating the checksum and finally send the + packet. */ + BUF(ustack)->ackno[0] = uip_connr->rcv_nxt[0]; + BUF(ustack)->ackno[1] = uip_connr->rcv_nxt[1]; + BUF(ustack)->ackno[2] = uip_connr->rcv_nxt[2]; + BUF(ustack)->ackno[3] = uip_connr->rcv_nxt[3]; + + BUF(ustack)->seqno[0] = uip_connr->snd_nxt[0]; + BUF(ustack)->seqno[1] = uip_connr->snd_nxt[1]; + BUF(ustack)->seqno[2] = uip_connr->snd_nxt[2]; + BUF(ustack)->seqno[3] = uip_connr->snd_nxt[3]; + + BUF(ustack)->proto = UIP_PROTO_TCP; + + BUF(ustack)->srcport = uip_connr->lport; + BUF(ustack)->destport = uip_connr->rport; + + uip_ipaddr_copy(BUF(ustack)->srcipaddr, ustack->uip_hostaddr); + uip_ipaddr_copy(BUF(ustack)->destipaddr, uip_connr->ripaddr); + + if(uip_connr->tcpstateflags & UIP_STOPPED) { + /* If the connection has issued uip_stop(), we advertise a zero + window so that the remote host will stop sending data. */ + BUF(ustack)->wnd[0] = BUF(ustack)->wnd[1] = 0; + } else { + BUF(ustack)->wnd[0] = ((UIP_RECEIVE_WINDOW) >> 8); + BUF(ustack)->wnd[1] = ((UIP_RECEIVE_WINDOW) & 0xff); + } + + tcp_send_noconn: + BUF(ustack)->ttl = UIP_TTL; +#if UIP_CONF_IPV6 + /* For IPv6, the IP length field does not include the IPv6 IP header + length. */ + BUF(ustack)->len[0] = ((ustack->uip_len - UIP_IPH_LEN) >> 8); + BUF(ustack)->len[1] = ((ustack->uip_len - UIP_IPH_LEN) & 0xff); +#else /* UIP_CONF_IPV6 */ + BUF(ustack)->len[0] = (ustack->uip_len >> 8); + BUF(ustack)->len[1] = (ustack->uip_len & 0xff); +#endif /* UIP_CONF_IPV6 */ + + BUF(ustack)->urgp[0] = BUF(ustack)->urgp[1] = 0; + + /* Calculate TCP checksum. */ + BUF(ustack)->tcpchksum = 0; + BUF(ustack)->tcpchksum = ~(uip_tcpchksum(ustack)); + + ip_send_nolen: + +#if UIP_CONF_IPV6 + BUF(ustack)->vtc = 0x60; + BUF(ustack)->tcflow = 0x00; + BUF(ustack)->flow = 0x00; +#else /* UIP_CONF_IPV6 */ + BUF(ustack)->vhl = 0x45; + BUF(ustack)->tos = 0; + BUF(ustack)->ipoffset[0] = BUF(ustack)->ipoffset[1] = 0; + ++ustack->ipid; + BUF(ustack)->ipid[0] = ustack->ipid >> 8; + BUF(ustack)->ipid[1] = ustack->ipid & 0xff; + /* Calculate IP checksum. */ + BUF(ustack)->ipchksum = 0; + BUF(ustack)->ipchksum = ~(uip_ipchksum(ustack)); + DEBUG_PRINTF("uip ip_send_nolen: chkecum 0x%04x\n", uip_ipchksum()); +#endif /* UIP_CONF_IPV6 */ + + ++ustack->stats.tcp.sent; + send: + DEBUG_PRINTF("Sending packet with length %d (%d)\n", uip_len, + (BUF(ustack)->len[0] << 8) | BUF(ustack)->len[1]); + + ++ustack->stats.ip.sent; + /* Return and let the caller do the actual transmission. */ + ustack->uip_flags = 0; + return; + drop: + ustack->uip_len = 0; + ustack->uip_flags = 0; + return; +} +#if 0 +/*---------------------------------------------------------------------------*/ +u16_t +htons(u16_t val) +{ + return HTONS(val); +} +#endif +/*---------------------------------------------------------------------------*/ +void +uip_send(struct uip_stack *ustack, const void *data, int len) +{ + if(len > 0) { + ustack->uip_slen = len; + if(data != ustack->uip_sappdata) { + memcpy(ustack->uip_sappdata, (data), ustack->uip_slen); + } + } +} + +u16_t +uip_datalen(struct uip_stack *ustack) +{ + return ustack->uip_len; +} +/** @} */ + +#define IPV6_LEN 16 + +int get_ipv6_link_local_address(struct uip_stack *ustack, + char *addr) +{ + memset(addr, 0, IPV6_LEN); + + addr[0] = 0xfe; + addr[1] = 0x80; + + addr[IPV6_LEN - 8] = ustack->uip_ethaddr.addr[0]; + addr[IPV6_LEN - 7] = ustack->uip_ethaddr.addr[1]; + addr[IPV6_LEN - 6] = ustack->uip_ethaddr.addr[2]; + addr[IPV6_LEN - 5] = 0xff; + addr[IPV6_LEN - 4] = 0xfe; + addr[IPV6_LEN - 3] = ustack->uip_ethaddr.addr[3]; + addr[IPV6_LEN - 2] = ustack->uip_ethaddr.addr[4]; + addr[IPV6_LEN - 1] = ustack->uip_ethaddr.addr[5]; + + return 0; +} diff --git a/brcm_iscsi_uio/src/uip/uip_ipv6.h b/brcm_iscsi_uio/src/uip/uip_ipv6.h new file mode 100644 index 0000000..06b2514 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uip_ipv6.h @@ -0,0 +1,1539 @@ + +/** + * \addtogroup uip + * @{ + */ + +/** + * \file + * Header file for the uIP TCP/IP stack. + * \author Adam Dunkels + * + * The uIP TCP/IP stack header file contains definitions for a number + * of C macros that are used by uIP programs as well as internal uIP + * structures, TCP/IP header structures and function declarations. + * + */ + + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uip.h,v 1.40 2006/06/08 07:12:07 adam Exp $ + * + */ + +#ifndef __UIP_IPv6_H__ +#define __UIP_IPv6_H__ + +#include + +#include "uipopt.h" + +#include "debug.h" + +/* Forware declaration */ +struct uip_stack; + +/** + * Repressentation of an IPv6 address. + * + */ +typedef u16_t uip_ip6addr_t[8]; + +/*---------------------------------------------------------------------------*/ +/* First, the functions that should be called from the + * system. Initialization, the periodic timer and incoming packets are + * handled by the following three functions. + */ + +/** + * \defgroup uipconffunc uIP configuration functions + * @{ + * + * The uIP configuration functions are used for setting run-time + * parameters in uIP such as IP addresses. + */ + +/** + * Set the IP address of this host. + * + * The IP address is represented as a 4-byte array where the first + * octet of the IP address is put in the first member of the 4-byte + * array. + * + * Example: + \code + + uip_ipaddr_t addr; + + uip_ipaddr(&addr, 192,168,1,2); + uip_sethostaddr(&addr); + + \endcode + * \param addr A pointer to an IP address of type uip_ipaddr_t; + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_sethostaddr(struct uip_stack *ustack, uip_ipaddr_t *addr); + +/** + * Set the default router's IP address. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the default router. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setdraddr(struct uip_stack *ustack, uip_ipaddr_t *addr); + +/** + * Set the netmask. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the netmask. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setnetmask(struct uip_stack *ustack, uip_ipaddr_t *addr); + +/** + * Set the ethernet MAC address. + * + * \param addr A pointer to a uip_ipaddr_t variable containing the IP + * address of the netmask. + * + * \sa uip_ipaddr() + * + * \hideinitializer + */ +void uip_setethernetmac(struct uip_stack *ustack, uint8_t *mac); + +/** + * Get the default router's IP address. + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the IP address of the default router. + * + * \hideinitializer + */ +#define uip_getdraddr(addr) uip_ipaddr_copy((addr), uip_draddr) + +/** + * Get the netmask. + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the value of the netmask. + * + * \hideinitializer + */ +#define uip_getnetmask(addr) uip_ipaddr_copy((addr), uip_netmask) + +/** @} */ + +/** + * \defgroup uipinit uIP initialization functions + * @{ + * + * The uIP initialization functions are used for booting uIP. + */ + +/** + * uIP initialization function. + * + * This function should be called at boot up to initilize the uIP + * TCP/IP stack. + */ +void uip_init(struct uip_stack *ustack, uint8_t enable_ipv6); + +/** + * uIP initialization function. + * + * This function may be used at boot time to set the initial ip_id. + */ +void uip_setipid(u16_t id); + +/** + * + * + */ +#define uip_conn_active(conn) (uip_conns[conn].tcpstateflags != UIP_CLOSED) + +#if UIP_UDP + +#if 0 +/** + * Periodic processing for a UDP connection identified by its number. + * + * This function is essentially the same as uip_periodic(), but for + * UDP connections. It is called in a similar fashion as the + * uip_periodic() function: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + devicedriver_send(); + } + } + \endcode + * + * \note As for the uip_periodic() function, special care has to be + * taken when using uIP together with ARP and Ethernet: + \code + for(i = 0; i < UIP_UDP_CONNS; i++) { + uip_udp_periodic(i); + if(uip_len > 0) { + uip_arp_out(); + ethernet_devicedriver_send(); + } + } + \endcode + * + * \param conn The number of the UDP connection to be processed. + * + * \hideinitializer + */ +#define uip_udp_periodic(conn) do { uip_udp_conn = &uip_udp_conns[conn]; \ + uip_process(UIP_UDP_TIMER); } while (0) + +/** + * Periodic processing for a UDP connection identified by a pointer to + * its structure. + * + * Same as uip_udp_periodic() but takes a pointer to the actual + * uip_conn struct instead of an integer as its argument. This + * function can be used to force periodic processing of a specific + * connection. + * + * \param conn A pointer to the uip_udp_conn struct for the connection + * to be processed. + * + * \hideinitializer + */ +#define uip_udp_periodic_conn(conn) do { uip_udp_conn = conn; \ + uip_process(UIP_UDP_TIMER); } while (0) + +#endif + +void uip_udp_periodic(struct uip_stack *ustack, int conn); +#endif /* UIP_UDP */ + +/** + * The uIP packet buffer. + * + * The uip_buf array is used to hold incoming and outgoing + * packets. The device driver should place incoming data into this + * buffer. When sending data, the device driver should read the link + * level headers and the TCP/IP headers from this buffer. The size of + * the link level headers is configured by the UIP_LLH_LEN define. + * + * \note The application data need not be placed in this buffer, so + * the device driver must read it from the place pointed to by the + * uip_appdata pointer as illustrated by the following example: + \code + void + devicedriver_send(void) + { + hwsend(&uip_buf[0], UIP_LLH_LEN); + if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) { + hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN); + } else { + hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN); + hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN); + } + } + \endcode + */ +//extern u8_t uip_buf[UIP_BUFSIZE+2]; + +/** @} */ + +/*---------------------------------------------------------------------------*/ +/* Functions that are used by the uIP application program. Opening and + * closing connections, sending and receiving data, etc. is all + * handled by the functions below. +*/ +/** + * \defgroup uipappfunc uIP application functions + * @{ + * + * Functions used by an application running of top of uIP. + */ + +/** + * Start listening to the specified port. + * + * \note Since this function expects the port number in network byte + * order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_listen(HTONS(80)); + \endcode + * + * \param port A 16-bit port number in network byte order. + */ +void uip_listen(struct uip_stack *ustack, u16_t port); + +/** + * Stop listening to the specified port. + * + * \note Since this function expects the port number in network byte + * order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_unlisten(HTONS(80)); + \endcode + * + * \param port A 16-bit port number in network byte order. + */ +void uip_unlisten(struct uip_stack *ustack, u16_t port); + +/** + * Connect to a remote host using TCP. + * + * This function is used to start a new connection to the specified + * port on the specied host. It allocates a new connection identifier, + * sets the connection to the SYN_SENT state and sets the + * retransmission timer to 0. This will cause a TCP SYN segment to be + * sent out the next time this connection is periodically processed, + * which usually is done within 0.5 seconds after the call to + * uip_connect(). + * + * \note This function is avaliable only if support for active open + * has been configured by defining UIP_ACTIVE_OPEN to 1 in uipopt.h. + * + * \note Since this function requires the port number to be in network + * byte order, a conversion using HTONS() or htons() is necessary. + * + \code + uip_ipaddr_t ipaddr; + + uip_ipaddr(&ipaddr, 192,168,1,2); + uip_connect(&ipaddr, HTONS(80)); + \endcode + * + * \param ripaddr The IP address of the remote hot. + * + * \param port A 16-bit port number in network byte order. + * + * \return A pointer to the uIP connection identifier for the new connection, + * or NULL if no connection could be allocated. + * + */ +struct uip_conn *uip_connect(struct uip_stack *ustack, + uip_ipaddr_t *ripaddr, u16_t port); + + + +/** + * \internal + * + * Check if a connection has outstanding (i.e., unacknowledged) data. + * + * \param conn A pointer to the uip_conn structure for the connection. + * + * \hideinitializer + */ +#define uip_outstanding(conn) ((conn)->len) + +/** + * Send data on the current connection. + * + * This function is used to send out a single segment of TCP + * data. Only applications that have been invoked by uIP for event + * processing can send data. + * + * The amount of data that actually is sent out after a call to this + * funcion is determined by the maximum amount of data TCP allows. uIP + * will automatically crop the data so that only the appropriate + * amount of data is sent. The function uip_mss() can be used to query + * uIP for the amount of data that actually will be sent. + * + * \note This function does not guarantee that the sent data will + * arrive at the destination. If the data is lost in the network, the + * application will be invoked with the uip_rexmit() event being + * set. The application will then have to resend the data using this + * function. + * + * \param data A pointer to the data which is to be sent. + * + * \param len The maximum amount of data bytes to be sent. + * + * \hideinitializer + */ +void uip_send(struct uip_stack *ustack, const void *data, int len); + +/** + * The length of any incoming data that is currently avaliable (if avaliable) + * in the uip_appdata buffer. + * + * The test function uip_data() must first be used to check if there + * is any data available at all. + * + * \hideinitializer + */ +/*void uip_datalen(void);*/ +u16_t uip_datalen(struct uip_stack *ustack); + +/** + * The length of any out-of-band data (urgent data) that has arrived + * on the connection. + * + * \note The configuration parameter UIP_URGDATA must be set for this + * function to be enabled. + * + * \hideinitializer + */ +#define uip_urgdatalen() uip_urglen + +/** + * Close the current connection. + * + * This function will close the current connection in a nice way. + * + * \hideinitializer + */ +#define uip_close() (uip_flags = UIP_CLOSE) + +/** + * Abort the current connection. + * + * This function will abort (reset) the current connection, and is + * usually used when an error has occured that prevents using the + * uip_close() function. + * + * \hideinitializer + */ +#define uip_abort() (uip_flags = UIP_ABORT) + +/** + * Tell the sending host to stop sending data. + * + * This function will close our receiver's window so that we stop + * receiving data for the current connection. + * + * \hideinitializer + */ +#define uip_stop() (uip_conn->tcpstateflags |= UIP_STOPPED) + +/** + * Find out if the current connection has been previously stopped with + * uip_stop(). + * + * \hideinitializer + */ +#define uip_stopped(conn) ((conn)->tcpstateflags & UIP_STOPPED) + +/** + * Restart the current connection, if is has previously been stopped + * with uip_stop(). + * + * This function will open the receiver's window again so that we + * start receiving data for the current connection. + * + * \hideinitializer + */ +#define uip_restart() do { uip_flags |= UIP_NEWDATA; \ + uip_conn->tcpstateflags &= ~UIP_STOPPED; \ + } while(0) + + +/* uIP tests that can be made to determine in what state the current + connection is, and what the application function should do. */ + +/** + * Is the current connection a UDP connection? + * + * This function checks whether the current connection is a UDP connection. + * + * \hideinitializer + * + */ +#define uip_udpconnection() (uip_conn == NULL) + +/** + * Function declarations for hte uip_flags + */ +/** + * Is new incoming data available? + * + * Will reduce to non-zero if there is new data for the application + * present at the uip_appdata pointer. The size of the data is + * avaliable through the uip_len variable. + * + * \hideinitializer + */ +int uip_newdata(struct uip_stack *ustack); + +/** + * Has previously sent data been acknowledged? + * + * Will reduce to non-zero if the previously sent data has been + * acknowledged by the remote host. This means that the application + * can send new data. + * + * \hideinitializer + */ +int uip_acked(struct uip_stack *ustack); + +/** + * Has the connection just been connected? + * + * Reduces to non-zero if the current connection has been connected to + * a remote host. This will happen both if the connection has been + * actively opened (with uip_connect()) or passively opened (with + * uip_listen()). + * + * \hideinitializer + */ +int uip_connected(struct uip_stack *ustack); + +/** + * Has the connection been closed by the other end? + * + * Is non-zero if the connection has been closed by the remote + * host. The application may then do the necessary clean-ups. + * + * \hideinitializer + */ +int uip_closed(struct uip_stack *ustack); + +/** + * Has the connection been aborted by the other end? + * + * Non-zero if the current connection has been aborted (reset) by the + * remote host. + * + * \hideinitializer + */ +int uip_aborted(struct uip_stack *ustack); + +/** + * Has the connection timed out? + * + * Non-zero if the current connection has been aborted due to too many + * retransmissions. + * + * \hideinitializer + */ +int uip_timedout(struct uip_stack *ustack); + +/** + * Do we need to retransmit previously data? + * + * Reduces to non-zero if the previously sent data has been lost in + * the network, and the application should retransmit it. The + * application should send the exact same data as it did the last + * time, using the uip_send() function. + * + * \hideinitializer + */ +int uip_rexmit(struct uip_stack *ustack); + +/** + * Is the connection being polled by uIP? + * + * Is non-zero if the reason the application is invoked is that the + * current connection has been idle for a while and should be + * polled. + * + * The polling event can be used for sending data without having to + * wait for the remote host to send data. + * + * \hideinitializer + */ +int uip_poll(struct uip_stack *ustack); + +/** + * Get the initial maxium segment size (MSS) of the current + * connection. + * + * \hideinitializer + */ +int uip_initialmss(struct uip_stack *ustack); + +/** + * Get the current maxium segment size that can be sent on the current + * connection. + * + * The current maxiumum segment size that can be sent on the + * connection is computed from the receiver's window and the MSS of + * the connection (which also is available by calling + * uip_initialmss()). + * + * \hideinitializer + */ +int uip_mss(struct uip_stack *ustack); + +/** + * Set up a new UDP connection. + * + * This function sets up a new UDP connection. The function will + * automatically allocate an unused local port for the new + * connection. However, another port can be chosen by using the + * uip_udp_bind() call, after the uip_udp_new() function has been + * called. + * + * Example: + \code + uip_ipaddr_t addr; + struct uip_udp_conn *c; + + uip_ipaddr(&addr, 192,168,2,1); + c = uip_udp_new(&addr, HTONS(12345)); + if(c != NULL) { + uip_udp_bind(c, HTONS(12344)); + } + \endcode + * \param ripaddr The IP address of the remote host. + * + * \param rport The remote port number in network byte order. + * + * \return The uip_udp_conn structure for the new connection or NULL + * if no connection could be allocated. + */ +struct uip_udp_conn *uip_udp_new(struct uip_stack *ustack, + uip_ipaddr_t *ripaddr, u16_t rport); + +/** + * Removed a UDP connection. + * + * \param conn A pointer to the uip_udp_conn structure for the connection. + * + * \hideinitializer + */ +#define uip_udp_remove(conn) (conn)->lport = 0 + +/** + * Bind a UDP connection to a local port. + * + * \param conn A pointer to the uip_udp_conn structure for the + * connection. + * + * \param port The local port number, in network byte order. + * + * \hideinitializer + */ +#define uip_udp_bind(conn, port) (conn)->lport = port + +/** + * Send a UDP datagram of length len on the current connection. + * + * This function can only be called in response to a UDP event (poll + * or newdata). The data must be present in the uip_buf buffer, at the + * place pointed to by the uip_appdata pointer. + * + * \param len The length of the data in the uip_buf buffer. + * + * \hideinitializer + */ +#define uip_udp_send(len) uip_send((char *)uip_appdata, len) + +/** @} */ + +/* uIP convenience and converting functions. */ + +/** + * \defgroup uipconvfunc uIP conversion functions + * @{ + * + * These functions can be used for converting between different data + * formats used by uIP. + */ + +/** + * Construct an IP address from four bytes. + * + * This function constructs an IP address of the type that uIP handles + * internally from four bytes. The function is handy for specifying IP + * addresses to use with e.g. the uip_connect() function. + * + * Example: + \code + uip_ipaddr_t ipaddr; + struct uip_conn *c; + + uip_ipaddr(&ipaddr, 192,168,1,2); + c = uip_connect(&ipaddr, HTONS(80)); + \endcode + * + * \param addr A pointer to a uip_ipaddr_t variable that will be + * filled in with the IP address. + * + * \param addr0 The first octet of the IP address. + * \param addr1 The second octet of the IP address. + * \param addr2 The third octet of the IP address. + * \param addr3 The forth octet of the IP address. + * + * \hideinitializer + */ +#define uip_ipaddr(addr, addr0,addr1,addr2,addr3) do { \ + ((u16_t *)(addr))[0] = const_htons(((addr0) << 8) | (addr1)); \ + ((u16_t *)(addr))[1] = const_htons(((addr2) << 8) | (addr3)); \ + } while(0) + +/** + * Construct an IPv6 address from eight 16-bit words. + * + * This function constructs an IPv6 address. + * + * \hideinitializer + */ +#define uip_ip6addr(addr, addr0,addr1,addr2,addr3,addr4,addr5,addr6,addr7) do { \ + ((u16_t *)(addr))[0] = HTONS((addr0)); \ + ((u16_t *)(addr))[1] = HTONS((addr1)); \ + ((u16_t *)(addr))[2] = HTONS((addr2)); \ + ((u16_t *)(addr))[3] = HTONS((addr3)); \ + ((u16_t *)(addr))[4] = HTONS((addr4)); \ + ((u16_t *)(addr))[5] = HTONS((addr5)); \ + ((u16_t *)(addr))[6] = HTONS((addr6)); \ + ((u16_t *)(addr))[7] = HTONS((addr7)); \ + } while(0) + +/** + * Copy an IP address to another IP address. + * + * Copies an IP address from one place to another. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr_copy(&ipaddr2, &ipaddr1); + \endcode + * + * \param dest The destination for the copy. + * \param src The source from where to copy. + * + * \hideinitializer + */ +#if !UIP_CONF_IPV6 +#define uip_ipaddr_copy(dest, src) do { \ + ((u16_t *)dest)[0] = ((u16_t *)src)[0]; \ + ((u16_t *)dest)[1] = ((u16_t *)src)[1]; \ + } while(0) +#else /* !UIP_CONF_IPV6 */ +#define uip_ipaddr_copy(dest, src) memcpy(dest, src, sizeof(uip_ip6addr_t)) +#endif /* !UIP_CONF_IPV6 */ + +/** + * Compare two IP addresses + * + * Compares two IP addresses. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + if(uip_ipaddr_cmp(&ipaddr2, &ipaddr1)) { + printf("They are the same"); + } + \endcode + * + * \param addr1 The first IP address. + * \param addr2 The second IP address. + * + * \hideinitializer + */ +#if !UIP_CONF_IPV6 +#define uip_ipaddr_cmp(addr1, addr2) (((u16_t *)addr1)[0] == ((u16_t *)addr2)[0] && \ + ((u16_t *)addr1)[1] == ((u16_t *)addr2)[1]) +#else /* !UIP_CONF_IPV6 */ +#define uip_ipaddr_cmp(addr1, addr2) (memcmp(addr1, addr2, sizeof(uip_ip6addr_t)) == 0) +#endif /* !UIP_CONF_IPV6 */ + +/** + * Compare two IP addresses with netmasks + * + * Compares two IP addresses with netmasks. The masks are used to mask + * out the bits that are to be compared. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2, mask; + + uip_ipaddr(&mask, 255,255,255,0); + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr(&ipaddr2, 192,16,1,3); + if(uip_ipaddr_maskcmp(&ipaddr1, &ipaddr2, &mask)) { + printf("They are the same"); + } + \endcode + * + * \param addr1 The first IP address. + * \param addr2 The second IP address. + * \param mask The netmask. + * + * \hideinitializer + */ +#define uip_ipaddr_maskcmp(addr1, addr2, mask) \ + (((((u16_t *)addr1)[0] & ((u16_t *)mask)[0]) == \ + (((u16_t *)addr2)[0] & ((u16_t *)mask)[0])) && \ + ((((u16_t *)addr1)[1] & ((u16_t *)mask)[1]) == \ + (((u16_t *)addr2)[1] & ((u16_t *)mask)[1]))) + + +/** + * Mask out the network part of an IP address. + * + * Masks out the network part of an IP address, given the address and + * the netmask. + * + * Example: + \code + uip_ipaddr_t ipaddr1, ipaddr2, netmask; + + uip_ipaddr(&ipaddr1, 192,16,1,2); + uip_ipaddr(&netmask, 255,255,255,0); + uip_ipaddr_mask(&ipaddr2, &ipaddr1, &netmask); + \endcode + * + * In the example above, the variable "ipaddr2" will contain the IP + * address 192.168.1.0. + * + * \param dest Where the result is to be placed. + * \param src The IP address. + * \param mask The netmask. + * + * \hideinitializer + */ +#define uip_ipaddr_mask(dest, src, mask) do { \ + ((u16_t *)dest)[0] = ((u16_t *)src)[0] & ((u16_t *)mask)[0]; \ + ((u16_t *)dest)[1] = ((u16_t *)src)[1] & ((u16_t *)mask)[1]; \ + } while(0) + +/** + * Pick the first octet of an IP address. + * + * Picks out the first octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr1(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 1. + * + * \hideinitializer + */ +#define uip_ipaddr1(addr) (htons(((u16_t *)(addr))[0]) >> 8) + +/** + * Pick the second octet of an IP address. + * + * Picks out the second octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr2(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 2. + * + * \hideinitializer + */ +#define uip_ipaddr2(addr) (htons(((u16_t *)(addr))[0]) & 0xff) + +/** + * Pick the third octet of an IP address. + * + * Picks out the third octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr3(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 3. + * + * \hideinitializer + */ +#define uip_ipaddr3(addr) (htons(((u16_t *)(addr))[1]) >> 8) + +/** + * Pick the fourth octet of an IP address. + * + * Picks out the fourth octet of an IP address. + * + * Example: + \code + uip_ipaddr_t ipaddr; + u8_t octet; + + uip_ipaddr(&ipaddr, 1,2,3,4); + octet = uip_ipaddr4(&ipaddr); + \endcode + * + * In the example above, the variable "octet" will contain the value 4. + * + * \hideinitializer + */ +#define uip_ipaddr4(addr) (htons(((u16_t *)(addr))[1]) & 0xff) + +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This macro is primarily used for converting constants from host + * byte order to network byte order. For converting variables to + * network byte order, use the htons() function instead. + * + * \hideinitializer + */ +#if 0 +#ifndef HTONS +# if UIP_BYTE_ORDER == UIP_BIG_ENDIAN +# define HTONS(n) (n) +# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +# define HTONS(n) (u16_t)((((u16_t) (n)) << 8) | (((u16_t) (n)) >> 8)) +# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +#else +#error "HTONS already defined!" +#endif /* HTONS */ +#endif + +#if UIP_BYTE_ORDER == UIP_BIG_ENDIAN +# error "Should not be here" +# define const_htons(n) (n) +# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ +# define const_htons(n) (u16_t)((((u16_t) (n)) << 8) | (((u16_t) (n)) >> 8)) +# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */ + +/* BWL */ +#if 0 +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This function is primarily used for converting variables from host + * byte order to network byte order. For converting constants to + * network byte order, use the HTONS() macro instead. + */ +#ifndef htons +u16_t htons(u16_t val); +#endif /* htons */ +#ifndef ntohs +#define ntohs htons +#endif +#endif + +/** @} */ + +/** + * Pointer to the application data in the packet buffer. + * + * This pointer points to the application data when the application is + * called. If the application wishes to send data, the application may + * use this space to write the data into before calling uip_send(). + */ +//extern void *uip_appdata; + +#if UIP_URGDATA > 0 +/* u8_t *uip_urgdata: + * + * This pointer points to any urgent data that has been received. Only + * present if compiled with support for urgent data (UIP_URGDATA). + */ +extern void *uip_urgdata; +#endif /* UIP_URGDATA > 0 */ + + +/** + * \defgroup uipdrivervars Variables used in uIP device drivers + * @{ + * + * uIP has a few global variables that are used in device drivers for + * uIP. + */ + +/** + * The length of the packet in the uip_buf buffer. + * + * The global variable uip_len holds the length of the packet in the + * uip_buf buffer. + * + * When the network device driver calls the uIP input function, + * uip_len should be set to the length of the packet in the uip_buf + * buffer. + * + * When sending packets, the device driver should use the contents of + * the uip_len variable to determine the length of the outgoing + * packet. + * + */ +//extern u16_t uip_len; + +/** @} */ + +#if UIP_URGDATA > 0 +extern u16_t uip_urglen, uip_surglen; +#endif /* UIP_URGDATA > 0 */ + + +/** + * Representation of a uIP TCP connection. + * + * The uip_conn structure is used for identifying a connection. All + * but one field in the structure are to be considered read-only by an + * application. The only exception is the appstate field whos purpose + * is to let the application store application-specific state (e.g., + * file pointers) for the connection. The type of this field is + * configured in the "uipopt.h" header file. + */ +struct __attribute__ ((__packed__)) uip_conn +{ + uip_ipaddr_t ripaddr; /**< The IP address of the remote host. */ + + u16_t lport; /**< The local TCP port, in network byte order. */ + u16_t rport; /**< The local remote TCP port, in network byte + order. */ + + u8_t rcv_nxt[4]; /**< The sequence number that we expect to + receive next. */ + u8_t snd_nxt[4]; /**< The sequence number that was last sent by + us. */ + u16_t len; /**< Length of the data that was previously sent. */ + u16_t mss; /**< Current maximum segment size for the + connection. */ + u16_t initialmss; /**< Initial maximum segment size for the + connection. */ + u8_t sa; /**< Retransmission time-out calculation state + variable. */ + u8_t sv; /**< Retransmission time-out calculation state + variable. */ + u8_t rto; /**< Retransmission time-out. */ + u8_t tcpstateflags; /**< TCP state and flags. */ + u8_t timer; /**< The retransmission timer. */ + u8_t nrtx; /**< The number of retransmissions for the last + segment sent. */ + + /** The application state. */ +/* BWL */ +// uip_tcp_appstate_t appstate; +}; + + +/** + * \addtogroup uiparch + * @{ + */ + +/** + * 4-byte array used for the 32-bit sequence number calculations. + */ +extern u8_t uip_acc32[4]; + +/** @} */ + + +#if UIP_UDP +/** + * Representation of a uIP UDP connection. + */ +struct uip_udp_conn { + uip_ipaddr_t ripaddr; /**< The IP address of the remote peer. */ + u16_t lport; /**< The local port number in network byte order. */ + u16_t rport; /**< The remote port number in network byte order. */ + u8_t ttl; /**< Default time-to-live. */ + + /** The application state. */ +// uip_udp_appstate_t appstate; +}; + +#endif /* UIP_UDP */ + +/** + * The structure holding the TCP/IP statistics that are gathered if + * UIP_STATISTICS is set to 1. + * + */ +struct uip_stats { + struct { + uip_stats_t drop; /**< Number of dropped packets at the IP + layer. */ + uip_stats_t recv; /**< Number of received packets at the IP + layer. */ + uip_stats_t sent; /**< Number of sent packets at the IP + layer. */ + uip_stats_t vhlerr; /**< Number of packets dropped due to wrong + IP version or header length. */ + uip_stats_t hblenerr; /**< Number of packets dropped due to wrong + IP length, high byte. */ + uip_stats_t lblenerr; /**< Number of packets dropped due to wrong + IP length, low byte. */ + uip_stats_t fragerr; /**< Number of packets dropped since they + were IP fragments. */ + uip_stats_t chkerr; /**< Number of packets dropped due to IP + checksum errors. */ + uip_stats_t protoerr; /**< Number of packets dropped since they + were neither ICMP, UDP nor TCP. */ + } ip; /**< IP statistics. */ + struct { + uip_stats_t drop; /**< Number of dropped ICMP packets. */ + uip_stats_t recv; /**< Number of received ICMP packets. */ + uip_stats_t sent; /**< Number of sent ICMP packets. */ + uip_stats_t typeerr; /**< Number of ICMP packets with a wrong + type. */ + } icmp; /**< ICMP statistics. */ + struct { + uip_stats_t drop; /**< Number of dropped TCP segments. */ + uip_stats_t recv; /**< Number of recived TCP segments. */ + uip_stats_t sent; /**< Number of sent TCP segments. */ + uip_stats_t chkerr; /**< Number of TCP segments with a bad + checksum. */ + uip_stats_t ackerr; /**< Number of TCP segments with a bad ACK + number. */ + uip_stats_t rst; /**< Number of recevied TCP RST (reset) segments. */ + uip_stats_t rexmit; /**< Number of retransmitted TCP segments. */ + uip_stats_t syndrop; /**< Number of dropped SYNs due to too few + connections was avaliable. */ + uip_stats_t synrst; /**< Number of SYNs for closed ports, + triggering a RST. */ + } tcp; /**< TCP statistics. */ +#if UIP_UDP + struct { + uip_stats_t drop; /**< Number of dropped UDP segments. */ + uip_stats_t recv; /**< Number of recived UDP segments. */ + uip_stats_t sent; /**< Number of sent UDP segments. */ + uip_stats_t chkerr; /**< Number of UDP segments with a bad + checksum. */ + } udp; /**< UDP statistics. */ +#endif /* UIP_UDP */ +}; + + +/*---------------------------------------------------------------------------*/ +/* All the stuff below this point is internal to uIP and should not be + * used directly by an application or by a device driver. + */ +/*---------------------------------------------------------------------------*/ +/* u8_t uip_flags: + * + * When the application is called, uip_flags will contain the flags + * that are defined in this file. Please read below for more + * infomation. + */ +//extern u8_t uip_flags; + +/* The following flags may be set in the global variable uip_flags + before calling the application callback. The UIP_ACKDATA, + UIP_NEWDATA, and UIP_CLOSE flags may both be set at the same time, + whereas the others are mutualy exclusive. Note that these flags + should *NOT* be accessed directly, but only through the uIP + functions/macros. */ + +#define UIP_ACKDATA 1 /* Signifies that the outstanding data was + acked and the application should send + out new data instead of retransmitting + the last data. */ +#define UIP_NEWDATA 2 /* Flags the fact that the peer has sent + us new data. */ +#define UIP_REXMIT 4 /* Tells the application to retransmit the + data that was last sent. */ +#define UIP_POLL 8 /* Used for polling the application, to + check if the application has data that + it wants to send. */ +#define UIP_CLOSE 16 /* The remote host has closed the + connection, thus the connection has + gone away. Or the application signals + that it wants to close the + connection. */ +#define UIP_ABORT 32 /* The remote host has aborted the + connection, thus the connection has + gone away. Or the application signals + that it wants to abort the + connection. */ +#define UIP_CONNECTED 64 /* We have got a connection from a remote + host and have set up a new connection + for it, or an active connection has + been successfully established. */ + +#define UIP_TIMEDOUT 128 /* The connection has been aborted due to + too many retransmissions. */ + +void uip_input(struct uip_stack *ustack); +void uip_periodic(struct uip_stack *ustack, int conn); + +/* uip_process(flag): + * + * The actual uIP function which does all the work. + */ +void uip_process(struct uip_stack *ustack, u8_t flag); + +/* The following flags are passed as an argument to the uip_process() + function. They are used to distinguish between the two cases where + uip_process() is called. It can be called either because we have + incoming data that should be processed, or because the periodic + timer has fired. These values are never used directly, but only in + the macrose defined in this file. */ + +#define UIP_DATA 1 /* Tells uIP that there is incoming + data in the uip_buf buffer. The + length of the data is stored in the + global variable uip_len. */ +#define UIP_TIMER 2 /* Tells uIP that the periodic timer + has fired. */ +#define UIP_POLL_REQUEST 3 /* Tells uIP that a connection should + be polled. */ +#define UIP_UDP_SEND_CONN 4 /* Tells uIP that a UDP datagram + should be constructed in the + uip_buf buffer. */ +#if UIP_UDP +#define UIP_UDP_TIMER 5 +#endif /* UIP_UDP */ + +/* The TCP states used in the uip_conn->tcpstateflags. */ +#define UIP_CLOSED 0 +#define UIP_SYN_RCVD 1 +#define UIP_SYN_SENT 2 +#define UIP_ESTABLISHED 3 +#define UIP_FIN_WAIT_1 4 +#define UIP_FIN_WAIT_2 5 +#define UIP_CLOSING 6 +#define UIP_TIME_WAIT 7 +#define UIP_LAST_ACK 8 +#define UIP_TS_MASK 15 + +#define UIP_STOPPED 16 + +/* The TCP and IPv4 headers. */ +struct __attribute__ ((__packed__)) uip_tcpipv4_hdr { + /* IPv4 header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; + + /* TCP header. */ + u16_t srcport, + destport; + u8_t seqno[4], + ackno[4], + tcpoffset, + flags, + wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +/* The TCP and IPv6 headers. */ +struct __attribute__ ((__packed__)) uip_tcpipv6_hdr { + /* IPv6 header. */ + u8_t vtc, + tcflow; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; + + /* TCP header. */ + u16_t srcport, + destport; + u8_t seqno[4], + ackno[4], + tcpoffset, + flags, + wnd[2]; + u16_t tcpchksum; + u8_t urgp[2]; + u8_t optdata[4]; +}; + +/* The ICMP and IP headers. */ +struct __attribute__ ((__packed__)) uip_icmpip_hdr { +#if UIP_CONF_IPV6 + /* IPv6 header. */ + u8_t vtc, + tcf; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; +#else /* UIP_CONF_IPV6 */ + /* IPv4 header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; +#endif /* UIP_CONF_IPV6 */ + + /* ICMP (echo) header. */ + u8_t type, icode; + u16_t icmpchksum; +#if !UIP_CONF_IPV6 + u16_t id, seqno; +#else /* !UIP_CONF_IPV6 */ + u8_t flags, reserved1, reserved2, reserved3; + u8_t icmp6data[16]; + u8_t options[1]; +#endif /* !UIP_CONF_IPV6 */ +}; + + +/* The UDP and IP headers. */ +struct __attribute__ ((__packed__)) uip_udpip_hdr { +#if UIP_CONF_IPV6 + /* IPv6 header. */ + u8_t vtc, + tcf; + u16_t flow; + u8_t len[2]; + u8_t proto, ttl; + uip_ip6addr_t srcipaddr, destipaddr; +#else /* UIP_CONF_IPV6 */ + /* IP header. */ + u8_t vhl, + tos, + len[2], + ipid[2], + ipoffset[2], + ttl, + proto; + u16_t ipchksum; + u16_t srcipaddr[2], + destipaddr[2]; +#endif /* UIP_CONF_IPV6 */ + + /* UDP header. */ + u16_t srcport, + destport; + u16_t udplen; + u16_t udpchksum; +}; + + + +/** + * The buffer size available for user data in the \ref uip_buf buffer. + * + * This macro holds the available size for user data in the \ref + * uip_buf buffer. The macro is intended to be used for checking + * bounds of available user data. + * + * Example: + \code + snprintf(uip_appdata, UIP_APPDATA_SIZE, "%u\n", i); + \endcode + * + * \hideinitializer + */ +#define UIP_APPDATA_SIZE (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) + + +#define UIP_PROTO_ICMP 1 +#define UIP_PROTO_TCP 6 +#define UIP_PROTO_UDP 17 +#define UIP_PROTO_ICMP6 58 + +/* Header sizes. */ +#if UIP_CONF_IPV6 +#define UIP_IPH_LEN 40 +#else /* UIP_CONF_IPV6 */ +#define UIP_IPH_LEN 20 /* Size of IP header */ +#endif /* UIP_CONF_IPV6 */ +#define UIP_UDPH_LEN 8 /* Size of UDP header */ +#define UIP_TCPH_LEN 20 /* Size of TCP header */ +#define UIP_IPUDPH_LEN (UIP_UDPH_LEN + UIP_IPH_LEN) /* Size of IP + + UDP + header */ +#define UIP_IPTCPH_LEN (UIP_TCPH_LEN + UIP_IPH_LEN) /* Size of IP + + TCP + header */ +#define UIP_TCPIP_HLEN UIP_IPTCPH_LEN + + +/** + * Representation of a 48-bit Ethernet address. + */ +struct uip_eth_addr { + u8_t addr[6]; +}; + +/** + * Calculate the Internet checksum over a buffer. + * + * The Internet checksum is the one's complement of the one's + * complement sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * \param buf A pointer to the buffer over which the checksum is to be + * computed. + * + * \param len The length of the buffer over which the checksum is to + * be computed. + * + * \return The Internet checksum of the buffer. + */ +u16_t uip_chksum(u16_t *buf, u16_t len); + +/** + * Calculate the IP header checksum of the packet header in uip_buf. + * + * The IP header checksum is the Internet checksum of the 20 bytes of + * the IP header. + * + * \return The IP header checksum of the IP header in the uip_buf + * buffer. + */ +u16_t uip_ipchksum(struct uip_stack *ustack); + +/** + * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. + * + * The TCP checksum is the Internet checksum of data contents of the + * TCP segment, and a pseudo-header as defined in RFC793. + * + * \return The TCP checksum of the TCP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_tcpchksum(struct uip_stack *ustack); + +/** + * Calculate the UDP checksum of the packet in uip_buf and uip_appdata. + * + * The UDP checksum is the Internet checksum of data contents of the + * UDP segment, and a pseudo-header as defined in RFC768. + * + * \return The UDP checksum of the UDP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_udpchksum(struct uip_stack *ustack); + +struct uip_stack { + struct uip_eth_addr uip_ethaddr; + +#ifndef UIP_CONF_EXTERNAL_BUFFER + u8_t uip_buf[UIP_BUFSIZE + 2]; /* The packet buffer that contains + incoming packets. */ +#endif /* UIP_CONF_EXTERNAL_BUFFER */ + + void *uip_appdata; /* The uip_appdata pointer points to + application data. */ + void *uip_sappdata; /* The uip_appdata pointer points to + the application data which is to + be sent. */ +#if UIP_URGDATA > 0 + void *uip_urgdata; /* The uip_urgdata pointer points to + urgent data (out-of-band data), if + present. */ + u16_t uip_urglen, uip_surglen; +#endif /* UIP_URGDATA > 0 */ + + u16_t uip_len, uip_slen; /* The uip_len is either 8 or 16 bits, + depending on the maximum packet + size. */ + u8_t uip_flags; /* The uip_flags variable is used for + communication between the TCP/IP stack + and the application program. */ + struct uip_conn *uip_conn; /* uip_conn always points to the current + connection. */ + + struct uip_conn uip_conns[UIP_CONNS]; + /* The uip_conns array holds all TCP + connections. */ + u16_t uip_listenports[UIP_LISTENPORTS]; + /* The uip_listenports list all currently + listning ports. */ +#if UIP_UDP + struct uip_udp_conn *uip_udp_conn; + struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; +#endif /* UIP_UDP */ + + u16_t ipid; /* This ipid variable is an increasing + number that is used for the IP ID + field. */ + + u8_t iss[4]; /* The iss variable is used for the TCP + initial sequence number. */ + +#if UIP_ACTIVE_OPEN + u16_t lastport; /* Keeps track of the last port used for + a new connection. */ +#endif /* UIP_ACTIVE_OPEN */ + + uip_ipaddr_t uip_hostaddr, uip_netmask, uip_draddr; + + struct uip_stats stats; + + u8_t opt; + + pthread_mutex_t lock; + + /* IPv6 support */ + +#define UIP_SUPPORT_IPv6_ENABLED 0x01 +#define UIP_SUPPORT_IPv6_DISABLED 0x02 + u8_t enable_IPv6; +}; + +/******************************************************************************* + * IPv6 Support + ******************************************************************************/ +int get_ipv6_link_local_address(struct uip_stack *ustack, + char *addr); + + + +#endif /* __UIP_H__ */ + + +/** @} */ diff --git a/brcm_iscsi_uio/src/uip/uiplib.c b/brcm_iscsi_uio/src/uip/uiplib.c new file mode 100644 index 0000000..cb5af2c --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uiplib.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004, Adam Dunkels and the Swedish Institute of + * Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: uiplib.c,v 1.2 2006/06/12 08:00:31 adam Exp $ + * + */ + + +#include "uip.h" +#include "uiplib.h" + + +/*-----------------------------------------------------------------------------------*/ +unsigned char +uiplib_ipaddrconv(char *addrstr, unsigned char *ipaddr) +{ + unsigned char tmp; + char c; + unsigned char i, j; + + tmp = 0; + + for(i = 0; i < 4; ++i) { + j = 0; + do { + c = *addrstr; + ++j; + if(j > 4) { + return 0; + } + if(c == '.' || c == 0) { + *ipaddr = tmp; + ++ipaddr; + tmp = 0; + } else if(c >= '0' && c <= '9') { + tmp = (tmp * 10) + (c - '0'); + } else { + return 0; + } + ++addrstr; + } while(c != '.' && c != 0); + } + return 1; +} + +/*-----------------------------------------------------------------------------------*/ diff --git a/brcm_iscsi_uio/src/uip/uiplib.h b/brcm_iscsi_uio/src/uip/uiplib.h new file mode 100644 index 0000000..c676849 --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uiplib.h @@ -0,0 +1,71 @@ +/** + * \file + * Various uIP library functions. + * \author + * Adam Dunkels + * + */ + +/* + * Copyright (c) 2002, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: uiplib.h,v 1.1 2006/06/07 09:15:19 adam Exp $ + * + */ +#ifndef __UIPLIB_H__ +#define __UIPLIB_H__ + +/** + * \addtogroup uipconvfunc + * @{ + */ + +/** + * Convert a textual representation of an IP address to a numerical representation. + * + * This function takes a textual representation of an IP address in + * the form a.b.c.d and converts it into a 4-byte array that can be + * used by other uIP functions. + * + * \param addrstr A pointer to a string containing the IP address in + * textual form. + * + * \param addr A pointer to a 4-byte array that will be filled in with + * the numerical representation of the address. + * + * \retval 0 If the IP address could not be parsed. + * \retval Non-zero If the IP address was parsed. + */ +unsigned char uiplib_ipaddrconv(char *addrstr, unsigned char *addr); + +/** @} */ + +#endif /* __UIPLIB_H__ */ diff --git a/brcm_iscsi_uio/src/uip/uipopt.h b/brcm_iscsi_uio/src/uip/uipopt.h new file mode 100644 index 0000000..98bfb0e --- /dev/null +++ b/brcm_iscsi_uio/src/uip/uipopt.h @@ -0,0 +1,541 @@ +/** + * \defgroup uipopt Configuration options for uIP + * @{ + * + * uIP is configured using the per-project configuration file + * uipopt.h. This file contains all compile-time options for uIP and + * should be tweaked to match each specific project. The uIP + * distribution contains a documented example "uipopt.h" that can be + * copied and modified for each project. + * + * \note Most of the configuration options in the uipopt.h should not + * be changed, but rather the per-project uip-conf.h file. + */ + +/** + * \file + * Configuration options for uIP. + * \author Adam Dunkels + * + * This file is used for tweaking various configuration options for + * uIP. You should make a copy of this file into one of your project's + * directories instead of editing this example "uipopt.h" file that + * comes with the uIP distribution. + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uipopt.h,v 1.4 2006/06/12 08:00:31 adam Exp $ + * + */ + +#ifndef __UIPOPT_H__ +#define __UIPOPT_H__ + +#ifndef UIP_LITTLE_ENDIAN +#define UIP_LITTLE_ENDIAN 3412 +#endif /* UIP_LITTLE_ENDIAN */ +#ifndef UIP_BIG_ENDIAN +#define UIP_BIG_ENDIAN 1234 +#endif /* UIP_BIG_ENDIAN */ + +#include "uip-conf.h" + +/*------------------------------------------------------------------------------*/ + +/** + * \name Static configuration options + * @{ + * + * These configuration options can be used for setting the IP address + * settings statically, but only if UIP_FIXEDADDR is set to 1. The + * configuration options for a specific node includes IP address, + * netmask and default router as well as the Ethernet address. The + * netmask, default router and Ethernet address are appliciable only + * if uIP should be run over Ethernet. + * + * All of these should be changed to suit your project. +*/ + +/** + * Determines if uIP should use a fixed IP address or not. + * + * If uIP should use a fixed IP address, the settings are set in the + * uipopt.h file. If not, the macros uip_sethostaddr(), + * uip_setdraddr() and uip_setnetmask() should be used instead. + * + * \hideinitializer + */ +#define UIP_FIXEDADDR 0 + +/** + * Ping IP address asignment. + * + * uIP uses a "ping" packets for setting its own IP address if this + * option is set. If so, uIP will start with an empty IP address and + * the destination IP address of the first incoming "ping" (ICMP echo) + * packet will be used for setting the hosts IP address. + * + * \note This works only if UIP_FIXEDADDR is 0. + * + * \hideinitializer + */ +#ifdef UIP_CONF_PINGADDRCONF +#define UIP_PINGADDRCONF UIP_CONF_PINGADDRCONF +#else /* UIP_CONF_PINGADDRCONF */ +#define UIP_PINGADDRCONF 0 +#endif /* UIP_CONF_PINGADDRCONF */ + + +/** + * Specifies if the uIP ARP module should be compiled with a fixed + * Ethernet MAC address or not. + * + * If this configuration option is 0, the macro uip_setethaddr() can + * be used to specify the Ethernet address at run-time. + * + * \hideinitializer + */ +#define UIP_FIXEDETHADDR 0 + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \name IP configuration options + * @{ + * + */ +/** + * The IP TTL (time to live) of IP packets sent by uIP. + * + * This should normally not be changed. + */ +#define UIP_TTL 64 + +/** + * Turn on support for IP packet reassembly. + * + * uIP supports reassembly of fragmented IP packets. This features + * requires an additonal amount of RAM to hold the reassembly buffer + * and the reassembly code size is approximately 700 bytes. The + * reassembly buffer is of the same size as the uip_buf buffer + * (configured by UIP_BUFSIZE). + * + * \note IP packet reassembly is not heavily tested. + * + * \hideinitializer + */ +#define UIP_REASSEMBLY 0 + +/** + * The maximum time an IP fragment should wait in the reassembly + * buffer before it is dropped. + * + */ +#define UIP_REASS_MAXAGE 40 + +/** @} */ + +/*------------------------------------------------------------------------------*/ +/** + * \name UDP configuration options + * @{ + */ + +/** + * Toggles wether UDP support should be compiled in or not. + * + * \hideinitializer + */ +#ifdef UIP_CONF_UDP +#define UIP_UDP UIP_CONF_UDP +#else /* UIP_CONF_UDP */ +#define UIP_UDP 0 +#endif /* UIP_CONF_UDP */ + +/** + * Toggles if UDP checksums should be used or not. + * + * \note Support for UDP checksums is currently not included in uIP, + * so this option has no function. + * + * \hideinitializer + */ +#ifdef UIP_CONF_UDP_CHECKSUMS +#define UIP_UDP_CHECKSUMS UIP_CONF_UDP_CHECKSUMS +#else +#define UIP_UDP_CHECKSUMS 0 +#endif + +/** + * The maximum amount of concurrent UDP connections. + * + * \hideinitializer + */ +#ifdef UIP_CONF_UDP_CONNS +#define UIP_UDP_CONNS UIP_CONF_UDP_CONNS +#else /* UIP_CONF_UDP_CONNS */ +#define UIP_UDP_CONNS 10 +#endif /* UIP_CONF_UDP_CONNS */ + +/** + * The name of the function that should be called when UDP datagrams arrive. + * + * \hideinitializer + */ + + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \name TCP configuration options + * @{ + */ + +/** + * Determines if support for opening connections from uIP should be + * compiled in. + * + * If the applications that are running on top of uIP for this project + * do not need to open outgoing TCP connections, this configration + * option can be turned off to reduce the code size of uIP. + * + * \hideinitializer + */ +#define UIP_ACTIVE_OPEN 1 + +/** + * The maximum number of simultaneously open TCP connections. + * + * Since the TCP connections are statically allocated, turning this + * configuration knob down results in less RAM used. Each TCP + * connection requires approximatly 30 bytes of memory. + * + * \hideinitializer + */ +#ifndef UIP_CONF_MAX_CONNECTIONS +#define UIP_CONNS 10 +#else /* UIP_CONF_MAX_CONNECTIONS */ +#define UIP_CONNS UIP_CONF_MAX_CONNECTIONS +#endif /* UIP_CONF_MAX_CONNECTIONS */ + + +/** + * The maximum number of simultaneously listening TCP ports. + * + * Each listening TCP port requires 2 bytes of memory. + * + * \hideinitializer + */ +#ifndef UIP_CONF_MAX_LISTENPORTS +#define UIP_LISTENPORTS 20 +#else /* UIP_CONF_MAX_LISTENPORTS */ +#define UIP_LISTENPORTS UIP_CONF_MAX_LISTENPORTS +#endif /* UIP_CONF_MAX_LISTENPORTS */ + +/** + * Determines if support for TCP urgent data notification should be + * compiled in. + * + * Urgent data (out-of-band data) is a rarely used TCP feature that + * very seldom would be required. + * + * \hideinitializer + */ +#define UIP_URGDATA 0 + +/** + * The initial retransmission timeout counted in timer pulses. + * + * This should not be changed. + */ +#define UIP_RTO 3 + +/** + * The maximum number of times a segment should be retransmitted + * before the connection should be aborted. + * + * This should not be changed. + */ +#define UIP_MAXRTX 8 + +/** + * The maximum number of times a SYN segment should be retransmitted + * before a connection request should be deemed to have been + * unsuccessful. + * + * This should not need to be changed. + */ +#define UIP_MAXSYNRTX 5 + +/** + * The TCP maximum segment size. + * + * This is should not be to set to more than + * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. + */ +#define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCP_IPv4_HLEN) + +/** + * The size of the advertised receiver's window. + * + * Should be set low (i.e., to the size of the uip_buf buffer) is the + * application is slow to process incoming data, or high (32768 bytes) + * if the application processes data quickly. + * + * \hideinitializer + */ +#ifndef UIP_CONF_RECEIVE_WINDOW +#define UIP_RECEIVE_WINDOW UIP_TCP_MSS +#else +#define UIP_RECEIVE_WINDOW UIP_CONF_RECEIVE_WINDOW +#endif + +/** + * How long a connection should stay in the TIME_WAIT state. + * + * This configiration option has no real implication, and it should be + * left untouched. + */ +#define UIP_TIME_WAIT_TIMEOUT 120 + + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \name ARP configuration options + * @{ + */ + +/** + * The size of the ARP table. + * + * This option should be set to a larger value if this uIP node will + * have many connections from the local network. + * + * \hideinitializer + */ +#ifdef UIP_CONF_ARPTAB_SIZE +#define UIP_ARPTAB_SIZE UIP_CONF_ARPTAB_SIZE +#else +#define UIP_ARPTAB_SIZE 8 +#endif + +/** + * The maxium age of ARP table entries measured in 10ths of seconds. + * + * An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD + * default). + */ +#define UIP_ARP_MAXAGE 120 + +/** @} */ + +/*------------------------------------------------------------------------------*/ + +/** + * \name General configuration options + * @{ + */ + +/** + * The size of the uIP packet buffer. + * + * The uIP packet buffer should not be smaller than 60 bytes, and does + * not need to be larger than 1500 bytes. Lower size results in lower + * TCP throughput, larger size results in higher TCP throughput. + * + * \hideinitializer + */ +#ifndef UIP_CONF_BUFFER_SIZE +#define UIP_BUFSIZE 400 +#else /* UIP_CONF_BUFFER_SIZE */ +#define UIP_BUFSIZE UIP_CONF_BUFFER_SIZE +#endif /* UIP_CONF_BUFFER_SIZE */ + + +/** + * Determines if statistics support should be compiled in. + * + * The statistics is useful for debugging and to show the user. + * + * \hideinitializer + */ +#ifndef UIP_CONF_STATISTICS +#define UIP_STATISTICS 0 +#else /* UIP_CONF_STATISTICS */ +#define UIP_STATISTICS UIP_CONF_STATISTICS +#endif /* UIP_CONF_STATISTICS */ + +/** + * Determines if logging of certain events should be compiled in. + * + * This is useful mostly for debugging. The function uip_log() + * must be implemented to suit the architecture of the project, if + * logging is turned on. + * + * \hideinitializer + */ +#ifndef UIP_CONF_LOGGING +#define UIP_LOGGING 0 +#else /* UIP_CONF_LOGGING */ +#define UIP_LOGGING UIP_CONF_LOGGING +#endif /* UIP_CONF_LOGGING */ + +/** + * Broadcast support. + * + * This flag configures IP broadcast support. This is useful only + * together with UDP. + * + * \hideinitializer + * + */ +#ifndef UIP_CONF_BROADCAST +#define UIP_BROADCAST 0 +#else /* UIP_CONF_BROADCAST */ +#define UIP_BROADCAST UIP_CONF_BROADCAST +#endif /* UIP_CONF_BROADCAST */ + +/** + * Print out a uIP log message. + * + * This function must be implemented by the module that uses uIP, and + * is called by uIP whenever a log message is generated. + */ +void uip_log(char *msg); + +/** + * The link level header length. + * + * This is the offset into the uip_buf where the IP header can be + * found. For Ethernet, this should be set to 14. For SLIP, this + * should be set to 0. + * + * \hideinitializer + */ +#ifdef UIP_CONF_LLH_LEN +#define UIP_LLH_LEN UIP_CONF_LLH_LEN +#else /* UIP_CONF_LLH_LEN */ +#define UIP_LLH_LEN 14 +#endif /* UIP_CONF_LLH_LEN */ + +#if 0 +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \name CPU architecture configuration + * @{ + * + * The CPU architecture configuration is where the endianess of the + * CPU on which uIP is to be run is specified. Most CPUs today are + * little endian, and the most notable exception are the Motorolas + * which are big endian. The BYTE_ORDER macro should be changed to + * reflect the CPU architecture on which uIP is to be run. + */ + +/** + * The byte order of the CPU architecture on which uIP is to be run. + * + * This option can be either BIG_ENDIAN (Motorola byte order) or + * LITTLE_ENDIAN (Intel byte order). + * + * \hideinitializer + */ +#ifdef UIP_CONF_BYTE_ORDER +#define UIP_BYTE_ORDER UIP_CONF_BYTE_ORDER +#else /* UIP_CONF_BYTE_ORDER */ +#define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN +#endif /* UIP_CONF_BYTE_ORDER */ +#endif + +/** @} */ +/*------------------------------------------------------------------------------*/ + +/** + * \name Appication specific configurations + * @{ + * + * An uIP application is implemented using a single application + * function that is called by uIP whenever a TCP/IP event occurs. The + * name of this function must be registered with uIP at compile time + * using the UIP_APPCALL definition. + * + * uIP applications can store the application state within the + * uip_conn structure by specifying the type of the application + * structure by typedef:ing the type uip_tcp_appstate_t and uip_udp_appstate_t. + * + * The file containing the definitions must be included in the + * uipopt.h file. + * + * The following example illustrates how this can look. + \code + +void httpd_appcall(void); +#define UIP_APPCALL httpd_appcall + +struct httpd_state { + u8_t state; + u16_t count; + char *dataptr; + char *script; +}; +typedef struct httpd_state uip_tcp_appstate_t + \endcode + */ + +/** + * \var #define UIP_APPCALL + * + * The name of the application function that uIP should call in + * response to TCP/IP events. + * + */ + +/** + * \var typedef uip_tcp_appstate_t + * + * The type of the application state that is to be stored in the + * uip_conn structure. This usually is typedef:ed to a struct holding + * application state information. + */ + +/** + * \var typedef uip_udp_appstate_t + * + * The type of the application state that is to be stored in the + * uip_conn structure. This usually is typedef:ed to a struct holding + * application state information. + */ +/** @} */ +/** @} */ + +#endif /* __UIPOPT_H__ */ diff --git a/brcm_iscsi_uio/src/unix/Makefile.am b/brcm_iscsi_uio/src/unix/Makefile.am new file mode 100644 index 0000000..56ee4f8 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/Makefile.am @@ -0,0 +1,39 @@ +SUBDIRS= libs + +INCLUDES = -I${top_srcdir}/src/uip \ + -I${top_srcdir}/src/apps/brcm-iscsi \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/include \ + -I${top_srcdir}/src/unix/libs/ + +bin_PROGRAMS = brcm_iscsiuio + +brcm_iscsiuio_SOURCES = build_date.c \ + main.c \ + clock-arch.c \ + logger.c \ + uevent.c \ + nic.c \ + nic_id.c \ + nic_vlan.c \ + nic_nl.c \ + nic_utils.c \ + packet.c \ + iscsid_ipc.c + +brcm_iscsiuio_CFLAGS = $(AM_CFLAGS) \ + $(LIBNL_CFLAGS) \ + -DBYTE_ORDER=@ENDIAN@ + +brcm_iscsiuio_LDFLAGS= $(AM_LDADD) \ + -ldl \ + -rdynamic \ + $(LIBNL_LIBS) \ + -lpthread + +brcm_iscsiuio_LDADD = ${top_srcdir}/src/uip/libbrcm_iscsi_uip.a \ + ${top_srcdir}/src/apps/dhcpc/libbrcm_apps_dhcpc.a\ + ${top_srcdir}/src/apps/brcm-iscsi/libbrcm_apps_brcm_iscsi.a \ + ${top_srcdir}/src/unix/libs/libbrcm_iscsiuio_hw_cnic.a + +brcm_iscsiuio_YFLAGS = -d diff --git a/brcm_iscsi_uio/src/unix/build_date.c b/brcm_iscsi_uio/src/unix/build_date.c new file mode 100644 index 0000000..6018481 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/build_date.c @@ -0,0 +1 @@ +char *build_date ="Thu Dec 10 05:30:25 PST 2009"; diff --git a/brcm_iscsi_uio/src/unix/build_date.h b/brcm_iscsi_uio/src/unix/build_date.h new file mode 100644 index 0000000..3033db7 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/build_date.h @@ -0,0 +1 @@ +char *build_date; diff --git a/brcm_iscsi_uio/src/unix/clock-arch.c b/brcm_iscsi_uio/src/unix/clock-arch.c new file mode 100644 index 0000000..d140aaf --- /dev/null +++ b/brcm_iscsi_uio/src/unix/clock-arch.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: clock-arch.c,v 1.2 2006/06/12 08:00:31 adam Exp $ + */ + +/** + * \file + * Implementation of architecture-specific clock functionality + * \author + * Adam Dunkels + */ + +#include "clock-arch.h" +#include + +/*---------------------------------------------------------------------------*/ +clock_time_t +clock_time(void) +{ + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} +/*---------------------------------------------------------------------------*/ diff --git a/brcm_iscsi_uio/src/unix/clock-arch.h b/brcm_iscsi_uio/src/unix/clock-arch.h new file mode 100644 index 0000000..e51eee9 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/clock-arch.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: clock-arch.h,v 1.2 2006/06/12 08:00:31 adam Exp $ + */ + +#ifndef __CLOCK_ARCH_H__ +#define __CLOCK_ARCH_H__ + +typedef int clock_time_t; +#define CLOCK_CONF_SECOND 1000 + +#endif /* __CLOCK_ARCH_H__ */ diff --git a/brcm_iscsi_uio/src/unix/iscsid_ipc.c b/brcm_iscsi_uio/src/unix/iscsid_ipc.c new file mode 100644 index 0000000..829f7ed --- /dev/null +++ b/brcm_iscsi_uio/src/unix/iscsid_ipc.c @@ -0,0 +1,403 @@ +/* iscsi_ipc.c: Generic NIC management/utility functions + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFX "iscsi_ipc " + +/* TODO fix me */ +#define IFNAMSIZ 15 + +#include "nic.h" +#include "nic_utils.h" +#include "options.h" +#include "mgmt_ipc.h" +#include "iscsid_ipc.h" +#include "uip.h" +#include "uip_mgmt_ipc.h" + +#include "logger.h" +#include "uip.h" + +/* private iscsid options stucture */ +struct iscsid_options { + int fd; + pthread_t thread; +}; + +/****************************************************************************** + * Globals + ******************************************************************************/ +static struct iscsid_options iscsid_opts = { + .fd = INVALID_FD, + .thread = INVALID_THREAD, +}; + +/****************************************************************************** + * iscsid Functions + ******************************************************************************/ +static void * parse_iface_thread(void * arg) +{ + int rc; + nic_t *nic; + nic_interface_t *nic_iface; + char *transport_name; + size_t transport_name_size; + nic_lib_handle_t *handle; + struct in_addr addr; + iscsid_uip_broadcast_t *data; + + data = (iscsid_uip_broadcast_t *) arg; + + /* Check if we can find the NIC device using the netdev + * name */ + rc = from_netdev_name_find_nic(data->u.iface_rec.rec.netdev, &nic); + + if(rc != 0) { + LOG_INFO(PFX "Couldn't find interface: %s, creating NIC", + data->u.iface_rec.rec.netdev); + + nic = nic_init(); + if(nic == NULL) { + LOG_ERR(PFX "Could not allocate space for NIC '%s'", + data->u.iface_rec.rec.netdev); + goto done; + } + + nic->config_device_name = strdup(data->u.iface_rec.rec.netdev); + if(nic->config_device_name == NULL) { + LOG_ERR(PFX "Could not allocate config device name " + "for NIC '%s'", + data->u.iface_rec.rec.netdev); + goto done; + } + nic->flags |= NIC_CONFIG_NAME_MALLOC; + nic->log_name = nic->config_device_name; + + } else { + LOG_INFO(PFX "Found interface: %s, using existing NIC", + data->u.iface_rec.rec.netdev); + } + + prepare_nic(nic); + + /* Sanity Check to ensure the transport names are the same */ + handle = nic->nic_library; + if(handle != NULL) { + (*handle->ops->lib_ops.get_transport_name)(&transport_name, + &transport_name_size); + + if(strncmp(transport_name, + data->u.iface_rec.rec.transport_name, + transport_name_size) != 0) { + LOG_ERR(PFX "%s Transport name is not equal " + "expected: %s got: %s", + nic->log_name, + data->u.iface_rec.rec.transport_name, + transport_name); + + } + } + + /* Create the network interface if it doesn't exist */ + nic_iface = nic_find_nic_iface(nic, 0); + if(nic_iface == NULL) { + /* Create the vlan interface */ + nic_iface = nic_iface_init(); + + if(nic_iface == NULL) { + LOG_ERR(PFX "Could not allocate nic_iface", + nic_iface); + goto done; + } + + nic_add_nic_iface(nic, nic_iface); + } + + /* Check to see if this is using DHCP or if this is + * a static IPv4 address. This is done by checking + * if the IP address is equal to 0.0.0.0. If it is + * then the user has specified to use DHCP. If not + * then the user has spcicied to use a static IP address + * an the default netmask will be used */ + inet_aton(data->u.iface_rec.rec.ipaddress, + (struct in_addr *) &addr); + + if(memcmp(&addr, + all_zeroes_addr4, sizeof(all_zeroes_addr4)) == 0) { + LOG_INFO(PFX "%s: configuring using DHCP", + nic->log_name); + nic_iface->ustack.ip_config = IP_CONFIG_DHCP; + } else { + struct in_addr netmask; + memcpy(&nic_iface->ustack.hostaddr, &addr, sizeof(addr)); + + LOG_INFO(PFX "%s: configuring using static IP\n" + " IPv4 address :%s", + nic->log_name, inet_ntoa(addr)) + netmask.s_addr = calculate_default_netmask(addr.s_addr); + LOG_INFO(PFX " netmask :%s", inet_ntoa(netmask)); + + memcpy(&nic_iface->ustack.netmask, + &netmask, + sizeof(netmask.s_addr)); + nic_iface->ustack.ip_config = IP_CONFIG_STATIC; + } + + /* Enable the NIC */ + rc = nic_enable(nic); + if(rc != 0) + goto done; + + LOG_INFO(PFX "ISCSID_UIP_IPC_GET_IFACE: command: %x " + "name: %s, netdev: %s ipaddr: %s transport_name:%s", + data->header.command, data->u.iface_rec.rec.name, + data->u.iface_rec.rec.netdev, + inet_ntoa(addr), + data->u.iface_rec.rec.transport_name); + +done: + free(data); + + pthread_exit(NULL); +} + +/** + * process_iscsid_broadcast() - This function is used to process the + * broadcast messages from iscsid + */ +int process_iscsid_broadcast(int s2) +{ + int rc = 0; + iscsid_uip_broadcast_t *data; + iscsid_uip_rsp_t rsp; + FILE *fd; + size_t size; + iscsid_uip_cmd_e cmd; + uint32_t payload_len; + pthread_t tmp_thread; + + fd = fdopen(s2, "r+"); + if (fd == NULL) { + LOG_ERR(PFX "Couldn't open file descriptor: %d(%s)", + errno, strerror(errno)); + return -EIO; + } + + /* This will be freed by parse_iface_thread() */ + data = (iscsid_uip_broadcast_t *) malloc(sizeof(*data)); + if(data == NULL) { + LOG_ERR(PFX "Couldn't allocate memory for iface data"); + return -ENOMEM; + } + + size = fread(data, sizeof(iscsid_uip_broadcast_header_t), 1, fd); + if (size == -1) { + LOG_ERR(PFX "Could not read request: %d(%s)", + errno, strerror(errno)); + rc = ferror(fd); + goto error; + } + + cmd = data->header.command; + payload_len = data->header.payload_len; + + LOG_DEBUG(PFX "recv iscsid request: cmd: %d, payload_len: %d", + cmd, payload_len); + + size = fread(&data->u.iface_rec, payload_len, 1, fd); + if (size == -1) { + LOG_ERR(PFX "Could not read data: %d(%s)", + errno, strerror(errno)); + goto error; + } + + switch (cmd) { + case ISCSID_UIP_IPC_GET_IFACE: + /* This thread will be thrown away when completed */ + rc = pthread_create(&tmp_thread, NULL, + parse_iface_thread, data); + if (rc != 0) { + LOG_ERR(PFX "Couldn't start parse_iface thread rc=%d", + rc); + goto error; + } + + break; + default: + LOG_WARN(PFX "Unknown iscsid broadcast command: %x", + data->header.command); + free(data); + break; + } + + /* Send a response back to iscsid to tell it the operation succeeded */ + rsp.command = cmd; + rsp.err = ISCSID_UIP_MGMT_IPC_OK; + + size = fwrite(&rsp, sizeof(rsp), 1, fd); + if (size == -1) { + LOG_ERR(PFX "Could not send response: %d(%s)", + errno, strerror(errno)); + rc = ferror(fd); + } + +error: + fclose(fd); + + return rc; +} + +static void iscsid_loop_close(void *arg) +{ + close(iscsid_opts.fd); + + LOG_INFO(PFX "Admin socket closed"); +} + +/** + * iscsid_loop() - This is the function which will process the broadcast + * messages from iscsid + * + */ +static void *iscsid_loop(void *arg) +{ + int rc; + sigset_t set; + + pthread_cleanup_push(iscsid_loop_close, arg); + + sigfillset(&set); + rc = pthread_sigmask(SIG_BLOCK, &set, NULL); + if (rc != 0) { + LOG_ERR(PFX "Couldn't set signal mask for the iscisd listening thread"); + } + + LOG_DEBUG(PFX "Started iscsid listening thread"); + + while (1) { + struct sockaddr_un remote; + socklen_t sock_len; + int s2; + + sock_len = sizeof(remote); + s2 = accept(iscsid_opts.fd, + (struct sockaddr *)&remote, &sock_len); + if (s2 == -1) { + if (errno == EAGAIN) { + LOG_DEBUG("Got EAGAIN from accept"); + sleep(1); + continue; + } else if (errno == EINTR) { + /* The program is terminating, time to exit */ + break; + } + + LOG_ERR(PFX "Could not accept: %d(%s)", + s2, strerror(errno)); + continue; + } + + process_iscsid_broadcast(s2); + } + + pthread_cleanup_pop(0); + + LOG_ERR(PFX "exit iscsid listening thread"); + + pthread_exit(NULL); +} + +/****************************************************************************** + * Initialize/Cleanup routines + ******************************************************************************/ +/** + * iscsid_init() - This function will setup the thread used to listen for + * the iscsid broadcast messages + * @return 0 on success, <0 on failure + */ +int iscsid_init() +{ + int rc; + struct sockaddr_un addr; + + iscsid_opts.fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (iscsid_opts.fd < 0) { + LOG_ERR(PFX "Can not create IPC socket"); + return iscsid_opts.fd; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + memcpy((char *) &addr.sun_path + 1, ISCSID_UIP_NAMESPACE, + strlen(ISCSID_UIP_NAMESPACE)); + + rc = bind(iscsid_opts.fd, + (struct sockaddr *) &addr, sizeof(addr)); + if ( rc < 0) { + LOG_ERR(PFX "Can not bind IPC socket: %s", strerror(errno)); + goto error; + } + + rc = listen(iscsid_opts.fd, 32); + if ( rc < 0) { + LOG_ERR(PFX "Can not listen IPC socket: %s", strerror(errno)); + goto error; + } + + rc = pthread_create(&iscsid_opts.thread, NULL, iscsid_loop, NULL); + if (rc != 0) { + LOG_ERR(PFX "Could not start iscsid listening thread rc=%d", rc); + goto error; + } + + return 0; + +error: + close(iscsid_opts.fd); + iscsid_opts.fd = INVALID_FD; + + return rc; +} + +/** + * iscsid_cleanup() - This is called when stoping the thread listening + * for the iscsid broadcast messages + */ +void iscsid_cleanup() +{ + int rc; + + if (iscsid_opts.fd != INVALID_FD) { + rc = pthread_cancel(iscsid_opts.thread); + if (rc != 0) { + LOG_ERR("Could not cancel iscsid listening thread: %s", + strerror(rc)); + } + + rc = pthread_join(iscsid_opts.thread, NULL); + if (rc != 0) { + LOG_ERR("Could not wait for the iscsid listenging thread: %s", + strerror(rc)); + } + } + + LOG_INFO(PFX "iscsid listening thread has shutdown"); +} + diff --git a/brcm_iscsi_uio/src/unix/iscsid_ipc.h b/brcm_iscsi_uio/src/unix/iscsid_ipc.h new file mode 100644 index 0000000..e42266d --- /dev/null +++ b/brcm_iscsi_uio/src/unix/iscsid_ipc.h @@ -0,0 +1,24 @@ +/* uip_ipc.h: Generic NIC management/utility functions + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#ifndef __ISCSID_IPC_H__ +#define __ISCSID_IPC_H__ + +#include "uip.h" +#include "mgmt_ipc.h" + +mgmt_ipc_err_e iscsid_connect(int *fd); +int iscsid_get_ipaddr(int fd, uip_ip4addr_t *ipaddr); + +int iscsid_init(); +void iscsid_cleanup(); + +#endif /* __ISCSID_IPC_H__ */ diff --git a/brcm_iscsi_uio/src/unix/libs/Makefile.am b/brcm_iscsi_uio/src/unix/libs/Makefile.am new file mode 100644 index 0000000..dd843d3 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/libs/Makefile.am @@ -0,0 +1,23 @@ +# src/unix/libs/Makefile.am: CNIC UIO libs Makefile.am +# +# Copyright (c) 2004-2008 Broadcom Corporation +# +# 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. +# +# Written by: Benjamin Li (benli@broadcom.com) +# + +INCLUDES = -I${top_srcdir}/src/uip \ + -I${top_srcdir}/src/unix \ + -I${top_srcdir}/src/unix/libs \ + -I${top_srcdir}/src/apps/dhcpc \ + -I${top_srcdir}/include + +noinst_LIBRARIES = libbrcm_iscsiuio_hw_cnic.a + +libbrcm_iscsiuio_hw_cnic_a_SOURCES = ../build_date.c \ + cnic.c \ + bnx2.c \ + bnx2x.c diff --git a/brcm_iscsi_uio/src/unix/libs/bnx2.c b/brcm_iscsi_uio/src/unix/libs/bnx2.c new file mode 100644 index 0000000..df99152 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/libs/bnx2.c @@ -0,0 +1,1107 @@ +/* bnx2.c: CNIC user space driver + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "build_date.h" +#include "bnx2.h" +#include "cnic.h" +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" +#include "options.h" + +#define PFX "bnx2 " + +/* Foward struct declarations */ +struct nic_ops bnx2_op; + +/* Determine is the CNIC kernel module is loaded or not */ +int lib_bnx2_loaded = 0; + +/******************************************************************************* + * NIC Library Strings + ******************************************************************************/ +static const char library_name[] = "bnx2"; +static const char library_version[] = PACKAGE_VERSION; +static const char library_uio_name[] = "bnx2_cnic"; + +/* Template strings used to read RX parameters from sysfs */ +static const char cnic_sysfs_buf_size_template[] = "/sys/class/uio/uio%d/device/uio_buf_size"; +static const char cnic_sysfs_rx_ring_size_template[] = "/sys/class/uio/uio%d/device/uio_rx_ring_size"; +static const char cnic_sysfs_uio_event_template[] = "/sys/class/uio/uio%d/event"; + +/* The name that should be returned from /sys/class/uio/uio0/name */ +static const char cnic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name"; +static const char cnic_uio_sysfs_name[] = "bnx2_cnic"; + +/******************************************************************************* + * String constants used to display human readable adapter name + ******************************************************************************/ +static const char brcm_5706C[] = "Broadcom NetXtreme II BCM5706 1000Base-T"; +static const char hp_NC370T[] = "HP NC370T Multifunction Gigabit Server Adapter"; +static const char hp_NC370I[] = "HP NC370i Multifunction Gigabit Server Adapter"; +static const char brcm_5706S[] = "Broadcom NetXtreme II BCM5706 1000Base-SX"; +static const char hp_NC370F[] = "HP NC370F Multifunction Gigabit Server Adapter"; +static const char brcm_5708C[] = "Broadcom NetXtreme II BCM5708 1000Base-T"; +static const char brcm_5708S[] = "Broadcom NetXtreme II BCM5708 1000Base-SX"; +static const char brcm_5709C[] = "Broadcom NetXtreme II BCM5709 1000Base-T"; +static const char brcm_5709S[] = "Broadcom NetXtreme II BCM5709 1000Base-SX"; +static const char brcm_5716C[] = "Broadcom NetXtreme II BCM5716 1000Base-T"; +static const char brcm_5716S[] = "Broadcom NetXtreme II BCM5716 1000Base-SX"; + +/******************************************************************************* + * PCI ID constants + ******************************************************************************/ +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_DEVICE_ID_NX2_5709 0x1639 +#define PCI_DEVICE_ID_NX2_5709S 0x163a +#define PCI_DEVICE_ID_NX2_5706 0x164a +#define PCI_DEVICE_ID_NX2_5708 0x164c +#define PCI_DEVICE_ID_NX2_5706S 0x16aa +#define PCI_DEVICE_ID_NX2_5708S 0x16ac + +#define PCI_VENDOR_ID_HP 0x103c + +#define PCI_ANY_ID (~0) + +/* This is the table used to match PCI vendor and device ID's to the + * human readable string names of the devices */ +static const struct pci_device_id bnx2_pci_tbl[] = { + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, + PCI_VENDOR_ID_HP, 0x3101, hp_NC370T }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, + PCI_VENDOR_ID_HP, 0x3106, hp_NC370I }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, + PCI_ANY_ID, PCI_ANY_ID, brcm_5706S }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708, + PCI_ANY_ID, PCI_ANY_ID, brcm_5708C }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, + PCI_VENDOR_ID_HP, 0x3102, hp_NC370F }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, + PCI_ANY_ID, PCI_ANY_ID, brcm_5706S }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708S, + PCI_ANY_ID, PCI_ANY_ID, brcm_5708S }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709, + PCI_ANY_ID, PCI_ANY_ID, brcm_5709C }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, + PCI_ANY_ID, PCI_ANY_ID, brcm_5709S }, + { PCI_VENDOR_ID_BROADCOM, 0x163b, + PCI_ANY_ID, PCI_ANY_ID, brcm_5716C }, + { PCI_VENDOR_ID_BROADCOM, 0x163c, + PCI_ANY_ID, PCI_ANY_ID, brcm_5716S }, +}; + +/******************************************************************************* + * bnx2 Library Functions + ******************************************************************************/ +/** + * bnx2_get_library_name() - Used to get the name of this NIC libary + * @param name - This function will return the pointer to this NIC + * library name + * @param name_size + */ +static void bnx2_get_library_name(char **name, + size_t *name_size) +{ + *name = (char *)library_name; + *name_size = sizeof(library_name); +} + +/** + * bnx2_get_library_version() - Used to get the version string of this + * NIC libary + * @param version - This function will return the pointer to this NIC + * library version string + * @param version_size - This will be set with the version size + */ +static void bnx2_get_library_version(char **version, + size_t *version_size) +{ + *version = (char *) library_version; + *version_size = sizeof(library_version); +} + +/** + * bnx2_get_build_date() - Used to get the build date string of this library + * @param version - This function will return the pointer to this NIC + * library build date string + * @param version_size - This will be set with the build date string size + */ +static void bnx2_get_build_date(char **build, + size_t *build_size) +{ + *build = (char *) build_date; + *build_size = sizeof(build_date); +} + +/** + * bnx2_get_transport_name() - Used to get the transport name associated + * with this this NIC libary + * @param transport_name - This function will return the pointer to this NIC + * library's associated transport string + * @param transport_name_size - This will be set with the transport name size + */ +static void bnx2_get_transport_name(char **transport_name, + size_t *transport_name_size) +{ + *transport_name = (char *) bnx2i_library_transport_name; + *transport_name_size = bnx2i_library_transport_name_size; +} + +/** + * bnx2_get_uio_name() - Used to get the uio name associated with this this + * NIC libary + * @param uio_name - This function will return the pointer to this NIC + * library's associated uio string + * @param transport_name_size - This will be set with the uio name size + */ +static void bnx2_get_uio_name(char **uio_name, + size_t *uio_name_size) +{ + *uio_name = (char *) library_uio_name; + *uio_name_size = sizeof(library_uio_name); +} + +/** + * bnx2_get_pci_table() - Used to get the PCI table for this NIC libary + * to determine which NIC's based off of PCI ID's + * are supported + * @param table - This function will return the pointer to the PCI table + * @param entries - This function will return the number of entries in the NIC + * library's PCI table + */ +static void bnx2_get_pci_table(struct pci_device_id **table, + uint32_t *entries) +{ + *table = (struct pci_device_id *) bnx2_pci_tbl; + *entries = (uint32_t) (sizeof(bnx2_pci_tbl)/sizeof(bnx2_pci_tbl[0])); +} + +/** + * bnx2_get_ops() - Used to get the NIC library op table + * @param op - The op table of this NIC library + */ +struct nic_ops * bnx2_get_ops() +{ + return &bnx2_op; +} + +/******************************************************************************* + * bnx2 Utility Functions + ******************************************************************************/ +/******************************************************************************* + * Utility Functions Used to read register from the CNIC device + ******************************************************************************/ +static void bnx2_wr32(bnx2_t *bp, __u32 off, __u32 val) +{ + *((volatile __u32 *)(bp->reg + off)) = val; +} + +static void bnx2_wr16(bnx2_t *bp, __u32 off, __u16 val) +{ + *((volatile __u16 *)(bp->reg + off)) = val; +} + +static __u32 bnx2_rd32(bnx2_t *bp, __u32 off) +{ + return *((volatile __u32 *)(bp->reg + off)); +} + +static int bnx2_reg_sync(bnx2_t *bp, __u32 off, __u16 length) +{ + return msync(bp->reg + off, length, MS_SYNC); +} + +/** + * bnx2_get_chip_id() - Used to retrive the chip ID from the nic + * @param dev - Device used to determin NIC type + * @return Chip ID read from the MISC ID register + */ +static int bnx2_get_chip_id(bnx2_t *bp) +{ + return bnx2_rd32(bp, BNX2_MISC_ID); +} + +/** + * bnx2_uio_verify() + * + */ +static int bnx2_uio_verify(nic_t *nic) +{ + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + char temp_path[sizeof(cnic_uio_sysfs_name_tempate) + 8]; + int rc = 0; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + cnic_uio_sysfs_name_tempate, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if(rc != 0) + { + goto error; + } + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while(*raw_tmp != '\n') + raw_tmp++; + *raw_tmp = '\0'; + + if (strncmp(raw, cnic_uio_sysfs_name, + sizeof(cnic_uio_sysfs_name)) != 0) { + LOG_ERR(PFX "%s: uio names not equal: " + "expecting %s got %s from %s", + nic->log_name, cnic_uio_sysfs_name, raw, temp_path); + rc = -EIO; + } + + free(raw); + + LOG_INFO(PFX "%s: Verified is a cnic_uio device", nic->log_name); + + error: + return rc; +} + +/******************************************************************************* + * bnx2 Utility Functions to get to the hardware consumer indexes + ******************************************************************************/ +static __u16 bnx2_get_rx_msix(bnx2_t *bp) +{ + struct status_block_msix *sblk = bp->status_blk.msix; + __u16 rx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + rx_cons = sblk->status_rx_quick_consumer_index; + if ((rx_cons & (MAX_RX_DESC_CNT)) == (MAX_RX_DESC_CNT)) + rx_cons++; + + return rx_cons; +} + +static __u16 bnx2_get_rx_msi(bnx2_t *bp) +{ + struct status_block *sblk = bp->status_blk.msi; + __u16 rx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + rx_cons = CNIC_SBLK_EVEN_IDX(sblk->rx2); + if ((rx_cons & (MAX_RX_DESC_CNT)) == (MAX_RX_DESC_CNT)) + rx_cons++; + + return rx_cons; +} + +static __u16 bnx2_get_tx_msix(bnx2_t *bp) +{ + struct status_block_msix *sblk = bp->status_blk.msix; + __u16 tx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + tx_cons = sblk->status_tx_quick_consumer_index; + if ((tx_cons & (MAX_TX_DESC_CNT)) == (MAX_TX_DESC_CNT)) + tx_cons++; + + return tx_cons; +} + +static __u16 bnx2_get_tx_msi(bnx2_t *bp) +{ + struct status_block *sblk = bp->status_blk.msi; + __u16 tx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + tx_cons = CNIC_SBLK_EVEN_IDX(sblk->tx2); + if ((tx_cons & (MAX_TX_DESC_CNT)) == (MAX_TX_DESC_CNT)) + tx_cons++; + + return tx_cons; +} + +typedef enum { + CNIC_VLAN_STRIPPING_ENABLED = 1, + CNIC_VLAN_STRIPPING_DISABLED = 2, +} CNIC_VLAN_STRIPPING_MODE; + +/** + * bnx2_strip_vlan_enabled() - This will query the device to determine whether + * VLAN tag stripping is enabled or not + * @param dev - device to check stripping or not + * @ return CNIC_VLAN_STRIPPING_ENABLED stripping is enabled + * CNIC_VLAN_STRIPPING_DISABLED stripping is not enabled + */ +static CNIC_VLAN_STRIPPING_MODE bnx2_strip_vlan_enabled(bnx2_t *bp) +{ + uint32_t val; + + val = bnx2_rd32(bp, BNX2_EMAC_RX_MODE); + + if(val & BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG) + return CNIC_VLAN_STRIPPING_DISABLED; + else + return CNIC_VLAN_STRIPPING_ENABLED; +} + +/** + * bnx2_alloc() - Used to allocate a CNIC structure + */ +static bnx2_t * bnx2_alloc(nic_t *nic) +{ + bnx2_t *bp = malloc(sizeof(*bp)); + if(bp == NULL) + { + LOG_ERR(PFX "%s: Could not allocate CNIC space", + nic->log_name); + return NULL; + } + + /* Clear out the CNIC contents */ + memset(bp, 0, sizeof(*bp)); + + bp->flags = CNIC_UIO_TX_HAS_SENT; + + bp->parent = nic; + nic->priv = (void *) bp; + + return bp; +} + +/** + * bnx2_open() - This will initialize all the hardware resources + * @param dev - The struct nic device to open + * @return 0 on success, on failure a errno will be returned + */ +static int bnx2_open(nic_t *nic) +{ + bnx2_t *bp; + struct stat uio_stat; + int i, rc; + __u32 val; + uint32_t tx_cid; + __u32 msix_vector = 0; + + /* Sanity Check: validate the parameters */ + if(nic == NULL) { + LOG_ERR(PFX "bnx2_open(): nic == NULL"); + return -EINVAL; + } + + bp = bnx2_alloc(nic); + if(bp == NULL) + return -ENOMEM; + + while(nic->fd < 0) { + nic->fd = open(nic->uio_device_name, O_RDWR | O_NONBLOCK); + if (nic->fd != INVALID_FD) { + LOG_ERR(PFX "%s: uio device has been brought up via pid: %d on fd: %d", + nic->uio_device_name, getpid(), nic->fd); + + rc = bnx2_uio_verify(nic); + if(rc != 0) + continue; + + break; + } else { + if( lib_bnx2_loaded == 0) { + LOG_ERR(PFX "%s: Could not open device: %s, " + "awaiting for the device to appear", + nic->log_name, nic->uio_device_name); + + /* Time to wait for the device to come up */ + pthread_mutex_lock(&nic->uio_wait_mutex); + pthread_cond_wait(&nic->uio_wait_cond, + &nic->uio_wait_mutex); + pthread_mutex_unlock(&nic->uio_wait_mutex); + + /* udev might not have created the file yet */ + sleep(2); + + lib_bnx2_loaded = 1; + } else { + /* udev might not have created the file yet */ + sleep(2); + } + } + } + if (fstat(nic->fd, &uio_stat) < 0) { + LOG_ERR(PFX "%s: Could not fstat device", nic->log_name); + return -ENODEV; + } + nic->uio_minor = minor(uio_stat.st_rdev); + + /* TODO: hardcoded with the cnic driver */ + bp->rx_ring_size = 3; + bp->rx_buffer_size = 0x400; + + LOG_DEBUG(PFX "%s: using rx ring size: %d, rx buffer size: %d", + nic->log_name, bp->rx_ring_size, bp->rx_buffer_size); + + /* Determine the number of UIO events that have already occured */ + rc = detemine_initial_uio_events(nic, &nic->intr_count); + if(rc != 0) { + LOG_ERR("Could not determine the number ofinitial UIO events"); + nic->intr_count = 0; + } + + /* Allocate space for rx ring pointer */ + bp->rx_ring = malloc(sizeof(struct l2_fhdr *) * bp->rx_ring_size); + if(bp->rx_ring == NULL) + { + LOG_ERR(PFX "%s: Could not allocate space for rx_ring", + nic->log_name); + goto error_alloc_rx_ring; + } + mlock(bp->rx_ring, sizeof(struct l2_fhdr *) * bp->rx_ring_size); + + /* Allocate space for rx pkt ring */ + bp->rx_pkt_ring = malloc(sizeof(void *) * bp->rx_ring_size); + if(bp->rx_pkt_ring == NULL) + { + LOG_ERR(PFX "%s: Could not allocate space for rx_pkt_ring", + nic->log_name); + goto error_alloc_rx_pkt_ring; + } + mlock(bp->rx_pkt_ring, sizeof(void *) * bp->rx_ring_size); + + bp->reg = mmap(NULL, 0x12800, PROT_READ | PROT_WRITE, MAP_SHARED, + nic->fd, (off_t) 0); + if (bp->reg == MAP_FAILED) { + LOG_INFO(PFX "%s: Couldn't mmap registers: %s", + nic->log_name, strerror(errno)); + bp->reg = NULL; + goto error_regs; + } + + msync(bp->reg, 0x12800, MS_SYNC); + LOG_DEBUG(PFX "Chip ID: %x", bnx2_get_chip_id(bp)); + + /* on a 5709 when using MSI-X the status block is at an offset */ + if(CHIP_NUM(bnx2_get_chip_id(bp)) == CHIP_NUM_5709) { + /* determine if we are using MSI-X */ + val = bnx2_rd32(bp, BNX2_TSCH_TSS_CFG); + if(val) { + /* We are in MSI-X mode */ + uint32_t base_cid = ((val >> 10) & 0x7ff) << 3; + msix_vector = (val >> 24) & 0xf; + + bp->status_blk_size = (128 * 9); + + tx_cid = base_cid + msix_vector - 1; + bp->flags |= CNIC_UIO_MSIX_ENABLED; + + bp->get_tx_cons = bnx2_get_tx_msix; + bp->get_rx_cons = bnx2_get_rx_msix; + + LOG_DEBUG(PFX "%s: tss_cfg: 0x%x tx cid: %d", + nic->log_name, val, tx_cid); + + LOG_INFO(PFX "%s: detected using MSI-X vector: %d", + nic->log_name, msix_vector); + } else { + /* We are not in MSI-X mode */ + bp->status_blk_size = 64; + tx_cid = 20; + + bp->get_tx_cons = bnx2_get_tx_msi; + bp->get_rx_cons = bnx2_get_rx_msi; + } + } else { + bp->status_blk_size = 64; + tx_cid = 20; + + bp->get_tx_cons = bnx2_get_tx_msi; + bp->get_rx_cons = bnx2_get_rx_msi; + } + + bp->sblk_map = mmap(NULL, bp->status_blk_size, + PROT_READ | PROT_WRITE, MAP_SHARED, + nic->fd, (off_t) getpagesize()); + if (bp->sblk_map == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap status block: %s", + nic->log_name, strerror(errno)); + goto error_sblk; + } + + if(bp->flags & CNIC_UIO_MSIX_ENABLED) { + uint8_t *status_blk = (uint8_t *) bp->sblk_map; + status_blk += (msix_vector * 128); + + bp->status_blk.msix = (struct status_block_msix *) status_blk; + + LOG_DEBUG(PFX "%s: msix initial cons: tx:%d rx:%d", + nic->log_name, + bp->status_blk.msix->status_tx_quick_consumer_index, + bp->status_blk.msix->status_rx_quick_consumer_index); + } else { + bp->status_blk.msi = (struct status_block *) bp->sblk_map; + + LOG_DEBUG(PFX "%s: msi initial tx:%d rx:%d", + nic->log_name, + CNIC_SBLK_EVEN_IDX(bp->status_blk.msi->tx2), + CNIC_SBLK_EVEN_IDX(bp->status_blk.msi->rx2)); + } + + bp->tx_ring = mmap(NULL, 2 * getpagesize(), + PROT_READ | PROT_WRITE, MAP_SHARED, nic->fd, + (off_t) 2 * getpagesize()); + if (bp->tx_ring == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap tx ring: %s", + nic->log_name, strerror(errno)); + bp->tx_ring = NULL; + goto error_tx_ring; + } + + bp->bufs = mmap(NULL, (bp->rx_ring_size + 1) * bp->rx_buffer_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, nic->fd, (off_t) 3 * getpagesize()); + if (bp->bufs == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap buffers: %s", + nic->log_name, strerror(errno)); + bp->bufs = NULL; + goto error_bufs; + } + + bp->tx_bidx_io = MB_GET_CID_ADDR(tx_cid) + BNX2_L2CTX_TX_HOST_BIDX; + bp->tx_bseq_io = MB_GET_CID_ADDR(tx_cid) + BNX2_L2CTX_TX_HOST_BSEQ; + LOG_INFO(PFX "%s: tx_bidx_io: 0x%x tx_bseq_io: 0x%x", + nic->log_name, + bp->tx_bidx_io, bp->tx_bseq_io); + + bp->rx_bidx_io = MB_GET_CID_ADDR(2) + BNX2_L2CTX_HOST_BDIDX; + bp->rx_bseq_io = MB_GET_CID_ADDR(2) + BNX2_L2CTX_HOST_BSEQ; + + bp->tx_cons = 0; + bp->tx_prod = 0; + bp->tx_pkt = bp->bufs; + + bp->rx_index = 0; + bp->rx_cons = 0; + bp->rx_prod = bp->rx_ring_size; + bp->rx_bseq = bp->rx_prod * bp->rx_buffer_size; + bnx2_wr16(bp, bp->rx_bidx_io, bp->rx_prod); + bnx2_wr32(bp, bp->rx_bseq_io, bp->rx_bseq); + + bnx2_reg_sync(bp, bp->rx_bidx_io, sizeof(__u16)); + bnx2_reg_sync(bp, bp->rx_bseq_io, sizeof(__u32)); + + for (i = 0; i < bp->rx_ring_size; i++) { + void *ptr = bp->bufs + (bp->rx_buffer_size * (i + 1)); + + bp->rx_ring[i] = (struct l2_fhdr *)ptr; + bp->rx_pkt_ring[i] = ptr + sizeof(struct l2_fhdr) + 2; + } + + /* Read the MAC address used for the iSCSI interface */ + val = bnx2_rd32(bp, BNX2_EMAC_MAC_MATCH4); + nic->mac_addr[0] = (__u8) (val >> 8); + nic->mac_addr[1] = (__u8) val; + + val = bnx2_rd32(bp, BNX2_EMAC_MAC_MATCH5); + nic->mac_addr[2] = (__u8) (val >> 24); + nic->mac_addr[3] = (__u8) (val >> 16); + nic->mac_addr[4] = (__u8) (val >> 8); + nic->mac_addr[5] = (__u8) val; + + LOG_INFO(PFX "%s: Using mac address: %2x:%2x:%2x:%2x:%2x:%2x", + nic->log_name, + nic->mac_addr[0], nic->mac_addr[1], nic->mac_addr[2], + nic->mac_addr[3], nic->mac_addr[4], nic->mac_addr[5]); + + /* Determine if Hardware VLAN tag stripping is enabled or not */ + if(CNIC_VLAN_STRIPPING_ENABLED == bnx2_strip_vlan_enabled(bp)) + { + nic->flags |= NIC_VLAN_STRIP_ENABLED; + } + + /* Prepare the multicast addresses */ + rc = enable_multicast(nic); + if(rc != 0) + goto error_bufs; + + msync(bp->reg, 0x12800, MS_SYNC); + LOG_INFO("%s: CNIC uio initialized", nic->log_name); + + return 0; + + error_bufs: + munmap(bp->tx_ring, 2 * getpagesize()); + + error_tx_ring: + munmap(bp->status_blk.msi, bp->status_blk_size); + + error_sblk: + munmap(bp->reg, 0x12800); + + error_regs: + munlock(bp->rx_pkt_ring, sizeof(void *) * bp->rx_ring_size); + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + + error_alloc_rx_pkt_ring: + munlock(bp->rx_ring, sizeof(struct l2_fhdr *) * bp->rx_ring_size); + free(bp->rx_ring); + bp->rx_ring = NULL; + + error_alloc_rx_ring: + + return errno; +} + +/** + * bnx2_uio_close_resources() - Used to free resource for the NIC/CNIC + * @param nic - NIC device to free resource + * @param graceful - whether to wait to close gracefully + * @return 0 on success, <0 on failure + */ +static int bnx2_uio_close_resources(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + int rc = 0; + + /* Remove the multicast addresses if added */ + if((nic->flags & NIC_ADDED_MULICAST) && + (graceful == ALLOW_GRACEFUL_SHUTDOWN)) + disable_multicast(nic); + + /* Check if there is an assoicated CNIC device */ + if(bp == NULL) { + LOG_WARN(PFX "%s: when closing resources there is " + "no assoicated bnx2", + nic->log_name); + return -EIO; + } + + /* Clean up allocated memory */ + if(bp->rx_ring != NULL) { + free(bp->rx_ring); + bp->rx_ring = NULL; + } + + if(bp->rx_pkt_ring != NULL) { + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + } + + /* Clean up mapped registers */ + if (bp->bufs != NULL) { + rc = munmap(bp->bufs, + (bp->rx_ring_size + 1) * bp->rx_buffer_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap bufs", + nic->log_name); + bp->bufs = NULL; + } + + if (bp->tx_ring != NULL) { + rc = munmap(bp->tx_ring, 2 * getpagesize()); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap tx_rings", + nic->log_name); + bp->tx_ring = NULL; + } + + if (bp->status_blk.msix != NULL || + bp->status_blk.msi != NULL) { + rc = munmap(bp->sblk_map, bp->status_blk_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap status block", + nic->log_name); + bp->sblk_map = NULL; + + bp->status_blk.msix = NULL; + bp->status_blk.msi = NULL; + } + + if (bp->reg != NULL) { + rc = munmap(bp->reg, 0x12800); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap regs", + nic->log_name); + bp->reg = NULL; + } + + if (nic->fd != INVALID_FD) { + rc = close(nic->fd); + if (rc != 0) { + LOG_WARN(PFX "%s: Couldn't close uio file descriptor: %d", + nic->log_name, nic->fd); + } else { + LOG_DEBUG(PFX "%s: Closed uio file descriptor: %d", + nic->log_name, nic->fd); + } + + nic->fd = INVALID_FD; + } else { + LOG_WARN(PFX "%s: Invalid uio file descriptor: %d", + nic->log_name, nic->fd); + } + + LOG_INFO(PFX "%s: Closed all resources", nic->log_name); + + return 0; +} + +/** + * bnx2_close() - Used to close the NIC device + * @param nic - NIC device to close + * @param graceful - whether to wait to close gracefully + * @return 0 if successful, <0 if there is an error + */ +static int bnx2_close(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + /* Sanity Check: validate the parameters */ + if(nic == NULL) { + LOG_ERR(PFX "bnx2_close(): nic == NULL"); + return -EINVAL; + } + + LOG_INFO(PFX "Closing NIC device: %s", nic->log_name); + + bnx2_uio_close_resources(nic, graceful); + + /* Free any named strings we might be holding onto */ + if(nic->flags & NIC_CONFIG_NAME_MALLOC) { + free(nic->config_device_name); + nic->flags &= ~NIC_CONFIG_NAME_MALLOC; + } + nic->config_device_name = NULL; + + if(nic->flags & NIC_UIO_NAME_MALLOC) { + free(nic->uio_device_name); + nic->uio_device_name = NULL; + + nic->flags &= ~NIC_UIO_NAME_MALLOC; + } + + return 0; +} + +static void bnx2_prepare_xmit_packet(nic_t *nic, + nic_interface_t *nic_iface, + struct packet *pkt) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + + /* Determine if we need to insert the VLAN tag */ + if((nic_iface->vlan_id != 0) && + (NIC_VLAN_STRIP_ENABLED & nic->flags)) + { + uint16_t insert_tpid = const_htons(UIP_ETHTYPE_8021Q); + uint16_t insert_vlan_id = htons((0x0FFF & nic_iface->vlan_id) + + ((0x000F & nic_iface->vlan_priority) << 12)); + + /* We need to reinsert the VLAN tag */ + memcpy(bp->tx_pkt, pkt->buf, 12); + memcpy(bp->tx_pkt + 12, &insert_tpid, 2); + memcpy(bp->tx_pkt + 14, &insert_vlan_id, 2); + memcpy(bp->tx_pkt + 16, pkt->buf + 12, pkt->buf_size - 12); + + pkt->buf_size = pkt->buf_size +4; + + LOG_DEBUG(PFX "%s: Inserted vlan tag id: 0x%x", + nic->log_name, + ntohs(insert_vlan_id)); + } else { + memcpy(bp->tx_pkt, pkt->buf, pkt->buf_size); + } + + msync(bp->tx_pkt, pkt->buf_size, MS_SYNC); +} + +/** + * bnx2_get_tx_pkt() - This function is used to a TX packet from the NIC + * @param nic - The NIC device to send the packet + * + */ +void * bnx2_get_tx_pkt(nic_t *nic) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + return bp->tx_pkt; +} + +/** + * bnx2_start_xmit() - This function is used to send a packet of data + * @param nic - The NIC device to send the packet + * @param len - the length of the TX packet + * + */ +void bnx2_start_xmit(nic_t *nic, size_t len) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + uint16_t ring_prod; + struct tx_bd *txbd; + + ring_prod = TX_RING_IDX(bp->tx_prod); + txbd = &bp->tx_ring[ring_prod]; + + txbd->tx_bd_mss_nbytes = len; + txbd->tx_bd_vlan_tag_flags = TX_BD_FLAGS_END | TX_BD_FLAGS_START; + + bp->tx_bseq += len; + bp->tx_prod = NEXT_TX_BD(bp->tx_prod); + + bnx2_wr16(bp, bp->tx_bidx_io, bp->tx_prod); + bnx2_wr32(bp, bp->tx_bseq_io, bp->tx_bseq); + + bnx2_reg_sync(bp, bp->tx_bidx_io, sizeof(__u16)); + bnx2_reg_sync(bp, bp->tx_bseq_io, sizeof(__u32)); + + LOG_DEBUG(PFX "%s: sent %d bytes using dev->tx_prod: %d", + nic->log_name, len, bp->tx_prod); +} + +/** + * bnx2_write() - Used to write the data to the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data to be sent on the wire + * @return 0 if successful, <0 if failed + */ +int bnx2_write(nic_t *nic, nic_interface_t *nic_iface, + packet_t *pkt) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + struct uip_stack *uip = &nic_iface->ustack; + + /* Sanity Check: validate the parameters */ + if(nic == NULL || nic_iface == NULL || pkt == NULL) { + LOG_ERR(PFX "%s: bnx2_write() nic == 0x%p || " + " nic_iface == 0x%p || " + " pkt == 0x%x", nic, nic_iface, pkt); + return -EINVAL; + } + + if(pkt->buf_size == 0) { + LOG_ERR(PFX "%s: Trying to transmitted 0 sized packet", + nic->log_name); + return -EINVAL; + } + + if(pthread_mutex_trylock(&nic->xmit_mutex) != 0) + { + LOG_ERR(PFX "%s: Dropped previous transmitted packet", + nic->log_name); + return -EINVAL; + } + + bnx2_prepare_xmit_packet(nic, nic_iface, + pkt); + bnx2_start_xmit(nic, pkt->buf_size); + + /* bump the bnx2 dev send statistics */ + nic->stats.tx.packets++; + nic->stats.tx.bytes += uip->uip_len; + + LOG_DEBUG(PFX "%s: transmitted %d bytes " + "dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bseq:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bseq); + + return 0; +} + +/** + * bnx2_read() - Used to read the data from the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data + * @return 0 if successful, <0 if failed + */ +static int bnx2_read(nic_t *nic, packet_t *pkt) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + int rc = 0; + uint16_t hw_cons, sw_cons; + + /* Sanity Check: validate the parameters */ + if(nic == NULL || pkt == NULL) { + LOG_ERR(PFX "%s: bnx2_write() nic == 0x%p || " + " pkt == 0x%x", nic, pkt); + return -EINVAL; + } + + hw_cons = bp->get_rx_cons(bp); + sw_cons = bp->rx_cons; + + if (sw_cons != hw_cons) { + uint8_t rx_index = bp->rx_index % 3; + struct l2_fhdr *rx_hdr = bp->rx_ring[rx_index]; + void *rx_pkt = bp->rx_pkt_ring[rx_index]; + int len; + uint16_t errors; + + LOG_DEBUG(PFX "%s: clearing rx interrupt: %d %d", + nic->log_name, + sw_cons, hw_cons); + + msync(rx_hdr, sizeof(struct l2_fhdr), MS_SYNC); + errors = ((rx_hdr->l2_fhdr_status & 0xffff0000) >> 16); + len = ((rx_hdr->l2_fhdr_vtag_len & 0xffff0000) >> 16) - 4; + + /* Doto query MTU size of physical device */ + /* Ensure len is valid */ + if(len > pkt->max_buf_size) + LOG_DEBUG(PFX "%s: bad BD length: %d", + nic->log_name, len); + + if ((errors == 0) && (len > 0)) { + msync(rx_pkt, len, MS_SYNC); + /* Copy the data */ + memcpy(pkt->buf, rx_pkt, len); + pkt->buf_size = len; + + /* Properly set the packet flags */ + /* check if there is VLAN tagging on the packet */ + if(rx_hdr->l2_fhdr_status & L2_FHDR_STATUS_VLAN_TAG) { + pkt->vlan_tag = rx_hdr->l2_fhdr_vtag_len & 0x0FFF; + pkt->flags |= VLAN_TAGGED; + } else { + pkt->vlan_tag = 0; + } + + rc = 1; + + LOG_DEBUG(PFX "%s: processing packet length: %d", + nic->log_name, len); + } else { + /* One of the fields in the BD is bad */ + uint16_t status = ((rx_hdr->l2_fhdr_status & 0x0000ffff)); + + LOG_ERR(PFX "%s: Recv error: %x status: %x len: %d", + nic->log_name, + errors, status, len); + + rc = 0; + } + + bp->rx_index++; + sw_cons = NEXT_RX_BD(sw_cons); + bp->rx_prod = NEXT_RX_BD(bp->rx_prod); + bp->rx_bseq += 0x400; + + bp->rx_cons = sw_cons; + bnx2_wr16(bp, bp->rx_bidx_io, bp->rx_prod); + bnx2_wr32(bp, bp->rx_bseq_io, bp->rx_bseq); + + bnx2_reg_sync(bp, bp->rx_bidx_io, sizeof(__u16)); + bnx2_reg_sync(bp, bp->rx_bseq_io, sizeof(__u32)); + + /* bump the bnx2 dev recv statistics */ + nic->stats.rx.packets++; + nic->stats.rx.bytes += pkt->buf_size; + } + + return rc; +} +/******************************************************************************* + * Clearing TX interrupts + ******************************************************************************/ +/** + * bnx2_clear_tx_intr() - This routine is called when a TX interrupt occurs + * @param nic - the nic the interrupt occured on + * @return 0 on success + */ +static int bnx2_clear_tx_intr(nic_t *nic) +{ + bnx2_t *bp = (bnx2_t *) nic->priv; + uint16_t hw_cons = bp->get_tx_cons(bp); + + /* Sanity check: ensure the parameters passed in are valid */ + if(unlikely(nic == NULL)) { + LOG_ERR(PFX "bnx2_read() nic == NULL"); + return -EINVAL; + } + + if(bp->flags & CNIC_UIO_TX_HAS_SENT) { + bp->flags &= ~CNIC_UIO_TX_HAS_SENT; + pthread_mutex_unlock(&nic->xmit_mutex); + return 0; + } + + if(bp->tx_cons == hw_cons) + return 0; + + LOG_DEBUG(PFX "%s: clearing tx interrupt [%d %d]", + nic->log_name, + bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; + + /* There is a queued TX packet that needs to be sent out. The usual + * case is when stack will send an ARP packet out before sending the + * intended packet */ + if(nic->tx_packet_queue != NULL) + { + packet_t *pkt; + + LOG_DEBUG(PFX "%s: sending queued tx packet", nic->log_name); + pkt = nic_dequeue_tx_packet(nic); + + /* Got a TX packet buffer of the TX queue and put it onto + * the hardware */ + if(pkt != NULL) + { + bnx2_prepare_xmit_packet(nic, + pkt->nic_iface, + pkt); + + bnx2_start_xmit(nic, pkt->buf_size); + + LOG_DEBUG(PFX "%s: transmitted queued packet %d bytes " + "dev->tx_cons: %d, dev->tx_prod: %d, " + "dev->tx_bseq:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, + bp->tx_bseq); + + return 0; + } + } + + pthread_mutex_unlock(&nic->xmit_mutex); + + return 0; +} + +/******************************************************************************* + * bnx2 NIC op's table + ******************************************************************************/ +struct nic_ops bnx2_op = { + .description = "bnx2", + .open = bnx2_open, + .close = bnx2_close, + .write = bnx2_write, + .get_tx_pkt = bnx2_get_tx_pkt, + .start_xmit = bnx2_start_xmit, + .read = bnx2_read, + .clear_tx_intr = bnx2_clear_tx_intr, + .handle_iscsi_path_req = cnic_handle_iscsi_path_req, + + .lib_ops = { + .get_library_name = bnx2_get_library_name, + .get_pci_table = bnx2_get_pci_table, + .get_library_version = bnx2_get_library_version, + .get_build_date = bnx2_get_build_date, + .get_transport_name = bnx2_get_transport_name, + .get_uio_name = bnx2_get_uio_name, + }, +}; diff --git a/brcm_iscsi_uio/src/unix/libs/bnx2.h b/brcm_iscsi_uio/src/unix/libs/bnx2.h new file mode 100644 index 0000000..fe3af31 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/libs/bnx2.h @@ -0,0 +1,273 @@ +/* cnic.h: CNIC user space driver + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#ifndef __CNIC_H__ +#define __CNIC_H__ + +#include "nic.h" + +/****************************************************************************** + * Default CNIC values + ******************************************************************************/ +#define DEFAULT_NUM_RXBD 3 +#define DEFAULT_RX_LEN 0x400 + + +/****************************************************************************** + * CNIC Hardware structures + ******************************************************************************/ +/* status_block definition for MSI */ +struct status_block { + volatile __u32 status_attn_bits; + volatile __u32 status_attn_bits_ack; + volatile __u32 tx0; + volatile __u32 tx2; + volatile __u32 rx0; + volatile __u32 rx2; + volatile __u32 rx4; + volatile __u32 rx6; + volatile __u32 rx8; + volatile __u32 rx10; + volatile __u32 rx12; + volatile __u32 rx14; + volatile __u32 cmd; + volatile __u32 idx; +}; + +/* status_block definition for MSI-X */ +struct status_block_msix { +#if 0 +#if defined(__BIG_ENDIAN) + __u16 status_tx_quick_consumer_index; + __u16 status_rx_quick_consumer_index; + __u16 status_completion_producer_index; + __u16 status_cmd_consumer_index; + __u32 status_unused; + __u16 status_idx; + __u8 status_unused2; + __u8 status_blk_num; +#elif defined(__LITTLE_ENDIAN) + __u16 status_rx_quick_consumer_index; + __u16 status_tx_quick_consumer_index; + __u16 status_cmd_consumer_index; + __u16 status_completion_producer_index; + __u32 status_unused; + __u8 status_blk_num; + __u8 status_unused2; + __u16 status_idx; +#endif +#endif + __u16 status_rx_quick_consumer_index; + __u16 status_tx_quick_consumer_index; + __u16 status_cmd_consumer_index; + __u16 status_completion_producer_index; + __u32 status_unused; + __u8 status_blk_num; + __u8 status_unused2; + __u16 status_idx; +}; + +/* TX Buffer descriptor */ +struct tx_bd { + __u32 tx_bd_haddr_hi; + __u32 tx_bd_haddr_lo; + __u32 tx_bd_mss_nbytes; + __u32 tx_bd_vlan_tag_flags; + #define TX_BD_FLAGS_END (1<<6) + #define TX_BD_FLAGS_START (1<<7) +}; + +/* RX Buffer descriptor */ +struct rx_bd { + __u32 rx_bd_haddr_hi; + __u32 rx_bd_haddr_lo; + + __u32 rx_bd_len; + __u32 rx_bd_flags; +#define RX_BD_FLAGS_END (1<<2) +#define RX_BD_FLAGS_START (1<<3) + +}; + +/* This is the RX L2 Frame header */ +struct l2_fhdr { + __u32 l2_fhdr_status; + #define L2_FHDR_ERRORS_BAD_CRC (1<<17) + #define L2_FHDR_ERRORS_PHY_DECODE (1<<18) + #define L2_FHDR_ERRORS_ALIGNMENT (1<<19) + #define L2_FHDR_ERRORS_TOO_SHORT (1<<20) + #define L2_FHDR_ERRORS_GIANT_FRAME (1<<21) + #define L2_FHDR_ERRORS_TCP_XSUM (1<<28) + #define L2_FHDR_ERRORS_UDP_XSUM (1<<31) + + #define L2_FHDR_STATUS_UDP_DATAGRAM (1<<15) + #define L2_FHDR_STATUS_TCP_DATAGRAM (1<<14) + #define L2_FHDR_STATUS_IP_DATAGRAM (1<<13) + #define L2_FHDR_STATUS_LLC_SNAP (1<<7) + #define L2_FHDR_STATUS_VLAN_TAG (1<<6) + + __u32 l2_fhdr_hash; + + __u32 l2_fhdr_vtag_len; + __u32 l2_fhdr_xsum; +}; + +/****************************************************************************** + * BNX2 Registers Defitions/Values + ******************************************************************************/ +#define BNX2_MISC_ID 0x00000808 +#define BNX2_EMAC_MAC_MATCH4 0x00001420 +#define BNX2_EMAC_MAC_MATCH5 0x00001424 + +#define BNX2_EMAC_RX_MODE 0x000014c8 +#define BNX2_EMAC_RX_MODE_RESET (1L<<0) +#define BNX2_EMAC_RX_MODE_FLOW_EN (1L<<2) +#define BNX2_EMAC_RX_MODE_KEEP_MAC_CONTROL (1L<<3) +#define BNX2_EMAC_RX_MODE_KEEP_PAUSE (1L<<4) +#define BNX2_EMAC_RX_MODE_ACCEPT_OVERSIZE (1L<<5) +#define BNX2_EMAC_RX_MODE_ACCEPT_RUNTS (1L<<6) +#define BNX2_EMAC_RX_MODE_LLC_CHK (1L<<7) +#define BNX2_EMAC_RX_MODE_PROMISCUOUS (1L<<8) +#define BNX2_EMAC_RX_MODE_NO_CRC_CHK (1L<<9) +#define BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG (1L<<10) +#define BNX2_EMAC_RX_MODE_FILT_BROADCAST (1L<<11) +#define BNX2_EMAC_RX_MODE_SORT_MODE (1L<<12) + +/* + * tsch_reg definition + * offset: 0x4c00 + */ +#define BNX2_TSCH_TSS_CFG 0x00004c1c +#define BNX2_TSCH_TSS_CFG_TSS_START_CID (0x7ffL<<8) +#define BNX2_TSCH_TSS_CFG_NUM_OF_TSS_CON (0xfL<<24) +#define CNIC_UIO_INVALID_FD -1 + +#define BNX2_L2CTX_TX_HOST_BIDX 0x00000088 +#define BNX2_L2CTX_TX_HOST_BSEQ 0x00000090 + +#define BNX2_L2CTX_HOST_BDIDX 0x00000004 +#define BNX2_L2CTX_HOST_BSEQ 0x00000008 + +/* Used to determin the CHIP ID */ +/* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ +#define CHIP_NUM(bp) ((bp) & 0xffff0000) +#define CHIP_NUM_5706 0x57060000 +#define CHIP_NUM_5708 0x57080000 +#define CHIP_NUM_5709 0x57090000 + +#define CHIP_REV(bp) ((bp) & 0x0000f000) +#define CHIP_REV_Ax 0x00000000 +#define CHIP_REV_Bx 0x00001000 +#define CHIP_REV_Cx 0x00002000 + +#define CHIP_METAL(bp) ((bp) & 0x00000ff0) +#define CHIP_BONDING(bp) ((bp) & 0x0000000f) + +#define CHIP_ID(bp) ((bp) & 0xfffffff0) +#define CHIP_ID_5706_A0 0x57060000 +#define CHIP_ID_5706_A1 0x57060010 +#define CHIP_ID_5706_A2 0x57060020 +#define CHIP_ID_5708_A0 0x57080000 +#define CHIP_ID_5708_B0 0x57081000 +#define CHIP_ID_5708_B1 0x57081010 +#define CHIP_ID_5709_A0 0x57090000 +#define CHIP_ID_5709_A1 0x57090010 + +#define CHIP_BOND_ID(bp) ((bp) & 0xf) + + +#define CNIC_SBLK_EVEN_IDX(x) (((x) & 0xffff0000) >> 16) + +#define TX_DESC_CNT (4096 / sizeof(struct tx_bd)) +#define MAX_TX_DESC_CNT (TX_DESC_CNT - 1) + +#define NEXT_TX_BD(x) (((x) & (MAX_TX_DESC_CNT - 1)) == \ + (MAX_TX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1 + +#define TX_RING_IDX(x) ((x) & MAX_TX_DESC_CNT) + + +#define RX_DESC_CNT (4096 / sizeof(struct rx_bd)) +#define MAX_RX_DESC_CNT (RX_DESC_CNT - 1) + +#define NEXT_RX_BD(x) (((x) & (MAX_RX_DESC_CNT - 1)) == \ + (MAX_RX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1 + +#define MB_KERNEL_CTX_SHIFT 8 +#define MB_KERNEL_CTX_SIZE (1 << MB_KERNEL_CTX_SHIFT) +#define MB_KERNEL_CTX_MASK (MB_KERNEL_CTX_SIZE - 1) +#define MB_GET_CID_ADDR(_cid) (0x10000 + ((_cid) << MB_KERNEL_CTX_SHIFT)) + +typedef struct bnx2 { + nic_t *parent; + + uint16_t flags; +#define CNIC_UIO_UNITIALIZED 0x0001 +#define CNIC_UIO_INITIALIZED 0x0002 +#define CNIC_UIO_ENABLED 0x0004 +#define CNIC_UIO_DISABLED 0x0008 +#define CNIC_UIO_IPv6_ENABLED 0x0010 +#define CNIC_UIO_ADDED_MULICAST 0x0020 +#define CNIC_UIO_MSIX_ENABLED 0x0200 +#define CNIC_UIO_TX_HAS_SENT 0x0400 + + void *reg; /* Pointer to the mapped registers */ + + __u32 tx_bidx_io; + __u32 tx_bseq_io; + + __u16 tx_prod; + __u16 tx_cons; + __u32 tx_bseq; + + __u32 rx_bidx_io; + __u32 rx_bseq_io; + + __u16 rx_prod; + __u16 rx_cons; + __u32 rx_bseq; + + /* RX ring parameters */ + uint32_t rx_ring_size; + uint32_t rx_buffer_size; + + void *bufs; /* Pointer to the mapped buffer space */ + + /* Hardware Status Block locations */ + void *sblk_map; + union { + struct status_block *msi; + struct status_block_msix *msix; + } status_blk; + size_t status_blk_size; + + __u16 (*get_rx_cons)(struct bnx2 *); + __u16 (*get_tx_cons)(struct bnx2 *); + + uint16_t rx_index; + struct l2_fhdr **rx_ring; + void **rx_pkt_ring; + + struct tx_bd *tx_ring; + void *tx_pkt; + + struct l2_fhdr rcv_l2_fhdr; + __u8 rcv_buf[1500 + 2]; + __u32 rcv_size; +} bnx2_t; + +/****************************************************************************** + * bnx2 Function Declarations + ******************************************************************************/ +struct nic_ops * bnx2_get_ops(); +#endif /* __CNIC_H__*/ diff --git a/brcm_iscsi_uio/src/unix/libs/bnx2x.c b/brcm_iscsi_uio/src/unix/libs/bnx2x.c new file mode 100644 index 0000000..f4eb876 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/libs/bnx2x.c @@ -0,0 +1,1103 @@ +/* bnx2x.c: bnx2x user space driver + * + * Copyright (c) 2004-2009 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "build_date.h" +#include "bnx2x.h" +#include "cnic.h" +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" +#include "options.h" + +#define PFX "bnx2x " + +/* Foward struct declarations */ +struct nic_ops bnx2x_op; + +/* Determine is the CNIC kernel module is loaded or not */ +int lib_bnx2x_loaded = 0; + +/******************************************************************************* + * NIC Library Strings + ******************************************************************************/ +static const char library_name[] = "bnx2x"; +static const char library_version[] = PACKAGE_VERSION; +static const char library_uio_name[] = "bnx2x_cnic"; + +/* Template strings used to read RX parameters from sysfs */ +static const char cnic_sysfs_buf_size_template[] = "/sys/class/uio/uio%d/device/uio_buf_size"; +static const char cnic_sysfs_rx_ring_size_template[] = "/sys/class/uio/uio%d/device/uio_rx_ring_size"; +static const char cnic_sysfs_uio_event_template[] = "/sys/class/uio/uio%d/event"; + +/* The name that should be returned from /sys/class/uio/uio0/name */ +static const char cnic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name"; +static const char bnx2x_uio_sysfs_name[] = "bnx2x_cnic"; + +static const char cnic_uio_sysfs_resc_tempate[] = "/sys/class/uio/uio%i/device/resource"; + +/******************************************************************************* + * String constants used to display human readable adapter name + ******************************************************************************/ +static const char brcm_57710[] = "Broadcom NetXtreme II BCM57710 10-Gigabit"; +static const char brcm_57711[] = "Broadcom NetXtreme II BCM57711 10-Gigabit"; +static const char brcm_57711e[] = "Broadcom NetXtreme II BCM57711E 10-Gigabit"; + +/******************************************************************************* + * PCI ID constants + ******************************************************************************/ +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_DEVICE_ID_NX2_57710 0x164e +#define PCI_DEVICE_ID_NX2_57711 0x164f +#define PCI_DEVICE_ID_NX2_57711E 0x1650 +#define PCI_ANY_ID (~0) + +/* This is the table used to match PCI vendor and device ID's to the + * human readable string names of the devices */ +static const struct pci_device_id bnx2x_pci_tbl[] = { + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57710, + PCI_ANY_ID, PCI_ANY_ID, brcm_57710 }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57711, + PCI_ANY_ID, PCI_ANY_ID, brcm_57711 }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57711E, + PCI_ANY_ID, PCI_ANY_ID, brcm_57711e }, +}; + +/******************************************************************************* + * BNX2X Library Functions + ******************************************************************************/ +/** + * bnx2x_get_library_name() - Used to get the name of this NIC libary + * @param name - This function will return the pointer to this NIC + * library name + * @param name_size + */ +static void bnx2x_get_library_name(char **name, + size_t *name_size) +{ + *name = (char *)library_name; + *name_size = sizeof(library_name); +} + +/** + * bnx2x_get_library_version() - Used to get the version string of this + * NIC libary + * @param version - This function will return the pointer to this NIC + * library version string + * @param version_size - This will be set with the version size + */ +static void bnx2x_get_library_version(char **version, + size_t *version_size) +{ + *version = (char *) library_version; + *version_size = sizeof(library_version); +} + +/** + * bnx2x_get_build_date() - Used to get the build date string of this library + * @param version - This function will return the pointer to this NIC + * library build date string + * @param version_size - This will be set with the build date string size + */ +static void bnx2x_get_build_date(char **build, + size_t *build_size) +{ + *build = (char *) build_date; + *build_size = sizeof(build_date); +} + +/** + * bnx2x_get_transport_name() - Used to get the transport name associated + * with this this NIC libary + * @param transport_name - This function will return the pointer to this NIC + * library's associated transport string + * @param transport_name_size - This will be set with the transport name size + */ +static void bnx2x_get_transport_name(char **transport_name, + size_t *transport_name_size) +{ + *transport_name = (char *) bnx2i_library_transport_name; + *transport_name_size = bnx2i_library_transport_name_size; +} + +/** + * bnx2x_get_uio_name() - Used to get the uio name associated with this this + * NIC libary + * @param uio_name - This function will return the pointer to this NIC + * library's associated uio string + * @param transport_name_size - This will be set with the uio name size + */ +static void bnx2x_get_uio_name(char **uio_name, + size_t *uio_name_size) +{ + *uio_name = (char *) library_uio_name; + *uio_name_size = sizeof(library_uio_name); +} + +/** + * bnx2x_get_pci_table() - Used to get the PCI table for this NIC libary to + * determine which NIC's based off of PCI ID's are + * supported + * @param table - This function will return the pointer to the PCI table + * @param entries - This function will return the number of entries in the NIC + * library's PCI table + */ +static void bnx2x_get_pci_table(struct pci_device_id **table, + uint32_t *entries) +{ + *table = (struct pci_device_id *) bnx2x_pci_tbl; + *entries = (uint32_t) (sizeof(bnx2x_pci_tbl)/sizeof(bnx2x_pci_tbl[0])); +} + +/** + * bnx2x_get_ops() - Used to get the NIC library op table + * @param op - The op table of this NIC library + */ +struct nic_ops * bnx2x_get_ops() +{ + return &bnx2x_op; +} + +/******************************************************************************* + * bnx2x Utility Functions + ******************************************************************************/ +/******************************************************************************* + * Utility Functions Used to read register from the bnx2x device + ******************************************************************************/ +static void bnx2x_wr32(bnx2x_t *bp, __u32 off, __u32 val) +{ + *((volatile __u32 *)(bp->reg + off)) = val; +} + +static void bnx2x_doorbell(bnx2x_t *bp, __u32 off, __u32 val) +{ + *((volatile __u32 *)(bp->reg2 + off)) = val; +} + +static __u32 bnx2x_rd32(bnx2x_t *bp, __u32 off) +{ + return *((volatile __u32 *)(bp->reg + off)); +} + +static int bnx2x_reg_sync(bnx2x_t *bp, __u32 off, __u16 length) +{ + return msync(bp->reg + off, length, MS_SYNC); +} + +static void bnx2x_update_rx_prod(bnx2x_t *bp) +{ + struct ustorm_eth_rx_producers rx_prods = {0}; + int i; + + rx_prods.bd_prod = bp->rx_bd_prod; + rx_prods.cqe_prod = bp->rx_prod; + + barrier(); + + for (i = 0; i < sizeof(struct ustorm_eth_rx_producers)/4; i++) + bnx2x_wr32(bp, bp->rx_prod_io + i * 4, + ((__u32 *)&rx_prods)[i]); + + barrier(); + + bnx2x_reg_sync(bp, bp->rx_prod_io, + sizeof(struct ustorm_eth_rx_producers)); +} + +/** + * bnx2x_get_chip_id() - Used to retrive the chip ID from the nic + * @param dev - Device used to determin NIC type + * @return Chip ID read from the MISC ID register + */ +static int bnx2x_get_chip_id(bnx2x_t *bp) +{ + int val, id; + + /* Get the chip revision id and number. */ + /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ + val = bnx2x_rd32(bp, BNX2X_MISC_REG_CHIP_NUM); + id = ((val & 0xffff) << 16); + val = bnx2x_rd32(bp, BNX2X_MISC_REG_CHIP_REV); + id |= ((val & 0xf) << 12); + val = bnx2x_rd32(bp, BNX2X_MISC_REG_CHIP_METAL); + id |= ((val & 0xff) << 4); + val = bnx2x_rd32(bp, BNX2X_MISC_REG_BOND_ID); + id |= (val & 0xf); + + return id; +} + + +/** + * bnx2x_uio_verify() + * + */ +static int bnx2x_uio_verify(nic_t *nic) +{ + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + char temp_path[sizeof(cnic_uio_sysfs_name_tempate) + 8]; + int rc = 0; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + cnic_uio_sysfs_name_tempate, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if(rc != 0) + { + goto error; + } + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while(*raw_tmp != '\n') + raw_tmp++; + *raw_tmp = '\0'; + + if (strncmp(raw, bnx2x_uio_sysfs_name, + sizeof(bnx2x_uio_sysfs_name)) != 0) { + LOG_ERR(PFX "%s: uio names not equal: " + "expecting %s got %s from %s", + nic->log_name, bnx2x_uio_sysfs_name, raw, temp_path); + rc = -EIO; + } + + free(raw); + + LOG_INFO(PFX "%s: Verified is a cnic_uio device", nic->log_name); + + error: + return rc; +} + +static unsigned long cnic_get_bar2(nic_t *nic) +{ + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + char temp_path[sizeof(cnic_uio_sysfs_resc_tempate) + 8]; + int rc = 0, i, new_line; + unsigned long bar = 0; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + cnic_uio_sysfs_resc_tempate, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if(rc != 0) + return 0; + + /* Skip 2 lines to get to BAR2 */ + raw_tmp = raw; + i = 0; + new_line = 0; + while (i++ < raw_size && new_line < 2) { + if (*raw_tmp == '\n') + new_line++; + raw_tmp++; + } + + if (new_line == 2) + sscanf(raw_tmp, "%lx ", &bar); + + free(raw); + + return bar; +} + +/******************************************************************************* + * bnx2x Utility Functions to get to the hardware consumer indexes + ******************************************************************************/ +static __u16 bnx2x_get_rx(bnx2x_t *bp) +{ + struct host_def_status_block *sblk = bp->status_blk; + __u16 rx_comp_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + rx_comp_cons = sblk->u_def_status_block.index_values[ + HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS]; + if ((rx_comp_cons & BNX2X_MAX_RCQ_DESC_CNT) == BNX2X_MAX_RCQ_DESC_CNT) + rx_comp_cons++; + + return rx_comp_cons; +} + + +static __u16 bnx2x_get_tx(bnx2x_t *bp) +{ + struct host_def_status_block *sblk = bp->status_blk; + __u16 tx_cons; + + msync(sblk, sizeof(*sblk), MS_SYNC); + tx_cons = sblk->c_def_status_block.index_values[ + HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS]; + + return tx_cons; +} + + +typedef enum { + CNIC_VLAN_STRIPPING_ENABLED = 1, + CNIC_VLAN_STRIPPING_DISABLED = 2, +} CNIC_VLAN_STRIPPING_MODE; + +/** + * bnx2x_strip_vlan_enabled() - This will query the device to determine whether + * VLAN tag stripping is enabled or not + * @param dev - device to check stripping or not + * @ return CNIC_VLAN_STRIPPING_ENABLED stripping is enabled + * CNIC_VLAN_STRIPPING_DISABLED stripping is not enabled + */ +static CNIC_VLAN_STRIPPING_MODE bnx2x_strip_vlan_enabled(bnx2x_t *bp) +{ + return CNIC_VLAN_STRIPPING_DISABLED; +} + +/** + * bnx2x_alloc() - Used to allocate a CNIC structure + */ +static bnx2x_t * bnx2x_alloc(nic_t *nic) +{ + bnx2x_t *bp = malloc(sizeof(*bp)); + + if(bp == NULL) + { + LOG_ERR(PFX "%s: Could not allocate BNX2X space", + nic->log_name); + return NULL; + } + + /* Clear out the CNIC contents */ + memset(bp, 0, sizeof(*bp)); + + bp->mem_fd = INVALID_FD; + + bp->parent = nic; + nic->priv = (void *) bp; + + return bp; +} + +/** + * bnx2x_open() - This will initialize all the hardware resources underneath + * a struct cnic_uio device + * @param dev - The struct cnic_uio device to attach the hardware with + * @return 0 on success, on failure a errno will be returned + */ +static int bnx2x_open(nic_t *nic) +{ + bnx2x_t *bp; + struct stat uio_stat; + int i, rc; + __u32 val; + unsigned long bar2; + + uint32_t bus; + uint32_t slot; + uint32_t func; + + /* Sanity Check: validate the parameters */ + if(nic == NULL) { + LOG_ERR(PFX "cnic_open(): nic == NULL"); + return -EINVAL; + } + + bp = bnx2x_alloc(nic); + if(bp == NULL) + return -ENOMEM; + + while(nic->fd < 0) { + /* udev might not have created the file yet */ + sleep(1); + + nic->fd = open(nic->uio_device_name, O_RDWR | O_NONBLOCK); + if (nic->fd != INVALID_FD) { + LOG_ERR(PFX "%s: uio device has been brought up " + "via pid: %d on fd: %d", + nic->uio_device_name, getpid(), nic->fd); + + rc = bnx2x_uio_verify(nic); + if(rc != 0) + continue; + + break; + } else { + if( lib_bnx2x_loaded == 0) { + LOG_ERR(PFX "%s: Could not open device: %s, " + "awaiting for the device to appear", + nic->log_name, nic->uio_device_name); + + /* Time to wait for the device to come up */ + pthread_mutex_lock(&nic->uio_wait_mutex); + pthread_cond_wait(&nic->uio_wait_cond, + &nic->uio_wait_mutex); + pthread_mutex_unlock(&nic->uio_wait_mutex); + + /* udev might not have created the file yet */ + sleep(2); + + lib_bnx2x_loaded = 1; + } else { + /* udev might not have created the file yet */ + sleep(2); + } + } + } + if (fstat(nic->fd, &uio_stat) < 0) { + LOG_ERR(PFX "%s: Could not fstat device", nic->log_name); + return -ENODEV; + } + nic->uio_minor = minor(uio_stat.st_rdev); + + bar2 = cnic_get_bar2(nic); + if (bar2 == 0) { + LOG_ERR(PFX "%s: Could not read BAR2", nic->log_name); + return -ENODEV; + } + + bp->mem_fd = open("/dev/mem", O_RDWR | O_SYNC); + if (bp->mem_fd < 0) { + LOG_ERR(PFX "%s: Could not open /dev/mem", nic->log_name); + return -ENODEV; + } + + bp->reg2 = mmap(NULL, BNX2X_BAR2_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, bp->mem_fd, (off_t) bar2); + + if (bp->reg2 == MAP_FAILED) { + LOG_INFO(PFX "%s: Couldn't mmap BAR2 registers: %s", + nic->log_name, strerror(errno)); + bp->reg2 = NULL; + rc = errno; + goto open_error; + } + + /* TODO: hardcoded with the cnic driver */ + bp->rx_ring_size = 15; + bp->rx_buffer_size = 0x400; + + LOG_DEBUG(PFX "%s: using rx ring size: %d, rx buffer size: %d", + nic->log_name, bp->rx_ring_size, bp->rx_buffer_size); + + /* Determine the number of UIO events that have already occured */ + rc = detemine_initial_uio_events(nic, &nic->intr_count); + if(rc != 0) { + LOG_ERR("Could not determine the number ofinitial UIO events"); + nic->intr_count = 0; + } + + /* Allocate space for rx pkt ring */ + bp->rx_pkt_ring = malloc(sizeof(void *) * bp->rx_ring_size); + if(bp->rx_pkt_ring == NULL) + { + LOG_ERR(PFX "%s: Could not allocate space for rx_pkt_ring", + nic->log_name); + rc = errno; + goto open_error; + } + + bp->reg = mmap(NULL, BNX2X_BAR_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + nic->fd, (off_t) 0); + if (bp->reg == MAP_FAILED) { + LOG_INFO(PFX "%s: Couldn't mmap registers: %s", + nic->log_name, strerror(errno)); + bp->reg = NULL; + rc = errno; + goto open_error; + } + + msync(bp->reg, BNX2X_BAR_SIZE, MS_SYNC); + + bp->status_blk = mmap(NULL, sizeof(struct host_def_status_block), + PROT_READ | PROT_WRITE, MAP_SHARED, + nic->fd, (off_t) getpagesize()); + if (bp->status_blk == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap status block: %s", + nic->log_name, strerror(errno)); + bp->status_blk = NULL; + rc = errno; + goto open_error; + } + + bp->tx_ring = mmap(NULL, 4 * getpagesize(), + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_LOCKED, + nic->fd, (off_t) 2 * getpagesize()); + if (bp->tx_ring == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap tx ring: %s", + nic->log_name, strerror(errno)); + bp->tx_ring = NULL; + rc = errno; + goto open_error; + } + + bp->rx_comp_ring = (union eth_rx_cqe *) + (((__u8 *) bp->tx_ring) + 2 * getpagesize()); + + bp->bufs = mmap(NULL, (bp->rx_ring_size + 1) * bp->rx_buffer_size, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_LOCKED, + nic->fd, (off_t) 3 * getpagesize()); + if (bp->bufs == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap buffers: %s", + nic->log_name, strerror(errno)); + bp->bufs = NULL; + rc = errno; + goto open_error; + } + + bp->chip_id = bnx2x_get_chip_id(bp); + LOG_DEBUG(PFX "Chip ID: %x", bp->chip_id); + + rc = get_bus_slot_func_num(nic, &bus, &slot, &func); + if(rc != 0) { + LOG_INFO(PFX "%s: Couldn't determine bus:slot.func", + nic->log_name); + goto open_error; + } + + bp->func = func; + bp->port = bp->func % PORT_MAX; + + bp->rx_prod_io = BAR_USTRORM_INTMEM + + USTORM_RX_PRODS_OFFSET(bp->port, 17); + + bp->tx_doorbell = 17 * getpagesize() + 0x40; + + bp->tx_cons = 0; + bp->tx_prod = 0; + bp->tx_bd_prod = 0; + bp->tx_pkt = bp->bufs; + + bp->rx_index = 0; + bp->rx_cons = 0; + bp->rx_bd_cons = 0; + bp->rx_prod = 127; + bp->rx_bd_prod = bp->rx_ring_size; + + for (i = 0; i < bp->rx_ring_size; i++) { + void *ptr = bp->bufs + (bp->rx_buffer_size * (i + 1)); + + bp->rx_pkt_ring[i] = ptr; + } + + val = bnx2x_rd32(bp, MISC_REG_SHARED_MEM_ADDR); + + bp->shmem_base = val; + val = bnx2x_rd32(bp, bp->shmem_base + SHMEM_ISCSI_MAC_UPPER(bp)); + nic->mac_addr[0] = (__u8) (val >> 8); + nic->mac_addr[1] = (__u8) val; + val = bnx2x_rd32(bp, bp->shmem_base + SHMEM_ISCSI_MAC_LOWER(bp)); + nic->mac_addr[2] = (__u8) (val >> 24); + nic->mac_addr[3] = (__u8) (val >> 16); + nic->mac_addr[4] = (__u8) (val >> 8); + nic->mac_addr[5] = (__u8) val; + + LOG_INFO(PFX "%s: Using mac address: %02x:%02x:%02x:%02x:%02x:%02x", + nic->log_name, + nic->mac_addr[0], nic->mac_addr[1], nic->mac_addr[2], + nic->mac_addr[3], nic->mac_addr[4], nic->mac_addr[5]); + + /* Determine if Hardware VLAN tag stripping is enabled or not */ + if(CNIC_VLAN_STRIPPING_ENABLED == bnx2x_strip_vlan_enabled(bp)) + { + nic->flags |= NIC_VLAN_STRIP_ENABLED; + } + + /* Prepare the multicast addresses */ + rc = enable_multicast(nic); + if(rc != 0) + goto open_error; + + msync(bp->reg, BNX2X_BAR_SIZE, MS_SYNC); + LOG_INFO("%s: bnx2x initialized", nic->log_name); + + bnx2x_update_rx_prod(bp); + + return 0; + +open_error: + if (bp->tx_ring) { + munmap(bp->tx_ring, 4 * getpagesize()); + bp->tx_ring = NULL; + } + + if (bp->status_blk) { + munmap(bp->status_blk, sizeof(struct host_def_status_block)); + bp->status_blk = NULL; + } + + if (bp->reg) { + munmap(bp->reg, BNX2X_BAR_SIZE); + bp->reg = NULL; + } + + if (bp->reg2) { + munmap(bp->reg2, BNX2X_BAR2_SIZE); + bp->reg2 = NULL; + } + + if (bp->rx_pkt_ring) { + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + } + + if (bp->mem_fd != INVALID_FD) { + close(bp->mem_fd); + bp->mem_fd = INVALID_FD; + } + + return rc; +} + +/** + * bnx2x_uio_close_resources() - Used to free resource for the NIC/CNIC + * @param nic - NIC device to free resource + * @param graceful - whether to wait to close gracefully + * @return 0 on success, <0 on failure + */ +static int bnx2x_uio_close_resources(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + int rc = 0; + + /* Remove the multicast addresses if added */ + if((nic->flags & NIC_ADDED_MULICAST) && + (graceful == ALLOW_GRACEFUL_SHUTDOWN)) + disable_multicast(nic); + + /* Check if there is an assoicated bnx2x device */ + if(bp == NULL) { + LOG_WARN(PFX "%s: when closing resources there is " + "no assoicated bnx2x", + nic->log_name); + return -EIO; + } + + /* Clean up allocated memory */ + + if(bp->rx_pkt_ring != NULL) { + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + } + + /* Clean up mapped registers */ + if (bp->bufs != NULL) { + rc = munmap(bp->bufs, + (bp->rx_ring_size + 1) * bp->rx_buffer_size); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap bufs", + nic->log_name); + bp->bufs = NULL; + } + + if (bp->tx_ring != NULL) { + munlock(bp->tx_ring, 4 * getpagesize()); + rc = munmap(bp->tx_ring, 4 * getpagesize()); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap tx_rings", + nic->log_name); + bp->tx_ring = NULL; + } + + if (bp->status_blk != NULL) { + rc = munmap(bp->status_blk, sizeof(struct host_def_status_block)); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap status block", + nic->log_name); + bp->status_blk = NULL; + } + + if (bp->reg != NULL) { + rc = munmap(bp->reg, BNX2X_BAR_SIZE); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap regs", + nic->log_name); + bp->reg = NULL; + } + + if (bp->reg2 != NULL) { + rc = munmap(bp->reg2, BNX2X_BAR2_SIZE); + if (rc != 0) + LOG_WARN(PFX "%s: Couldn't unmap regs", + nic->log_name); + bp->reg2 = NULL; + } + + if (bp->mem_fd != INVALID_FD) { + close(bp->mem_fd); + bp->mem_fd = INVALID_FD; + } + + if (nic->fd != INVALID_FD) { + rc = close(nic->fd); + if (rc != 0) { + LOG_WARN(PFX "%s: Couldn't close uio file descriptor: %d", + nic->log_name, nic->fd); + } else { + LOG_DEBUG(PFX "%s: Closed uio file descriptor: %d", + nic->log_name, nic->fd); + } + + nic->fd = INVALID_FD; + } else { + LOG_WARN(PFX "%s: Invalid uio file descriptor: %d", + nic->log_name, nic->fd); + } + + LOG_INFO(PFX "%s: Closed all resources", nic->log_name); + + return 0; +} + +/** + * cnic_close() - Used to close the NIC device + * @param nic - NIC device to close + * @param graceful - whether to wait to close gracefully + * @return 0 if successful, <0 if there is an error + */ +static int bnx2x_close(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + /* Sanity Check: validate the parameters */ + if(nic == NULL) { + LOG_ERR(PFX "cnic_close(): nic == NULL"); + return -EINVAL; + } + + LOG_INFO(PFX "Closing NIC device: %s", nic->log_name); + + bnx2x_uio_close_resources(nic, graceful); + + /* Free any named strings we might be holding onto */ + if(nic->flags & NIC_CONFIG_NAME_MALLOC) { + free(nic->config_device_name); + nic->flags &= ~NIC_CONFIG_NAME_MALLOC; + } + nic->config_device_name = NULL; + + if(nic->flags & NIC_UIO_NAME_MALLOC) { + free(nic->uio_device_name); + nic->uio_device_name = NULL; + + nic->flags &= ~NIC_UIO_NAME_MALLOC; + } + + return 0; +} + +static void bnx2x_prepare_xmit_packet(nic_t *nic, + nic_interface_t *nic_iface, + struct packet *pkt) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + + /* Determine if we need to insert the VLAN tag */ + if((nic_iface->vlan_id != 0) && + (NIC_VLAN_STRIP_ENABLED & nic->flags)) + { + uint16_t insert_tpid = const_htons(UIP_ETHTYPE_8021Q); + uint16_t insert_vlan_id = htons((0x0FFF & nic_iface->vlan_id) + + ((0x000F & nic_iface->vlan_priority) << 12)); + + /* We need to reinsert the VLAN tag */ + memcpy(bp->tx_pkt, pkt->buf, 12); + memcpy(bp->tx_pkt + 12, &insert_tpid, 2); + memcpy(bp->tx_pkt + 14, &insert_vlan_id, 2); + memcpy(bp->tx_pkt + 16, pkt->buf + 12, pkt->buf_size - 12); + + pkt->buf_size = pkt->buf_size +4; + + LOG_DEBUG(PFX "%s: Inserted vlan tag id: 0x%x", + nic->log_name, + ntohs(insert_vlan_id)); + } else { + memcpy(bp->tx_pkt, pkt->buf, pkt->buf_size); + } + + msync(bp->tx_pkt, pkt->buf_size, MS_SYNC); +} + +/** + * bnx2x_get_tx_pkt() - This function is used to a TX packet from the NIC + * @param nic - The NIC device to send the packet + */ +void * bnx2x_get_tx_pkt(nic_t *nic) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + return bp->tx_pkt; +} + + +/** + * bnx2x_start_xmit() - This function is used to send a packet of data + * @param nic - The NIC device to send the packet + * @param len - the length of the TX packet + * + */ +void bnx2x_start_xmit(nic_t *nic, size_t len) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + uint16_t ring_prod; + struct eth_tx_start_bd *txbd; + struct eth_tx_bd *txbd2; + + ring_prod = BNX2X_TX_RING_IDX(bp->tx_bd_prod); + txbd = &bp->tx_ring[ring_prod]; + + txbd->vlan = bp->tx_prod; + + bp->tx_prod = BNX2X_NEXT_TX_BD(bp->tx_prod); + bp->tx_bd_prod = BNX2X_NEXT_TX_BD(bp->tx_bd_prod); + bp->tx_bd_prod = BNX2X_NEXT_TX_BD(bp->tx_bd_prod); + + ring_prod = BNX2X_TX_RING_IDX(bp->tx_bd_prod); + txbd2 = (struct eth_tx_bd *) &bp->tx_ring[ring_prod]; + + txbd2->nbytes = len - 0x10; + txbd2->total_pkt_bytes = len; + + bp->tx_bd_prod = BNX2X_NEXT_TX_BD(bp->tx_bd_prod); + + barrier(); + bnx2x_doorbell(bp, bp->tx_doorbell, 0x02 | (bp->tx_bd_prod << 16)); + + LOG_DEBUG(PFX "%s: sent %d bytes using bp->tx_prod: %d", + nic->log_name, len, bp->tx_prod); +} + +/** + * bnx2x_write() - Used to write the data to the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data to be sent on the wire + * @return 0 if successful, <0 if failed + */ +int bnx2x_write(nic_t *nic, nic_interface_t *nic_iface, + packet_t *pkt) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + struct uip_stack *uip = &nic_iface->ustack; + + /* Sanity Check: validate the parameters */ + if(nic == NULL || nic_iface == NULL || pkt == NULL) { + LOG_ERR(PFX "%s: cnic_write() nic == 0x%p || " + " nic_iface == 0x%p || " + " pkt == 0x%x", nic, nic_iface, pkt); + return -EINVAL; + } + + if(pkt->buf_size == 0) { + LOG_ERR(PFX "%s: Trying to transmitted 0 sized packet", + nic->log_name); + return -EINVAL; + } + + if(pthread_mutex_trylock(&nic->xmit_mutex) != 0) + { + LOG_ERR(PFX "%s: Dropped previous transmitted packet", + nic->log_name); + return -EINVAL; + } + + bnx2x_prepare_xmit_packet(nic, nic_iface, + pkt); + bnx2x_start_xmit(nic, pkt->buf_size); + + /* bump the cnic dev send statistics */ + nic->stats.tx.packets++; + nic->stats.tx.bytes += uip->uip_len; + + LOG_DEBUG(PFX "%s: transmitted %d bytes " + "dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + + return 0; +} + +/** + * bnx2x_read() - Used to read the data from the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data + * @return 0 if successful, <0 if failed + */ +static int bnx2x_read(nic_t *nic, packet_t *pkt) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + int rc = 0; + uint16_t hw_cons, sw_cons, bd_cons, bd_prod; + + /* Sanity Check: validate the parameters */ + if(nic == NULL || pkt == NULL) { + LOG_ERR(PFX "%s: bnx2x_read() nic == 0x%p || " + " pkt == 0x%x", nic, pkt); + return -EINVAL; + } + + hw_cons = bnx2x_get_rx(bp); + sw_cons = bp->rx_cons; + bd_cons = bp->rx_bd_cons; + bd_prod = bp->rx_bd_prod; + + if (sw_cons != hw_cons) { + uint16_t comp_ring_index = sw_cons & BNX2X_MAX_RCQ_DESC_CNT; + uint8_t ring_index; + union eth_rx_cqe *cqe; + __u8 cqe_fp_flags; + void *rx_pkt; + int len; + + cqe = &bp->rx_comp_ring[comp_ring_index]; + cqe_fp_flags = cqe->fast_path_cqe.type_error_flags; + + LOG_DEBUG(PFX "%s: clearing rx interrupt: %d %d", + nic->log_name, + sw_cons, hw_cons); + + msync(cqe, sizeof(*cqe), MS_SYNC); + if (!(cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE)) { + ring_index = bd_cons % 15; + len = cqe->fast_path_cqe.pkt_len; + rx_pkt = bp->rx_pkt_ring[ring_index]; + + /* Doto query MTU size of physical device */ + /* Ensure len is valid */ + if(len > pkt->max_buf_size) + LOG_DEBUG(PFX "%s: bad BD length: %d", + nic->log_name, len); + + if (len > 0) { + msync(rx_pkt, len, MS_SYNC); + /* Copy the data */ + memcpy(pkt->buf, rx_pkt, len); + pkt->buf_size = len; + + /* TODO - VLAN tag info */ + rc = 1; + + LOG_DEBUG(PFX "%s: processing packet length: %d", + nic->log_name, len); + + /* bump the cnic dev recv statistics */ + nic->stats.rx.packets++; + nic->stats.rx.bytes += pkt->buf_size; + } + + bd_cons = BNX2X_NEXT_RX_IDX(bd_cons); + bd_prod = BNX2X_NEXT_RX_IDX(bd_prod); + + } + sw_cons = BNX2X_NEXT_RCQ_IDX(bd_cons); + bp->rx_prod = BNX2X_NEXT_RCQ_IDX(bp->rx_prod); + } + bp->rx_cons = sw_cons; + bp->rx_bd_cons = bd_cons; + bp->rx_bd_prod = bd_prod; + + bnx2x_update_rx_prod(bp); + + return rc; +} +/******************************************************************************* + * Clearing TX interrupts + ******************************************************************************/ +/** + * bnx2x_clear_tx_intr() - This routine is called when a TX interrupt occurs + * @param nic - the nic the interrupt occured on + * @return 0 on success + */ +static int bnx2x_clear_tx_intr(nic_t *nic) +{ + bnx2x_t *bp = (bnx2x_t *) nic->priv; + uint16_t hw_cons = bnx2x_get_tx(bp); + + /* Sanity check: ensure the parameters passed in are valid */ + if(unlikely(nic == NULL)) { + LOG_ERR(PFX "bnx2x_read() nic == NULL"); + return -EINVAL; + } + + if(bp->tx_cons == hw_cons) + return 0; + + LOG_DEBUG(PFX "%s: clearing tx interrupt [%d %d]", + nic->log_name, + bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; + + /* There is a queued TX packet that needs to be sent out. The usual + * case is when stack will send an ARP packet out before sending the + * intended packet */ + if(nic->tx_packet_queue != NULL) + { + packet_t *pkt; + + LOG_DEBUG(PFX "%s: sending queued tx packet", nic->log_name); + pkt = nic_dequeue_tx_packet(nic); + + /* Got a TX packet buffer of the TX queue and put it onto + * the hardware */ + if(pkt != NULL) + { + bnx2x_prepare_xmit_packet(nic, + pkt->nic_iface, + pkt); + + bnx2x_start_xmit(nic, pkt->buf_size); + + LOG_DEBUG(PFX "%s: transmitted queued packet %d bytes " + "dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + + return 0; + } + } + + pthread_mutex_unlock(&nic->xmit_mutex); + + return 0; +} + +/******************************************************************************* + * bnx2x NIC op's table + ******************************************************************************/ +struct nic_ops bnx2x_op = { + .description = "bnx2x", + .open = bnx2x_open, + .close = bnx2x_close, + .write = bnx2x_write, + .get_tx_pkt = bnx2x_get_tx_pkt, + .start_xmit = bnx2x_start_xmit, + .read = bnx2x_read, + .clear_tx_intr = bnx2x_clear_tx_intr, + .handle_iscsi_path_req = cnic_handle_iscsi_path_req, + + .lib_ops = { + .get_library_name = bnx2x_get_library_name, + .get_pci_table = bnx2x_get_pci_table, + .get_library_version = bnx2x_get_library_version, + .get_build_date = bnx2x_get_build_date, + .get_transport_name = bnx2x_get_transport_name, + .get_uio_name = bnx2x_get_uio_name, + }, +}; diff --git a/brcm_iscsi_uio/src/unix/libs/bnx2x.h b/brcm_iscsi_uio/src/unix/libs/bnx2x.h new file mode 100644 index 0000000..d711463 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/libs/bnx2x.h @@ -0,0 +1,365 @@ +/* bnx2x.h: bnx2x user space driver + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#ifndef __BNX2X_H__ +#define __BNX2X_H__ + +#include "nic.h" + +/****************************************************************************** + * Default CNIC values + ******************************************************************************/ +#define DEFAULT_BNX2X_NUM_RXBD 15 +#define DEFAULT_BNX2X_RX_LEN 0x400 + + +/****************************************************************************** + * BNX2X Hardware structures + ******************************************************************************/ +#define HC_USTORM_DEF_SB_NUM_INDICES 8 +#define HC_CSTORM_DEF_SB_NUM_INDICES 8 +#define HC_XSTORM_DEF_SB_NUM_INDICES 4 +#define HC_TSTORM_DEF_SB_NUM_INDICES 4 + +struct atten_def_status_block { + volatile __u32 attn_bits; + volatile __u32 attn_bits_ack; + volatile __u8 status_block_id; + volatile __u8 reserved0; + volatile __u16 attn_bits_index; + volatile __u32 reserved1; +}; + +struct cstorm_def_status_block_u { + volatile __u16 index_values[HC_USTORM_DEF_SB_NUM_INDICES]; + volatile __u16 status_block_index; + volatile __u8 func; + volatile __u8 status_block_id; + volatile __u32 __flags; +}; + +struct cstorm_def_status_block_c { + volatile __u16 index_values[HC_CSTORM_DEF_SB_NUM_INDICES]; + volatile __u16 status_block_index; + volatile __u8 func; + volatile __u8 status_block_id; + volatile __u32 __flags; +}; + +struct xstorm_def_status_block { + volatile __u16 index_values[HC_XSTORM_DEF_SB_NUM_INDICES]; + volatile __u16 status_block_index; + volatile __u8 func; + volatile __u8 status_block_id; + volatile __u32 __flags; +}; + +struct tstorm_def_status_block { + volatile __u16 index_values[HC_TSTORM_DEF_SB_NUM_INDICES]; + volatile __u16 status_block_index; + volatile __u8 func; + volatile __u8 status_block_id; + volatile __u32 __flags; +}; + +struct host_def_status_block { + struct atten_def_status_block atten_status_block; + struct cstorm_def_status_block_u u_def_status_block; + struct cstorm_def_status_block_c c_def_status_block; + struct xstorm_def_status_block x_def_status_block; + struct tstorm_def_status_block t_def_status_block; +}; + +#define HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS 1 +#define HC_INDEX_DEF_U_ETH_ISCSI_RX_BD_CONS 3 +#define HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS 5 + +/* TX Buffer descriptor */ +struct eth_tx_bd_flags { + __u8 as_bitfield; +#define ETH_TX_BD_FLAGS_VLAN_TAG (0x1<<0) +#define ETH_TX_BD_FLAGS_VLAN_TAG_SHIFT 0 +#define ETH_TX_BD_FLAGS_IP_CSUM (0x1<<1) +#define ETH_TX_BD_FLAGS_IP_CSUM_SHIFT 1 +#define ETH_TX_BD_FLAGS_L4_CSUM (0x1<<2) +#define ETH_TX_BD_FLAGS_L4_CSUM_SHIFT 2 +#define ETH_TX_BD_FLAGS_END_BD (0x1<<3) +#define ETH_TX_BD_FLAGS_END_BD_SHIFT 3 +#define ETH_TX_BD_FLAGS_START_BD (0x1<<4) +#define ETH_TX_BD_FLAGS_START_BD_SHIFT 4 +#define ETH_TX_BD_FLAGS_HDR_POOL (0x1<<5) +#define ETH_TX_BD_FLAGS_HDR_POOL_SHIFT 5 +#define ETH_TX_BD_FLAGS_SW_LSO (0x1<<6) +#define ETH_TX_BD_FLAGS_SW_LSO_SHIFT 6 +#define ETH_TX_BD_FLAGS_IPV6 (0x1<<7) +#define ETH_TX_BD_FLAGS_IPV6_SHIFT 7 +}; + +struct eth_tx_start_bd { + __u32 addr_lo; + __u32 addr_hi; + __u16 nbd; + __u16 nbytes; + __u16 vlan; + struct eth_tx_bd_flags bd_flags; + __u8 general_data; +#define ETH_TX_START_BD_HDR_NBDS (0x3F<<0) +#define ETH_TX_START_BD_HDR_NBDS_SHIFT 0 +#define ETH_TX_START_BD_ETH_ADDR_TYPE (0x3<<6) +#define ETH_TX_START_BD_ETH_ADDR_TYPE_SHIFT 6 +}; + +struct eth_tx_bd { + __u32 addr_lo; + __u32 addr_hi; + __u16 total_pkt_bytes; + __u16 nbytes; + __u8 reserved[4]; +}; + +/* RX Buffer descriptor */ +struct eth_rx_bd { + __u32 addr_lo; + __u32 addr_hi; +}; + +struct ramrod_data { + volatile __u32 data_lo; + volatile __u32 data_hi; +}; + +struct common_ramrod_eth_rx_cqe { + volatile __u8 ramrod_type; +#define COMMON_RAMROD_ETH_RX_CQE_TYPE (0x1<<0) +#define COMMON_RAMROD_ETH_RX_CQE_TYPE_SHIFT 0 +#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0 (0x7F<<1) +#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0_SHIFT 1 + volatile __u8 conn_type; + volatile __u16 reserved1; + volatile __u32 conn_and_cmd_data; +#define COMMON_RAMROD_ETH_RX_CQE_CID (0xFFFFFF<<0) +#define COMMON_RAMROD_ETH_RX_CQE_CID_SHIFT 0 +#define COMMON_RAMROD_ETH_RX_CQE_CMD_ID (0xFF<<24) +#define COMMON_RAMROD_ETH_RX_CQE_CMD_ID_SHIFT 24 + struct ramrod_data protocol_data; + __u32 reserved2[4]; +}; + +struct parsing_flags { + volatile __u16 flags; +}; + +struct eth_fast_path_rx_cqe { + volatile __u8 type_error_flags; +#define ETH_FAST_PATH_RX_CQE_TYPE (0x1<<0) +#define ETH_FAST_PATH_RX_CQE_TYPE_SHIFT 0 +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG (0x1<<1) +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT 1 +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG (0x1<<2) +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT 2 +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG (0x1<<3) +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT 3 +#define ETH_FAST_PATH_RX_CQE_START_FLG (0x1<<4) +#define ETH_FAST_PATH_RX_CQE_START_FLG_SHIFT 4 +#define ETH_FAST_PATH_RX_CQE_END_FLG (0x1<<5) +#define ETH_FAST_PATH_RX_CQE_END_FLG_SHIFT 5 +#define ETH_FAST_PATH_RX_CQE_RESERVED0 (0x3<<6) +#define ETH_FAST_PATH_RX_CQE_RESERVED0_SHIFT 6 + volatile __u8 status_flags; +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE (0x7<<0) +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE_SHIFT 0 +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG (0x1<<3) +#define ETH_FAST_PATH_RX_CQE_RSS_HASH_FLG_SHIFT 3 +#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG (0x1<<4) +#define ETH_FAST_PATH_RX_CQE_BROADCAST_FLG_SHIFT 4 +#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG (0x1<<5) +#define ETH_FAST_PATH_RX_CQE_MAC_MATCH_FLG_SHIFT 5 +#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG (0x1<<6) +#define ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG_SHIFT 6 +#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG (0x1<<7) +#define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG_SHIFT 7 + volatile __u8 placement_offset; + volatile __u8 queue_index; + volatile __u32 rss_hash_result; + volatile __u16 vlan_tag; + volatile __u16 pkt_len; + volatile __u16 len_on_bd; + struct parsing_flags pars_flags; + volatile __u16 sgl[8]; +}; + +struct eth_rx_cqe_next_page { + __u32 addr_lo; + __u32 addr_hi; + __u32 reserved[6]; +}; + +union eth_rx_cqe { + struct eth_fast_path_rx_cqe fast_path_cqe; + struct common_ramrod_eth_rx_cqe ramrod_cqe; + struct eth_rx_cqe_next_page next_page_cqe; +}; + +/****************************************************************************** + * BNX2X Registers and HSI + ******************************************************************************/ +#define BNX2X_BAR_SIZE 0x500000 +#define BNX2X_BAR2_SIZE 0x12000 + +#define BNX2X_CHIP_ID(bp) (bp->chip_id & 0xfffffff0) + +#define PORT_MAX 2 + +/* [R 4] This field indicates the type of the device. '0' - 2 Ports; '1' - 1 + * Port. */ +#define BNX2X_MISC_REG_BOND_ID 0xa400 +/* [R 8] These bits indicate the metal revision of the chip. This value + * starts at 0x00 for each all-layer tape-out and increments by one for each + * tape-out. */ +#define BNX2X_MISC_REG_CHIP_METAL 0xa404 +/* [R 16] These bits indicate the part number for the chip. */ +#define BNX2X_MISC_REG_CHIP_NUM 0xa408 +/* [R 4] These bits indicate the base revision of the chip. This value + * starts at 0x0 for the A0 tape-out and increments by one for each + * all-layer tape-out. */ +#define BNX2X_MISC_REG_CHIP_REV 0xa40c + +#define BNX2X_CHIP_NUM(bp) (bp->chip_id >> 16) +#define CHIP_NUM_57710 0x164e +#define CHIP_NUM_57711 0x164f +#define CHIP_NUM_57711E 0x1650 +#define CHIP_IS_E1(bp) (BNX2X_CHIP_NUM(bp) == CHIP_NUM_57710) +#define CHIP_IS_57711(bp) (BNX2X_CHIP_NUM(bp) == CHIP_NUM_57711) +#define CHIP_IS_57711E(bp) (BNX2X_CHIP_NUM(bp) == CHIP_NUM_57711E) +#define CHIP_IS_E1H(bp) (CHIP_IS_57711(bp) || \ + CHIP_IS_57711E(bp)) +#define IS_E1H_OFFSET CHIP_IS_E1H(bp) + +#define MISC_REG_SHARED_MEM_ADDR 0xa2b4 + +#define MISC_REG_BOND_ID 0xa400 +#define MISC_REG_CHIP_METAL 0xa404 +#define MISC_REG_CHIP_NUM 0xa408 +#define MISC_REG_CHIP_REV 0xa40c + +#define BAR_USTRORM_INTMEM 0x400000 +#define BAR_CSTRORM_INTMEM 0x410000 +#define BAR_XSTRORM_INTMEM 0x420000 +#define BAR_TSTRORM_INTMEM 0x430000 + +#define USTORM_RX_PRODS_OFFSET(port, client_id) \ + (IS_E1H_OFFSET ? (0x1000 + (port * 0x680) + (client_id * 0x40)) \ + : (0x4000 + (port * 0x360) + (client_id * 0x30))) + +#define SHMEM_P0_ISCSI_MAC_UPPER 0x4c +#define SHMEM_P0_ISCSI_MAC_LOWER 0x50 +#define SHMEM_P1_ISCSI_MAC_UPPER 0x1dc +#define SHMEM_P1_ISCSI_MAC_LOWER 0x1e0 + +#define SHMEM_ISCSI_MAC_UPPER(bp) \ + (((bp)->port == 0) ? SHMEM_P0_ISCSI_MAC_UPPER : SHMEM_P1_ISCSI_MAC_UPPER) + +#define SHMEM_ISCSI_MAC_LOWER(bp) \ + (((bp)->port == 0) ? SHMEM_P0_ISCSI_MAC_LOWER : SHMEM_P1_ISCSI_MAC_LOWER) + +#define BNX2X_RCQ_DESC_CNT (4096 / sizeof(union eth_rx_cqe)) +#define BNX2X_MAX_RCQ_DESC_CNT (BNX2X_RCQ_DESC_CNT - 1) + +#define BNX2X_RX_DESC_CNT (4096 / sizeof(struct eth_rx_bd)) +#define BNX2X_MAX_RX_DESC_CNT (BNX2X_RX_DESC_CNT - 2) + +#define BNX2X_TX_DESC_CNT (4096 / sizeof(struct eth_tx_start_bd)) +#define BNX2X_MAX_TX_DESC_CNT (BNX2X_TX_DESC_CNT - 1) + +#define BNX2X_NEXT_RX_IDX(x) ((((x) & (BNX2X_RX_DESC_CNT - 1)) == \ + (BNX2X_MAX_RX_DESC_CNT - 1)) ? (x) + 3 : (x) + 1) + +#define BNX2X_NEXT_RCQ_IDX(x) ((((x) & BNX2X_MAX_RCQ_DESC_CNT) == \ + (BNX2X_MAX_RCQ_DESC_CNT - 1)) ? (x) + 2 : (x) + 1) + +#define BNX2X_NEXT_TX_BD(x) (((x) & (BNX2X_MAX_TX_DESC_CNT - 1)) == \ + (BNX2X_MAX_TX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1 + +#define BNX2X_TX_RING_IDX(x) ((x) & BNX2X_MAX_TX_DESC_CNT) + +struct ustorm_eth_rx_producers { + __u16 cqe_prod; + __u16 bd_prod; + __u16 sge_prod; + __u16 reserved; +}; + +typedef struct bnx2x { + nic_t *parent; + + uint16_t flags; +#define CNIC_UIO_UNITIALIZED 0x0001 +#define CNIC_UIO_INITIALIZED 0x0002 +#define CNIC_UIO_ENABLED 0x0004 +#define CNIC_UIO_DISABLED 0x0008 +#define CNIC_UIO_IPv6_ENABLED 0x0010 +#define CNIC_UIO_ADDED_MULICAST 0x0020 +#define CNIC_UIO_MSIX_ENABLED 0x0200 +#define CNIC_UIO_TX_HAS_SENT 0x0400 + + void *reg; /* Pointer to the BAR1 mapped registers */ + void *reg2; /* Pointer to the BAR2 mapped registers */ + + int mem_fd; + + __u32 chip_id; + __u32 shmem_base; + int func; + int port; + + __u32 tx_doorbell; + + __u16 tx_prod; + __u16 tx_bd_prod; + __u16 tx_cons; + + __u32 rx_prod_io; + + __u16 rx_prod; + __u16 rx_bd_prod; + __u16 rx_cons; + __u16 rx_bd_cons; + + /* RX ring parameters */ + uint32_t rx_ring_size; + uint32_t rx_buffer_size; + + void *bufs; /* Pointer to the mapped buffer space */ + + /* Hardware Status Block locations */ + void *sblk_map; + struct host_def_status_block *status_blk; + + uint16_t rx_index; + union eth_rx_cqe *rx_comp_ring; + void **rx_pkt_ring; + + struct eth_tx_start_bd *tx_ring; + void *tx_pkt; + +} bnx2x_t; + +/****************************************************************************** + * bnx2x Function Declarations + ******************************************************************************/ +void bnx2x_start_xmit(nic_t *nic, size_t len); + +//struct nic_interface * bnx2x_find_nic_iface(nic_t * nic, +// uint16_t vlan_id); + +struct nic_ops * bnx2x_get_ops(); +#endif /* __BNX2X_H__*/ diff --git a/brcm_iscsi_uio/src/unix/libs/cnic.c b/brcm_iscsi_uio/src/unix/libs/cnic.c new file mode 100644 index 0000000..11b64a9 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/libs/cnic.c @@ -0,0 +1,238 @@ +/* cnic_nl.c: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uip_arp.h" +#include "nic.h" +#include "logger.h" +#include "options.h" +#include "uevent.h" + +#include "cnic.h" +#include "iscsi_if.h" + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "CNIC " + +/******************************************************************************* + * Constants shared between the bnx2 and bnx2x modules + ******************************************************************************/ +const char bnx2i_library_transport_name[] = "bnx2i"; +const size_t bnx2i_library_transport_name_size = + sizeof(bnx2i_library_transport_name); + +/****************************************************************************** + * Netlink Functions + ******************************************************************************/ + +static int cnic_arp_send(nic_t *nic, nic_interface_t *nic_iface, int fd, + __u8 * mac_addr, __u32 ip_addr, __u16 op) +{ + struct ether_header *eth; + struct ether_arp *arp; + __u32 dst_ip = ip_addr; + int pkt_size = sizeof(*eth) + sizeof(*arp); + int rc; + + rc = pthread_mutex_trylock(&nic->xmit_mutex); + + eth = (*nic->ops->get_tx_pkt)(nic); + arp = (struct ether_arp *)(eth + 1); + + if(rc != 0) { + LOG_DEBUG(PFX "%s: could not get xmit_mutex", nic->log_name); + return -EAGAIN; + } + + if (op == ARPOP_REQUEST) { + int i; + + for (i = 0; i < 6; i++) + eth->ether_dhost[i] = 0xff; + } else + memcpy(eth->ether_dhost, mac_addr, 6); + + memcpy(eth->ether_shost, nic->mac_addr, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_ARP); + arp->arp_hrd = htons(ARPHRD_ETHER); + arp->arp_pro = htons(ETHERTYPE_IP); + arp->arp_hln = ETH_ALEN; + arp->arp_pln = 4; + arp->arp_op = htons(op); + memcpy(arp->arp_sha, nic->mac_addr, ETH_ALEN); + + /* Copy the IP address's into the ARP response */ + memcpy(arp->arp_spa, nic_iface->ustack.hostaddr, 4); + memcpy(arp->arp_tpa, &dst_ip, 4); + + (*nic->nic_library->ops->start_xmit)(nic, pkt_size); + + LOG_DEBUG(PFX "%s: Sent cnic arp request", nic->log_name); + + return 0; +} + +static int cnic_nl_neigh_rsp(nic_t *nic, int fd, + struct iscsi_uevent *ev, + struct iscsi_path *path_req, + __u8 *mac_addr, + nic_interface_t *nic_iface, + int status) + +{ + int rc; + uint8_t *ret_buf; + struct iscsi_uevent *ret_ev; + struct iscsi_path *path_rsp; + struct sockaddr_nl dest_addr; + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; + dest_addr.nl_groups = 0; /* unicast */ + + ret_buf = calloc(1, NLMSG_SPACE(sizeof(struct iscsi_uevent) + 256)); + + memset(ret_buf, 0, NLMSG_SPACE(sizeof(struct iscsi_uevent) + 256)); + + /* prepare the iscsi_uevent buffer */ + ret_ev = (struct iscsi_uevent *)ret_buf; + ret_ev->type = ISCSI_UEVENT_PATH_UPDATE; + ret_ev->transport_handle = ev->transport_handle; + ret_ev->u.set_path.host_no = ev->r.req_path.host_no; + + /* Prepare the iscsi_path buffer */ + path_rsp = ret_buf + sizeof(*ret_ev); + path_rsp->handle = path_req->handle; + path_rsp->ip_addr_len = 4; + memcpy(&path_rsp->src.v4_addr, &nic_iface->ustack.hostaddr, + sizeof(nic_iface->ustack.hostaddr)); + memcpy(path_rsp->mac_addr, mac_addr, 6); + path_rsp->vlan_id = path_req->vlan_id; + path_rsp->pmtu = path_req->pmtu; + + rc = __kipc_call(fd, ret_ev, sizeof(*ret_ev) + sizeof(*path_rsp)); + if (rc > 0) { + LOG_ERR(PFX "neighbor reply sent back to kernel"); + } else { + LOG_ERR(PFX "send neighbor reply failed: %d", rc); + } + + free(ret_buf); + + return rc; +} + +/** + * cnic_handle_iscsi_path_req() - This function will handle the path req calls + * the bnx2i kernel module + * @param nic - The nic the message is directed towards + * @param fd - The file descriptor to be used to extract the private data + * @param ev - The iscsi_uevent + * @param buf - The private message buffer + * @param buf_len - The private message buffer length + */ +int cnic_handle_iscsi_path_req(nic_t *nic, int fd, struct iscsi_uevent *ev, + struct iscsi_path *path, size_t buf_len) +{ + nic_interface_t *nic_iface; + struct in_addr addr; + __u8 mac_addr[6]; + int rc; + uint16_t arp_retry; + int status = 0; + + LOG_DEBUG(PFX "%s: Netlink message with VLAN ID: %d, path MTU: %d " + "minor: %d", + nic->log_name, path->vlan_id, path->pmtu, 0/* TODO FIX */); + + pthread_mutex_lock(&nic_list_mutex); + + /* Find the proper interface via VLAN id */ + nic_iface = nic_find_nic_iface(nic, path->vlan_id); +// nic_iface = cnic_find_nic_iface(nic, path->vlan_id); + if (nic_iface == NULL) { + pthread_mutex_unlock(&nic_list_mutex); + LOG_ERR(PFX "%s: Couldn't find net_iface vlan_id: %d", + nic->log_name, path->vlan_id); + return -EINVAL; + } + + memcpy(&addr, &path->dst.v4_addr, sizeof(addr)); + +#define MAX_ARP_RETRY 4 + arp_retry = 0; + + rc = uip_lookup_arp_entry(addr.s_addr, mac_addr); + if (rc != 0) { + while ((arp_retry < MAX_ARP_RETRY) && (event_loop_stop == 0)) { + char *addr_str; + int count; + + addr_str = inet_ntoa(addr); + LOG_INFO(PFX "%s: Didn't find ip: %s\n", + nic->log_name, addr_str); + rc = cnic_arp_send(nic, nic_iface, fd, + mac_addr, addr.s_addr, + ARPOP_REQUEST); + if(rc != 0) { + status = -EIO; + goto done; + } + + for(count=0; count<4; count++) { + usleep(250000); + + rc = uip_lookup_arp_entry(addr.s_addr, + mac_addr); + if (rc == 0) + goto done; + } + + arp_retry++; + } + } + +done: + pthread_mutex_unlock(&nic_list_mutex); + + if(arp_retry >= MAX_ARP_RETRY) { + status = -EIO; + rc = -EIO; + } + + if(status != 0 || rc != 0) + pthread_mutex_unlock(&nic->xmit_mutex); + + cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr, + nic_iface, status); + + + return rc; +} diff --git a/brcm_iscsi_uio/src/unix/libs/cnic.h b/brcm_iscsi_uio/src/unix/libs/cnic.h new file mode 100644 index 0000000..af53b24 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/libs/cnic.h @@ -0,0 +1,27 @@ +/* cnic.h: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#ifndef __CNIC_NL_H__ +#define __CNIC_NL_H__ + +/******************************************************************************* + * Constants shared between the bnx2 and bnx2x modules + ******************************************************************************/ +extern const char bnx2i_library_transport_name[]; +extern const size_t bnx2i_library_transport_name_size; + +int cnic_nl_open(); +void cnic_nl_close(); + +int cnic_handle_iscsi_path_req(nic_t *nic, int, struct iscsi_uevent *, + struct iscsi_path *path, size_t len); + +#endif /* __CNIC_NL_H__ */ diff --git a/brcm_iscsi_uio/src/unix/logger.c b/brcm_iscsi_uio/src/unix/logger.c new file mode 100644 index 0000000..af15925 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/logger.c @@ -0,0 +1,175 @@ +/* logger.c: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include + +#include "options.h" +#include "logger.h" + +/****************************************************************************** + * Default logger values + ******************************************************************************/ +static const char default_logger_filename[] = "/var/log/brcm-iscsi.log"; + +struct logger main_log = { + .enabled = LOGGER_ENABLED, + .fp = NULL, + .log_file = (char *) default_logger_filename, + .level = LOG_LEVEL_INFO, + .lock = PTHREAD_MUTEX_INITIALIZER, + + .stats = { + .debug = 0, + .info = 0, + .warn = 0, + .error = 0, + + .last_log_time = 0, + }, +}; + +/****************************************************************************** + * Logger Functions + ******************************************************************************/ +/** + * log_stream() - Main logging function + * @param fp - FILE stream to write the log to + * @param level_str - log level string + * @param fmt - log format + * @param ap - variable argument lists + */ +void log_stream(FILE *fp, char *level_str, char *fmt, va_list ap) +{ + char time_buf[32]; + va_list va_cp; + + va_copy(va_cp, ap); + + pthread_mutex_lock(&main_log.lock); + main_log.stats.last_log_time = time(NULL); + strftime(time_buf, 26, "%a %b %d %T %Y", + localtime(&main_log.stats.last_log_time)); + fprintf(fp, "%s [%s]", level_str, time_buf); + vfprintf(fp, fmt, va_cp); + fprintf(fp, "\n"); + pthread_mutex_unlock(&main_log.lock); +} + + +void log_uip(char *level_str, char *fmt, ...) +{ + va_list ap, va_cp; + + va_start(ap, fmt); + va_copy(va_cp, ap); + + if(main_log.fp == NULL) + return; + + if(main_log.enabled == LOGGER_ENABLED) + log_stream(main_log.fp, level_str, fmt, va_cp); + + if(opt.debug == DEBUG_ON) { + log_stream(stdout, level_str, fmt, va_cp); + + /* Force the printing of the log file */ + fflush(main_log.fp); + + /* Force the printing of the log out to standard output */ + fflush(stdout); + } + va_end(ap); +} + +int backup_logger_settings(struct logger *src, struct logger *dest) +{ + dest->level = src->level; + + dest->log_file = malloc(strlen(src->log_file) + 1); + if(dest->log_file == NULL) { + LOG_ERR("Could not allocate memory for log file path for backup"); + return -ENOMEM; + } + + return 0; +} + +int restore_backup(struct logger *src, struct logger *dest) +{ + if(dest->log_file != NULL) + { + free(dest->log_file); + } + dest->log_file = src->log_file; + + return 0; +} + + + +/****************************************************************************** + * Initialize/Clean up routines + ******************************************************************************/ +/** + * init_logger() - Prepare the logger + * @param filename - path to where the log will be written to + * @return 0 on success, <0 on failure + */ +int init_logger(char *filename) +{ + int rc = 0; + + pthread_mutex_lock(&main_log.lock); + + main_log.fp = fopen( filename, "a"); + if( main_log.fp == NULL) { + printf("Could not create log file: %s <%s>\n", + filename, strerror(errno)); + rc = -EIO; + } + + pthread_mutex_unlock(&main_log.lock); + + LOG_INFO("Initialize logger using log file: %s", filename); + + return rc; +} + +void fini_logger(int type) +{ + pthread_mutex_lock(&main_log.lock); + + if ( main_log.fp != NULL ) { + fclose(main_log.fp); + main_log.fp = NULL; + + if(opt.debug == DEBUG_ON) { + printf("Closed logger\n"); + fflush(stdout); + } + } + + if(type == SHUTDOWN_LOGGER) { + if ( (main_log.log_file != NULL) && + (main_log.log_file != default_logger_filename)) { + free (main_log.log_file ); + main_log.log_file = NULL; + } + } + + main_log.enabled = LOGGER_DISABLED; + + pthread_mutex_unlock(&main_log.lock); +} diff --git a/brcm_iscsi_uio/src/unix/logger.h b/brcm_iscsi_uio/src/unix/logger.h new file mode 100644 index 0000000..7035526 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/logger.h @@ -0,0 +1,83 @@ +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +#include +#include +#include +#include +#include + +/******************************************************************************* + * Logger Levels + ******************************************************************************/ +#define LOG_LEVEL_DEBUG 4 +#define LOG_LEVEL_INFO 3 +#define LOG_LEVEL_WARN 2 +#define LOG_LEVEL_ERR 1 +#define LOG_LEVEL_UNKNOWN 0 + +#define LOG_LEVEL_DEBUG_STR "DBG " +#define LOG_LEVEL_INFO_STR "INFO " +#define LOG_LEVEL_WARN_STR "WARN " +#define LOG_LEVEL_ERR_STR "ERR " +#define LOG_LEVEL_UNKNOWN_STR "? " + +/******************************************************************************* + * Logging Macro's + ******************************************************************************/ +#define LOG_DEBUG(fmt, args...) { if (LOG_LEVEL_DEBUG <= main_log.level) { \ + log_uip(LOG_LEVEL_DEBUG_STR, fmt, ##args);\ + } } + +#define LOG_INFO(fmt, args...) { if (LOG_LEVEL_INFO <= main_log.level) { \ + log_uip(LOG_LEVEL_INFO_STR, fmt, ##args); \ + } } +#define LOG_WARN(fmt, args...) { if (LOG_LEVEL_WARN <= main_log.level) { \ + log_uip(LOG_LEVEL_WARN_STR, fmt, ##args); \ + } } +#define LOG_ERR(fmt, args...) { if (LOG_LEVEL_ERR <= main_log.level) { \ + log_uip(LOG_LEVEL_ERR_STR, fmt, ##args); \ + } } + +/******************************************************************************* + * Logging Statistics + ******************************************************************************/ +struct logger_stats { + uint64_t debug; + uint64_t info; + uint64_t warn; + uint64_t error; + + time_t last_log_time; +}; + +/******************************************************************************* + * Logger Structure + ******************************************************************************/ +struct logger { + FILE *fp; + char *log_file; + int8_t level; + +#define LOGGER_ENABLED 0x01 +#define LOGGER_DISABLED 0x02 + int8_t enabled; + + pthread_mutex_t lock; + + struct logger_stats stats; +}; + +extern struct logger main_log; + +int init_logger(char *); +void log_uip(char *level_str, char *fmt, ...); +void fini_logger(); + +int backup_logger_settings(struct logger *src, struct logger *dest); +int restore_backup(struct logger *src, struct logger *dest); + +#define CLOSE_LOGGER 0x01 +#define SHUTDOWN_LOGGER 0x02 + +#endif diff --git a/brcm_iscsi_uio/src/unix/main.c b/brcm_iscsi_uio/src/unix/main.c new file mode 100644 index 0000000..bf307ff --- /dev/null +++ b/brcm_iscsi_uio/src/unix/main.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2001, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: main.c,v 1.16 2006/06/11 21:55:03 adam Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uip.h" +#include "uip_arp.h" +#include "uip_eth.h" + +#include "timer.h" + +#include "build_date.h" +#include "config.h" +#include "logger.h" +#include "nic.h" +#include "nic_id.h" +#include "nic_nl.h" +#include "nic_utils.h" +#include "options.h" +#include "packet.h" +#include "uevent.h" + +#include "dhcpc.h" + +#include "iscsid_ipc.h" +#include "brcm_iscsi.h" + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "main " + +static char default_pid_filepath[] = "/var/run/brcm_iscsiuio.pid"; + +/******************************************************************************* + * Global Variables + ******************************************************************************/ +static const struct option long_options[] = { + {"debug", 0, 0, 0}, + {"version", 0, 0, 0}, + {"help", 0, 0, 0}, + {0, 0, 0, 0} +}; + +struct options opt = { + .debug = DEBUG_OFF, +}; + +int event_loop_stop = 0; + +/** + * cleanup() - This function is called when this program is to be closed + * This function will clean up all the cnic uio interfaces and + * flush/close the logger + */ +static void cleanup() +{ + iscsid_cleanup(); + + cleanup_uevent_netlink_sock(); + + nic_close_all(); + + unload_all_nic_libraries(); + + LOG_INFO("Done waiting for cnic's/stacks to gracefully close"); + + fini_logger(SHUTDOWN_LOGGER); +} + +/** + * signal_handle_thread() - This is the signal handling thread of this program + * This is the only thread which will handle signals. + * All signals are routed here and handled here to + * provide consistant handling. + */ +static pthread_t signal_thread; +static void * signal_handle_thread(void *arg) +{ + sigset_t set; + int rc; + int signal; + + sigfillset(&set); + + LOG_INFO("signal handling thread ready"); + + rc = sigwait(&set, &signal); + + event_loop_stop = 1; + switch(signal) + { + case SIGINT: + LOG_INFO("Caught SIGINT signal"); + break; + } + + LOG_INFO("terminating..."); + + cleanup(); + exit(EXIT_SUCCESS); +} + +static void show_version() +{ + printf("%s: Version '%s', Build Date: '%s'\n", + APP_NAME, PACKAGE_VERSION, build_date); +} + +static void main_usage() +{ + show_version(); + + printf("\nUsage: %s [OPTION]\n", APP_NAME); + printf("\ +Open-iSCSI initiator daemon.\n\ + -f, --foreground make the program run in the foreground\n\ + -d, --debug debuglevel print debugging information\n\ + -p, --pid=pidfile use pid file (default %s ).\n\ + -h, --help display this help and exit\n\ + -v, --version display version and exit\n\ +", default_pid_filepath); +} + +static void daemon_init() +{ + int fd; + + fd = open("/dev/null", O_RDWR); + if (fd == -1) { + exit(-1); + } + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + setsid(); + chdir("/"); +} + +/******************************************************************************* + * Main routine + ******************************************************************************/ +int main(int argc, char *argv[]) +{ + int rc; + nic_t *nic; + sigset_t set; + char *pid_file = default_pid_filepath; + int fd; + int foreground=0; + pid_t pid; + + /* Record the start time for the user space daemon */ + opt.start_time = time(NULL); + + /* parse the parameters */ + while (1) { + int c, option_index; + + c = getopt_long(argc, argv, "fd:p:vh", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + + case 'f': + foreground = 1; + break; + + /* Enable debugging mode */ + case 'd': + main_log.level = atoi(optarg); + opt.debug = DEBUG_ON; + break; + case 'p': + pid_file = optarg; + break; + case 'v': + show_version(); + exit(EXIT_SUCCESS); + case 'h': + default: + main_usage(); + exit(EXIT_SUCCESS); + } + } + + if(!foreground) { + char buf[64]; + + fd = open(pid_file, O_WRONLY|O_CREAT, 0644); + if (fd < 0) { + printf("Unable to create pid file: %s", pid_file); + exit(1); + } + + pid = fork(); + if (pid < 0) { + printf("Starting daemon failed"); + exit(1); + } else if (pid) { + exit(0); + } + + chdir("/"); + if (lockf(fd, F_TLOCK, 0) < 0) { + printf("Unable to lock pid file: %s [%s]", + pid_file, strerror(errno)); + exit(1); + } + + ftruncate(fd, 0); + sprintf(buf, "%d\n", getpid()); + write(fd, buf, strlen(buf)); + + daemon_init(); + } + + if(main_log.enabled == LOGGER_ENABLED) { + /* initialize the logger */ + rc = init_logger(main_log.log_file); + if (rc != 0) { + printf("Could not initialize the logger\n"); + goto error; + } + } + + LOG_INFO("Started BRCM iSCSI stack: Ver " PACKAGE_VERSION); + LOG_INFO("Build date: %s", build_date); + + if (opt.debug == DEBUG_ON) { + LOG_INFO("Debug mode enabled"); + } + + /* Load the NIC libraries */ + rc = load_all_nic_libraries(); + if(rc != 0) { + goto error; + } + + /* Initialze the iscsid listener */ + rc = iscsid_init(); + if (rc != 0) { + goto error; + } + + /* Setup the watching of uio devices */ + rc = init_uevent_netlink_sock(); + if(rc != 0) { + goto error; + } + + brcm_iscsi_init(); + + /* ensure we don't see any signals */ + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGQUIT); + sigaddset(&set, SIGTERM); + rc = pthread_sigmask(SIG_SETMASK, &set, NULL); + + /* Spin off the signal handling thread */ + rc = pthread_create(&signal_thread, NULL, signal_handle_thread, NULL); + if (rc != 0) { + LOG_ERR("Could not create singal handling thread"); + } + + pthread_mutex_lock(&nic_list_mutex); + /* Start to spin off the nic threads */ + nic = nic_list; + while(nic != NULL) { + prepare_nic(nic); + nic = nic->next; + } + + pthread_mutex_unlock(&nic_list_mutex); + + /* Using sysfs to discover iSCSI hosts */ + nic_discover_iscsi_hosts(); + + /* NetLink connection to listen to NETLINK_ISCSI private messages */ + nic_nl_open(); + + error: + cleanup(); + exit(EXIT_FAILURE); +} diff --git a/brcm_iscsi_uio/src/unix/nic.c b/brcm_iscsi_uio/src/unix/nic.c new file mode 100644 index 0000000..af1b5fd --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic.c @@ -0,0 +1,1128 @@ +/* nic.c: Generic NIC management/utility functions + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcpc.h" + +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" +#include "options.h" + +#include "uip.h" +#include "uip_arp.h" +#include "uip_eth.h" +#include "uip-neighbor.h" + +#include "bnx2.h" +#include "bnx2x.h" + +/****************************************************************************** + * Constants + *****************************************************************************/ +#define PFX "nic " + +/****************************************************************************** + * Global variables + *****************************************************************************/ +/* Used to store a list of NIC libraries */ +pthread_mutex_t nic_lib_list_mutex = PTHREAD_MUTEX_INITIALIZER; +nic_lib_handle_t *nic_lib_list; + +/* Used to store a list of active cnic devices */ +pthread_mutex_t nic_list_mutex = PTHREAD_MUTEX_INITIALIZER; +nic_t *nic_list = NULL; + +/****************************************************************************** + * Functions to handle NIC libraries + *****************************************************************************/ +/** + * alloc_nic_library_handle() - Used to allocate a NIC library handle + * @return NULL if memory couldn't be allocated, pointer to the handle + * to the NIC library handle + */ +static nic_lib_handle_t * alloc_nic_library_handle() +{ + nic_lib_handle_t *handle; + + handle = malloc(sizeof(*handle)); + if(handle == NULL) + return NULL; + + memset(handle, 0, sizeof(*handle)); + handle->ops = NULL; + + pthread_mutex_init(&handle->mutex, NULL); + + return handle; +} + +static void free_nic_library_handle(nic_lib_handle_t *handle) +{ + free(handle); +} + +/** + * load_nic_library() - This function is used to load a NIC library + * @param handle - This is the library handle to load + * @return 0 = Success; <0 = failure + */ +static int load_nic_library(nic_lib_handle_t *handle) +{ + int rc; + char *library_name; + size_t library_name_size; + char *library_version; + size_t library_version_size; + char *build_date_str; + size_t build_date_str_size; + + pthread_mutex_lock(&handle->mutex); + + /* Validate the NIC ops table ensure that all the fields are not NULL */ + if((handle->ops->open) == NULL || + (handle->ops->close) == NULL || + (handle->ops->read) == NULL || + (handle->ops->write) == NULL || + (handle->ops->clear_tx_intr == NULL)) { + LOG_ERR("Invalid NIC ops table: open: 0x%x, close: 0x%x," + "read: 0x%x, write: 0x%x clear_tx_intr: 0x%x " + "lib_ops: 0x%x", + handle->ops->open, handle->ops->close, + handle->ops->read, handle->ops->write, + handle->ops->clear_tx_intr, + handle->ops->lib_ops); + rc = -EINVAL; + handle->ops = NULL; + goto error; + } + + /* Validate the NIC library ops table to ensure that all the proper + * fields are filled */ + if((handle->ops->lib_ops.get_library_name == NULL) || + (handle->ops->lib_ops.get_pci_table == NULL) || + (handle->ops->lib_ops.get_library_version == NULL) || + (handle->ops->lib_ops.get_build_date == NULL) || + (handle->ops->lib_ops.get_transport_name == NULL)) { + rc =-EINVAL; + goto error; + } + + (*handle->ops->lib_ops.get_library_name)(&library_name, + &library_name_size); + (*handle->ops->lib_ops.get_library_version)(&library_version, + &library_version_size); + (*handle->ops->lib_ops.get_build_date)(&build_date_str, + &build_date_str_size); + + LOG_DEBUG("Loaded nic library '%s' Version: '%s' build on %s'", + library_name, library_version, build_date_str); + + pthread_mutex_unlock(&handle->mutex); + + return 0; + +error: + pthread_mutex_unlock(&handle->mutex); + + return rc; +} + +static struct nic_ops * (*nic_get_ops[])() = { + bnx2_get_ops, + bnx2x_get_ops, +}; + +int load_all_nic_libraries() +{ + int rc, i = 0; + nic_lib_handle_t *handle; + + for(i=0; i < sizeof(nic_get_ops) / sizeof(nic_get_ops[0]); i++) { + /* Add the CNIC library */ + handle = alloc_nic_library_handle(); + if(handle == NULL) { + LOG_ERR("Could not allocate memory for CNIC nic lib"); + return -ENOMEM; + } + + handle->ops = (*nic_get_ops[i])(); + + rc = load_nic_library(handle); + if(rc != 0) + return rc; + + /* Add the CNIC library to the list of library handles */ + pthread_mutex_lock(&nic_lib_list_mutex); + + /* Add this library to the list of nic libraries we + * know about */ + if(nic_lib_list == NULL) { + nic_lib_list = handle; + } else { + nic_lib_handle_t *current = nic_lib_list; + + while(current->next != NULL) { + current = current->next; + } + + current->next = handle; + } + pthread_mutex_unlock(&nic_lib_list_mutex); + + LOG_DEBUG("Added '%s' nic library", handle->ops->description); + } + + return rc; +} + +int unload_all_nic_libraries() { + nic_lib_handle_t *current, *next; + + pthread_mutex_lock(&nic_lib_list_mutex); + current = nic_lib_list; + + while(current != NULL) { + next = current->next; + free_nic_library_handle(current); + + current = next; + } + + pthread_mutex_unlock(&nic_lib_list_mutex); + + nic_lib_list = NULL; + + return 0; +} + +NIC_LIBRARY_EXIST_T does_nic_uio_name_exist(char *name) +{ + NIC_LIBRARY_EXIST_T rc; + nic_lib_handle_t *current; + + pthread_mutex_lock(&nic_lib_list_mutex); + current = nic_lib_list; + + while(current != NULL) { + char *uio_name; + size_t uio_name_size; + + (*current->ops->lib_ops.get_uio_name)(&uio_name, + &uio_name_size); + + if(strncmp(name, uio_name, uio_name_size) == 0) { + rc = NIC_LIBRARY_EXSITS; + goto done; + } + + current = current->next; + } + + rc = NIC_LIBRARY_DOESNT_EXIST; + +done: + pthread_mutex_unlock(&nic_lib_list_mutex); + return rc; +} + +NIC_LIBRARY_EXIST_T does_nic_library_exist(char *name) +{ + NIC_LIBRARY_EXIST_T rc; + nic_lib_handle_t *current; + + pthread_mutex_lock(&nic_lib_list_mutex); + current = nic_lib_list; + + while(current != NULL) { + char *library_name; + size_t library_name_size; + + (*current->ops->lib_ops.get_library_name)(&library_name, + &library_name_size); + + if(strncmp(name, library_name, library_name_size) == 0) { + rc = NIC_LIBRARY_EXSITS; + goto done; + } + + current = current->next; + } + + rc = NIC_LIBRARY_DOESNT_EXIST; + +done: + pthread_mutex_unlock(&nic_lib_list_mutex); + return rc; +} + +/** + * find_nic_lib_using_pci_id() - Find the proper NIC library using the + * PCI ID's + * @param vendor - PCI vendor ID to search on + * @param device - PCI device ID to search on + * @param subvendor - PCI subvendor ID to search on + * @param subdevice - PCI subdevice ID to search on + * @param handle - This function will return the nic lib handle if found + * @return 0 if found, <0 not found + */ +int find_nic_lib_using_pci_id(uint32_t vendor, uint32_t device, + uint32_t subvendor, uint32_t subdevice, + nic_lib_handle_t **handle, + struct pci_device_id **pci_entry) +{ + int rc; + nic_lib_handle_t *current; + + pthread_mutex_lock(&nic_lib_list_mutex); + current = nic_lib_list; + + while(current != NULL) { + struct pci_device_id *pci_table; + uint32_t entries; + int i; + + current->ops->lib_ops.get_pci_table(&pci_table, &entries); + + /* Sanity check the the pci table coming from the + * hardware library */ + if(entries > MAX_PCI_DEVICE_ENTRIES) { + LOG_WARN(PFX "Too many pci_table entries(%d) skipping", + entries); + continue; + } + + for(i=0; inext; + } + rc = -EINVAL; + +done: + pthread_mutex_unlock(&nic_lib_list_mutex); + + return rc; +} + +/** + * nic_init() - This will properly initialize a struct cnic_uio device + * @return NULL is there is a failure and pointer to an allocated/initialized + * struct cnic_uio on success + */ +nic_t *nic_init() +{ + nic_t *nic; + + nic = malloc(sizeof(*nic)); + if (nic == NULL) { + LOG_ERR("Coudln't malloc space for nic"); + return NULL; + } + + memset(nic, 0, sizeof(*nic)); + nic->uio_minor = -1; + nic->fd = INVALID_FD; + nic->next = NULL; + nic->thread = INVALID_THREAD; + nic->flags |= NIC_UNITIALIZED | NIC_DISABLED; + nic->state |= NIC_STOPPED; + nic->free_packet_queue = NULL; + nic->tx_packet_queue = NULL; + nic->nic_library = NULL; + nic->pci_id = NULL; + + pthread_mutex_init(&nic->uio_wait_mutex, NULL); + pthread_cond_init(&nic->uio_wait_cond, NULL); + + pthread_mutex_init(&nic->enable_wait_mutex, NULL); + pthread_cond_init(&nic->enable_wait_cond, NULL); + + pthread_mutex_init(&nic->enable_done_mutex, NULL); + pthread_cond_init(&nic->enable_done_cond, NULL); + + pthread_mutex_init(&nic->nic_loop_started_mutex, NULL); + pthread_cond_init(&nic->nic_loop_started_cond, NULL); + + pthread_mutex_init(&nic->disable_wait_mutex, NULL); + pthread_cond_init(&nic->disable_wait_cond, NULL); + + pthread_mutex_init(&nic->xmit_mutex, NULL); + pthread_mutex_init(&nic->nic_iface_mutex, NULL); + + pthread_mutex_init(&nic->free_packet_queue_mutex, NULL); + + nic->rx_poll_usec = DEFAULT_RX_POLL_USEC; + + /* Add this device to our list of nics */ + pthread_mutex_lock(&nic_list_mutex); + if(nic_list == NULL) { + nic_list = nic; + } else { + nic_t *current = nic_list; + + while (current->next != NULL) { + current = current->next; + } + + current->next = nic; + } + + pthread_mutex_unlock(&nic_list_mutex); + + return nic; +} + +int nic_remove(nic_t *nic, int locked) +{ + int rc; + nic_t *prev, *current; + + if(nic->ops) + nic->ops->close(nic, 0); + + nic->state = NIC_EXIT; + rc = pthread_cancel(nic->thread); + if(rc != 0) + LOG_ERR(PFX "%s: Coudln't send cancel to nic", nic->log_name); + + rc = pthread_join(nic->thread, NULL); + if(rc != 0) + LOG_ERR(PFX "%s: Coudln't join to canceled nic thread", + nic->log_name); + + nic->thread == INVALID_THREAD; + + if(!locked) + pthread_mutex_lock(&nic_list_mutex); + + current = prev = nic_list; + while(current != NULL) { + if(current == nic) + break; + + prev = current; + current = current->next; + } + + if (current != NULL) { + if (current == nic_list) + nic_list = current->next; + else + prev->next = current->next; + + free(nic); + } else { + LOG_ERR(PFX "%s: Coudln't find nic", nic->log_name); + } + + if(!locked) + pthread_mutex_unlock(&nic_list_mutex); +} + +/** + * nic_close() - Used to indicate to a NIC that it should close + * @param nic - the nic to close + * @param graceful - ALLOW_GRACEFUL_SHUTDOWN will check the nic state + * before proceeding to close() + * FORCE_SHUTDOWN will force the nic to close() + * reguardless of the state + */ +void nic_close(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + int rc; + nic_interface_t *nic_iface; + + if((nic->flags & NIC_DISABLED) && + (graceful == ALLOW_GRACEFUL_SHUTDOWN)) + return; + + /* The NIC could be configured by the uIP config file + * but not assoicated with a hardware library just yet + * we will need to check for this */ + if(nic->ops == NULL) { + LOG_WARN(PFX "%s: when closing nic->ops == NULL", + nic->log_name); + return; + } + + rc = (*nic->ops->close)(nic, graceful); + if(rc != 0) { + LOG_ERR(PFX "%s: Could not close nic", nic->log_name); + } else { + nic->state = NIC_STOPPED; + nic->flags &= ~NIC_ENABLED; + nic->flags |= NIC_DISABLED; + } + + pthread_mutex_lock(&nic->nic_iface_mutex); + + nic_iface = nic->nic_iface; + while(nic_iface != NULL) + { + uip_reset(&nic_iface->ustack); + nic_iface = nic_iface->next; + } + pthread_mutex_unlock(&nic->nic_iface_mutex); +} + + +/** + * net_iface_init() - This function is used to add an interface to the + * structure cnic_uio + * @return 0 on success, <0 on failure + */ +nic_interface_t * nic_iface_init() +{ + nic_interface_t *nic_iface = malloc(sizeof(*nic_iface)); + if(nic_iface == NULL) + { + LOG_ERR("Could not allocate space for nic iface"); + return NULL; + } + + memset(nic_iface, 0, sizeof(*nic_iface)); + nic_iface->next = NULL; + + return nic_iface; +} + +/** + * nic_add_net_iface() - This function is used to add an interface to the + * nic structure + * @param nic - struct nic device to add the interface to + * @param nic_iface - network interface used to add to the nic + * @return 0 on success, <0 on failure + */ +int nic_add_nic_iface(nic_t *nic, + nic_interface_t *nic_iface) +{ + + pthread_mutex_lock(&nic->nic_iface_mutex); + + /* Add the nic_interface */ + if(nic->nic_iface == NULL) { + nic->nic_iface = nic_iface; + } else { + nic_interface_t *current = nic->nic_iface; + + while(current->next != NULL) + { + current = current->next; + } + + current->next = nic_iface; + } + + /* Set nic_interface common fields */ + nic_iface->parent = nic; + nic->num_of_nic_iface++; + + pthread_mutex_unlock(&nic->nic_iface_mutex); + + return 0; +} + +/****************************************************************************** + * Routine to process interrupts from the NIC device + ******************************************************************************/ +/** + * nic_process_intr() - Routine used to process interrupts from the hardware + * @param nic - NIC hardware to process the interrupt on + * @return 0 on success, <0 on failure + */ +int nic_process_intr(nic_t *nic, int discard_check) +{ + fd_set fdset; + int ret; + int count; + struct timeval tv; + + /* Simple sanity checks */ + if ((discard_check != 1) && + (nic->state & NIC_RUNNING) != NIC_RUNNING) { + LOG_ERR(PFX "%s: Couldn't process interupt NIC not running", + nic->log_name); + return -EBUSY; + } + + if((discard_check != 1) && + (nic->fd == INVALID_FD)) { + LOG_ERR(PFX "%s: NIC fd not valid", nic->log_name); + return -EIO; + } + + FD_ZERO(&fdset); + FD_SET(nic->fd, &fdset); + + tv.tv_sec = 0; + if(nic->state & NIC_LONG_SLEEP) { + tv.tv_usec = 1000; + } else { + tv.tv_usec = nic->rx_poll_usec; + } + + /* Wait for an interrupt to come in or timeout */ + ret = select(nic->fd + 1, &fdset, NULL, NULL, &tv); + switch(ret) + { + case 1: + /* Usually there should only be one file descriptor ready + * to read */ + break; + case 0: + return ret; + case -1: + LOG_ERR(PFX "%s: error waiting for interrupt: %s", + nic->log_name, strerror(errno)); + return 0; + default: + LOG_ERR(PFX "%s: unknown number of FD's, ignoring: %d ret", + nic->log_name, ret); + return 0; + } + + ret = read(nic->fd, &count, sizeof(count)); + if (ret > 0) { + nic->stats.interrupts++; + LOG_DEBUG(PFX "%s: interrupt count: %d prev: %d", + nic->log_name, count, nic->intr_count); + + if(count == nic->intr_count) { + LOG_ERR(PFX "%s: got interrupt but count still the " + "same", + nic->log_name, count); + return 0; + } + + /* Check if we missed an interrupt. With UIO, + * the count should be incremental */ + if(count != nic->intr_count + 1) { + nic->stats.missed_interrupts++; + LOG_ERR(PFX "%s: Missed interrupt! on %d not %d", + nic->log_name, count, nic->intr_count); + } + + nic->intr_count = count; + + (*nic->ops->clear_tx_intr)(nic); + ret = 1; + } + + return ret; +} + +static void prepare_ipv4_packet(nic_t *nic, + nic_interface_t *nic_iface, + struct uip_stack *ustack, + packet_t *pkt) +{ + u16_t ipaddr[2]; + arp_table_query_t arp_query; + dest_ipv4_addr_t dest_ipv4_addr; + struct arp_entry *tabptr; + int queue_rc; + + dest_ipv4_addr = uip_determine_dest_ipv4_addr(ustack, ipaddr); + if(dest_ipv4_addr == LOCAL_BROADCAST) + { + uip_build_eth_header(ustack, ipaddr, NULL, pkt); + return; + } + + arp_query = is_in_arp_table(ipaddr, &tabptr); + + switch(arp_query) { + case IS_IN_ARP_TABLE: + uip_build_eth_header(ustack, + ipaddr, + tabptr, + pkt); + break; + case NOT_IN_ARP_TABLE: + queue_rc = nic_queue_tx_packet(nic, + nic_iface, + pkt); + uip_build_arp_request(ustack, ipaddr); + break; + default: + LOG_ERR("Unknown arp state"); + break; + } +} + +static void prepare_ustack(nic_t *nic, + nic_interface_t *nic_iface, + struct uip_stack *ustack, + struct packet * pkt) +{ + ustack->uip_buf = pkt->buf; + ustack->uip_len = pkt->buf_size; + + pkt->nic = nic; + pkt->nic_iface = nic_iface; + + ustack->data_link_layer = pkt->buf; + /* Adjust the network layer pointer depending if + * there is a VLAN tag or not, or if the hardware + * has stripped out the + * VLAN tag */ + if((nic_iface->vlan_id == 0) || + (NIC_VLAN_STRIP_ENABLED & nic->flags)) { + ustack->network_layer = ustack->data_link_layer + + sizeof(struct uip_eth_hdr); + } else { + ustack->network_layer = ustack->data_link_layer + + sizeof(struct uip_vlan_eth_hdr); + } +} + +static int check_timers(nic_t *nic, + struct timer *periodic_timer, + struct timer *arp_timer, + uint8_t take_iface_mutex) +{ + if (timer_expired(periodic_timer)) { + int i; + nic_interface_t *current; + + timer_reset(periodic_timer); + + if(take_iface_mutex) + pthread_mutex_lock(&nic->nic_iface_mutex); + + current = nic->nic_iface; + while(current != NULL) + { + packet_t *pkt; + struct uip_stack *ustack = ¤t->ustack; + + pkt = get_next_free_packet(nic); + if(pkt == NULL) + { + continue; + } + + + for (i = 0; i < UIP_CONNS; i++) { + prepare_ustack(nic, + current, + ustack, + pkt); + + uip_periodic(ustack, i); + /* If the above function invocation resulted + * in data that should be sent out on the + * network, the global variable uip_len + * is set to a value > 0. */ + if (ustack->uip_len > 0) { + pkt->buf_size = ustack->uip_len; + + prepare_ipv4_packet(nic, + current, + ustack, + pkt); + + (*nic->ops->write)(nic, current, pkt); + } + } + + for (i = 0; i < UIP_UDP_CONNS; i++) { + prepare_ustack(nic, + current, + ustack, + pkt); + + uip_udp_periodic(ustack, i); + /* If the above function invocation resulted + * in data that should be sent out on the + * network, the global variable uip_len is + * set to a value > 0. */ + if (ustack->uip_len > 0) { + pkt->buf_size = ustack->uip_len; + + prepare_ipv4_packet(nic, + current, + ustack, + pkt); + + (*nic->ops->write)(nic, current, pkt); + } + } + + /* Call the ARP timer function every 10 seconds. */ + if (timer_expired(arp_timer)) { + timer_reset(arp_timer); + uip_arp_timer(); + } + + put_packet_in_free_queue(pkt, nic); + + current = current->next; + } + + if(take_iface_mutex) + pthread_mutex_unlock(&nic->nic_iface_mutex); + } + + return 0; +} + +int process_packets(nic_t *nic, + struct timer *periodic_timer, + struct timer *arp_timer, + nic_interface_t *nic_iface) +{ + int rc; + packet_t *pkt; + + pkt = get_next_free_packet(nic); + if(pkt == NULL) + return -ENOMEM; + + rc = (*nic->ops->read)(nic, pkt); + if (rc != 0) { + uint16_t type; + struct uip_stack *ustack; + + /* check if we have the given VLAN interface */ + if(nic_iface == NULL) { + nic_iface = nic_find_nic_iface(nic, pkt->vlan_tag); + if(nic_iface == NULL) + { + LOG_ERR(PFX "%s: Couldn't find interface for " + "VLAN: %d", + nic->log_name, pkt->vlan_tag); + rc =0; + goto done; + } + } + + pkt->nic_iface = nic_iface; + + ustack = &nic_iface->ustack; + + ustack->uip_buf = pkt->buf; + ustack->uip_len = pkt->buf_size; + ustack->data_link_layer = pkt->buf; + + pkt->data_link_layer = pkt->buf; + + /* Adjust the network layer pointer depending if there is a + * VLAN tag or not, or if the hardware has stripped out the + * VLAN tag */ + if((pkt->vlan_tag == 0) || + (NIC_VLAN_STRIP_ENABLED & nic->flags)) { + ustack->network_layer = ustack->data_link_layer + + sizeof(struct uip_eth_hdr); + pkt->network_layer = pkt->data_link_layer + + sizeof(struct uip_eth_hdr); + type = ntohs(ETH_BUF(pkt->buf)->type); + } else { + ustack->network_layer = ustack->data_link_layer + + sizeof(struct uip_vlan_eth_hdr); + pkt->network_layer = pkt->data_link_layer + + sizeof(struct uip_vlan_eth_hdr); + type = ntohs(VLAN_ETH_BUF(pkt->buf)->type); + } + + /* determine how we should process this packet based on the + * ethernet type */ + switch(type) { + case UIP_ETHTYPE_IPv6: + case UIP_ETHTYPE_IPv4: + uip_arp_ipin(); + uip_input(ustack); + /* If the above function invocation resulted + * in data that should be sent out on the + * network, the global variable uip_len is + * set to a value > 0. */ + if (ustack->uip_len > 0) { + if(is_ipv6(ustack)) + uip_neighbor_out(ustack); + else + prepare_ipv4_packet(nic, + nic_iface, + ustack, + pkt); + + (*nic->ops->write)(nic, nic_iface, pkt); + } + break; + case UIP_ETHTYPE_ARP: + uip_arp_arpin(ustack, pkt); + + /* If the above function invocation resulted + * in data that should be sent out on the + * network, the global variable uip_len + * is set to a value > 0. */ + if (pkt->buf_size > 0) { + + (*nic->ops->write)(nic, nic_iface, pkt); + } + break; + } + } + +done: + put_packet_in_free_queue(pkt, nic); + + return rc; +} + +static int process_dhcp_loop(nic_t *nic, + nic_interface_t *nic_iface, + struct timer *periodic_timer, + struct timer *arp_timer) +{ + struct dhcpc_state *s; + int rc; + + s = nic_iface->ustack.dhcpc; + + while ((event_loop_stop == 0) && + (s->state != STATE_CONFIG_RECEIVED) && + (nic->flags & NIC_ENABLED)) { + /* Check the periodic and ARP timer */ + check_timers(nic, periodic_timer, arp_timer, 0); + + rc = nic_process_intr(nic, 1); + + while(rc > 0) { + rc = process_packets(nic, + periodic_timer, + arp_timer, + nic_iface); + } + + sleep(1); + } + + return 0; +} + + +static void nic_loop_close(void *arg) +{ + nic_t *nic = (nic_t *) arg; + + (*nic->ops->close)(nic, 0); +} + +void *nic_loop(void *arg) +{ + nic_t *nic = (nic_t *) arg; + int rc = -1; + struct timer periodic_timer, arp_timer; + sigset_t set; + + sigfillset(&set); + rc = pthread_sigmask(SIG_BLOCK, &set, NULL); + if (rc != 0 ) + { + /* TODO: determine if we need to exit this thread if we fail + * to set the signal mask */ + LOG_ERR(PFX "%s: Couldn't set signal mask", nic->log_name); + } + + pthread_cleanup_push(nic_loop_close, arg); + + /* Signal the device to enable itself */ + pthread_mutex_lock(&nic->nic_loop_started_mutex); + pthread_cond_signal(&nic->nic_loop_started_cond); + pthread_mutex_unlock(&nic->nic_loop_started_mutex); + + while(event_loop_stop == 0) { + nic_interface_t *nic_iface; + + if(nic->flags & NIC_DISABLED) + { + LOG_DEBUG(PFX "%s: Waiting to be enabled", + nic->log_name); + + /* Wait for the device to be enabled */ + pthread_mutex_lock(&nic->enable_wait_mutex); + pthread_cond_wait(&nic->enable_wait_cond, + &nic->enable_wait_mutex); + pthread_mutex_unlock(&nic->enable_wait_mutex); + + if (nic->state == NIC_EXIT) + pthread_exit(NULL); + + LOG_DEBUG(PFX "%s: is now enabled", nic->log_name); + } + + /* initialize the device to send/rec data */ + rc = (*nic->ops->open)(nic); + if(rc != 0) + { + LOG_ERR(PFX "%s: Could not initialize CNIC UIO device", + nic->log_name); + } + + add_vlan_interfaces(nic); + nic_set_all_nic_iface_mac_to_parent(nic); + + rc = alloc_free_queue(nic, 5); + if(rc != 5) + { + LOG_WARN(PFX "%s: Allocated %d packets instead of %d", + nic->log_name, rc, 5); + } + + /* Initialize the system clocks */ + timer_set(&periodic_timer, CLOCK_SECOND / 2); + timer_set(&arp_timer, CLOCK_SECOND * 10); + + /* Prepare the stack for each of the VLAN interfaces */ + pthread_mutex_lock(&nic->nic_iface_mutex); + + nic_iface = nic->nic_iface; + while(nic_iface != NULL) + { + uip_init(&nic_iface->ustack, + nic->flags & NIC_IPv6_ENABLED); + + LOG_INFO(PFX "%s: Initialized ip stack: VLAN: %d", + nic->log_name, nic_iface->vlan_id); + + LOG_INFO(PFX "%s: mac: %02x:%02x:%02x:%02x:%02x:%02x", + nic->log_name, + nic_iface->mac_addr[0], + nic_iface->mac_addr[1], + nic_iface->mac_addr[2], + nic_iface->mac_addr[3], + nic_iface->mac_addr[4], + nic_iface->mac_addr[5]); + + if( nic_iface->ustack.ip_config == IP_CONFIG_STATIC ) { + struct in_addr addr; + uip_ip4addr_t tmp = { 0, 0}; + + memcpy(&addr.s_addr, nic_iface->ustack.hostaddr, + sizeof(addr.s_addr)); + + LOG_INFO(PFX "%s: Using IP address: %s", + nic->log_name, + inet_ntoa(addr)); + + memcpy(&addr.s_addr, nic_iface->ustack.netmask, + sizeof(addr.s_addr)); + + LOG_INFO(PFX "%s: Using netmask: %s", + nic->log_name, + inet_ntoa(addr)); + + set_uip_stack(&nic_iface->ustack, + &nic_iface->ustack.hostaddr, + &nic_iface->ustack.netmask, + &tmp, + nic_iface->mac_addr); + + } else { + struct uip_stack *ustack = &nic_iface->ustack; + uip_ip4addr_t tmp = { 0, 0}; + + + set_uip_stack(&nic_iface->ustack, + &nic_iface->ustack.hostaddr, + &nic_iface->ustack.netmask, + &tmp, + nic_iface->mac_addr); + + dhcpc_init(nic, ustack, + nic_iface->mac_addr, ETH_ALEN); + rc = process_dhcp_loop(nic, nic_iface, + &periodic_timer, + &arp_timer); + + if(nic->flags & NIC_DISABLED) + break; + + LOG_INFO(PFX "%s: Initialized dhcp client", + nic->log_name); + } + + nic_iface = nic_iface->next; + } + + if(nic->flags & NIC_DISABLED) + goto dev_close; + + pthread_mutex_unlock(&nic->nic_iface_mutex); + + /* This is when we start the processing of packets */ + nic->start_time = time(NULL); + nic->flags &= ~NIC_UNITIALIZED; + nic->flags |= NIC_INITIALIZED; + nic->state |= NIC_RUNNING; + + /* Signal that the device enable is done */ + pthread_mutex_lock(&nic->enable_done_mutex); + pthread_cond_broadcast(&nic->enable_done_cond); + pthread_mutex_unlock(&nic->enable_done_mutex); + + while ((nic->state & NIC_RUNNING) && (event_loop_stop == 0)) { + /* Check the periodic and ARP timer */ + check_timers(nic, + &periodic_timer, + &arp_timer, + 1); + + rc = nic_process_intr(nic, 0); + while((rc > 0) && (nic->state & NIC_RUNNING)) { + rc = process_packets(nic, + &periodic_timer, + &arp_timer, + NULL); + } + } + +dev_close: + nic->state = NIC_STOPPED; + nic_close(nic, 1); + + /* Signal we are done closing CNIC/UIO device */ + pthread_mutex_lock(&nic->disable_wait_mutex); + pthread_cond_broadcast(&nic->disable_wait_cond); + pthread_mutex_unlock(&nic->disable_wait_mutex); + } + + pthread_cleanup_pop(0); + + pthread_exit(NULL); +} diff --git a/brcm_iscsi_uio/src/unix/nic.h b/brcm_iscsi_uio/src/unix/nic.h new file mode 100644 index 0000000..d20e9d1 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic.h @@ -0,0 +1,319 @@ +/* nic.h: NIC header file + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include + +#ifndef __NIC_H__ +#define __NIC_H__ + +#include +#include +#include +#include +#include +#include + +#include "packet.h" +#include "uip.h" + +#include "iscsi_if.h" + +/* Foward declarations */ +struct nic_ops; +struct nic_lib_handle; +struct packet; +struct nic_op; + +extern pthread_mutex_t nic_lib_list_mutex; +extern struct nic_lib_handle *nic_lib_list; + +/* Used to store a list of active cnic devices */ +extern pthread_mutex_t nic_list_mutex; +extern struct nic *nic_list; + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define MAX_PCI_DEVICE_ENTRIES 64 /* Maxium number of pci_device_id + entries a hw library may contain */ + +/******************************************************************************* + * Structure used to hold PCI vendor, device, subvendor and subdevice ID's + ******************************************************************************/ +struct pci_device_id { + const uint32_t vendor, device; /* Vendor and device ID or PCI_ANY_ID */ + const uint32_t subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ + const char *device_name; /* Data private to the driver */ +}; + +/****************************************************************************** + * NIC statistics structure + ******************************************************************************/ +struct nic_stats { + uint64_t interrupts; + uint64_t missed_interrupts; + + struct { + uint64_t packets; + uint64_t bytes; + } tx; + + struct { + uint64_t packets; + uint64_t bytes; + } rx; +}; + +/****************************************************************************** + * NIC interface structure + ******************************************************************************/ +typedef struct nic_interface { + struct nic_interface *next; + struct nic *parent; + + uint16_t flags; + uint8_t mac_addr[ETH_ALEN]; + uint8_t vlan_priority; + uint16_t vlan_id; + + time_t start_time; + + struct uip_stack ustack; +} nic_interface_t; + +/****************************************************************************** + * NIC lib operations structure + ******************************************************************************/ +struct nic_lib_ops { + /* Used to get the NIC library name */ + void (*get_library_name)(char **library_name, + size_t *library_name_size); + + /* Used to get to the PCI table supported by the NIC library */ + void (*get_pci_table)(struct pci_device_id **table, + uint32_t *entries); + + /* Used to get the version of this NIC library */ + void (*get_library_version)(char **version_string, + size_t *version_string_size); + + /* Used to get the NIC library build date*/ + void (*get_build_date)(char **build_date_string, + size_t *build_date_string_size); + + /* Used to get the transport name assoicated with this library */ + void (*get_transport_name)(char **transport_name, + size_t *transport_name_size); + + /* Used to get the uio name assoicated with this library */ + void (*get_uio_name)(char **uio_name, + size_t *uio_name_size); + +}; + +/******************************************************************************* + * NIC op table definition + ******************************************************************************/ +typedef struct nic_ops { + struct nic_lib_ops lib_ops; + + char *description; + int (*open)(struct nic *); + int (*close)(struct nic *, int); + int (*read)(struct nic *, struct packet *); + int (*write)(struct nic *, nic_interface_t *, + struct packet *); + void * (*get_tx_pkt)(struct nic *); + void (*start_xmit)(struct nic *, size_t); + int (*clear_tx_intr)(struct nic *); + int (*handle_iscsi_path_req)(struct nic *, + int, + struct iscsi_uevent *ev, + struct iscsi_path *path, + size_t len); +} net_ops_t; + +typedef struct nic_lib_handle { + struct nic_lib_handle *next; + + pthread_mutex_t mutex; + struct nic_ops *ops; +} nic_lib_handle_t; + +typedef struct nic { + struct nic *next; + + uint16_t flags; +#define NIC_UNITIALIZED 0x0001 +#define NIC_INITIALIZED 0x0002 +#define NIC_ENABLED 0x0004 +#define NIC_DISABLED 0x0008 +#define NIC_IPv6_ENABLED 0x0010 +#define NIC_ADDED_MULICAST 0x0020 +#define NIC_VLAN_STRIP_ENABLED 0x0100 +#define NIC_MSIX_ENABLED 0x0200 +#define NIC_TX_HAS_SENT 0x0400 + +#define NIC_UIO_NAME_MALLOC 0x1000 +#define NIC_CONFIG_NAME_MALLOC 0x2000 + + uint16_t state; +#define NIC_STOPPED 0x0001 +#define NIC_RUNNING 0x0002 +#define NIC_LONG_SLEEP 0x0004 +#define NIC_EXIT 0x0008 + + int fd; /* Holds the file descriptor to UIO */ + uint16_t uio_minor; /* Holds the UIO minor number */ + + uint32_t host_no; /* Holds the associated host number */ + + char *library_name; /* Name of the library to assoicate with */ + char *log_name; /* Human friendly name used in the log + file */ + char *config_device_name; /* Name read from the XML configuration + file */ + char eth_device_name[IFNAMSIZ]; /* Network interface name */ + char *uio_device_name; /* UIO device name */ + + uint32_t intr_count; /* Total UIO interrupt count */ + + /* iSCSI ring ethernet MAC address */ + __u8 mac_addr[ETH_ALEN]; + + /* Used to manage the network interfaces of this device */ + pthread_mutex_t nic_iface_mutex; + __u32 num_of_nic_iface; + nic_interface_t *nic_iface; + + /* Wait for the particular uio device to appear */ + pthread_mutex_t uio_wait_mutex; + pthread_cond_t uio_wait_cond; + + /* Wait for the device to be enabled */ + pthread_mutex_t enable_wait_mutex; + pthread_cond_t enable_wait_cond; + + /* Wait for the device to be finished enabled */ + pthread_mutex_t enable_done_mutex; + pthread_cond_t enable_done_cond; + + /* Wait for the nic loop to start */ + pthread_mutex_t nic_loop_started_mutex; + pthread_cond_t nic_loop_started_cond; + + /* Wait for the device to be disabled */ + pthread_mutex_t disable_wait_mutex; + pthread_cond_t disable_wait_cond; + + /* Held when transmitting */ + pthread_mutex_t xmit_mutex; + + /* The thread this device is running on */ + pthread_t thread; + + /* Statistical Information on this device */ + time_t start_time; + struct nic_stats stats; + +#define DEFAULT_RX_POLL_USEC 100 /* usec */ + /* options enabled by the user */ + uint32_t rx_poll_usec; + + /* Used to hold hardware specific data */ + void *priv; + + /* Used to hold the TX packets that are needed to be sent */ + struct packet *tx_packet_queue; + + /* Mutex to protect the list of free packets */ + pthread_mutex_t free_packet_queue_mutex; + + /* Used to hold the free packets that are needed to be sent */ + struct packet *free_packet_queue; + + /* Points to the NIC library */ + nic_lib_handle_t *nic_library; + + /* Points to the PCI table entry */ + struct pci_device_id *pci_id; + + /* Used to process the interrupt */ + int (*process_intr)(struct nic *nic); + + struct nic_ops *ops; +} nic_t; + + +/****************************************************************************** + * Function Prototypes + *****************************************************************************/ +int load_all_nic_libraries(); + +nic_t *nic_init(); +int nic_remove(nic_t * nic, int locked); + +int nic_add_nic_iface(nic_t *nic, + nic_interface_t *nic_iface); +int nic_process_intr(nic_t *nic, int discard_check); + +nic_interface_t * nic_iface_init(); + +typedef enum { + NIC_LIBRARY_EXSITS = 1, + NIC_LIBRARY_DOESNT_EXIST= 2, +} NIC_LIBRARY_EXIST_T; + +NIC_LIBRARY_EXIST_T does_nic_uio_name_exist(char *name); +NIC_LIBRARY_EXIST_T does_nic_library_exist(char *name); + +/******************************************************************************* + * Packet management utility functions + ******************************************************************************/ +struct packet * get_next_tx_packet(nic_t *nic); +struct packet * get_next_free_packet(nic_t *nic); +void put_packet_in_tx_queue(struct packet *pkt, + nic_t *nic); +void put_packet_in_free_queue(struct packet *pkt, + nic_t *nic); + + +typedef enum { + ALLOW_GRACEFUL_SHUTDOWN = 1, + FORCE_SHUTDOWN = 2, +} NIC_SHUTDOWN_T; + +int unload_all_nic_libraries(); +void nic_close(nic_t *nic, NIC_SHUTDOWN_T graceful); + +/* Use this function to fill in minor number and uio, and eth names */ +int nic_fill_name(nic_t *nic); + +int detemine_initial_uio_events(); + +int enable_multicast(nic_t *nic); +int disable_multicast(nic_t *nic); + +void nic_set_all_nic_iface_mac_to_parent(nic_t *nic); +struct nic_interface * nic_find_nic_iface(nic_t *nic, + uint16_t vlan_id); +int find_nic_lib_using_pci_id(uint32_t vendor, uint32_t device, + uint32_t subvendor, uint32_t subdevice, + nic_lib_handle_t **handle, + struct pci_device_id **pci_entry); + +void *nic_loop(void *arg); + +int nic_packet_capture(struct nic *, + struct packet *pkt); + + +#endif /* __NIC_H__ */ diff --git a/brcm_iscsi_uio/src/unix/nic_id.c b/brcm_iscsi_uio/src/unix/nic_id.c new file mode 100644 index 0000000..49fb4a7 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic_id.c @@ -0,0 +1,359 @@ +/* nic_id.c: Using sysfs to determine the PCI vendor, device, subvendor and + * subdevice ID's + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "logger.h" +#include "nic.h" + +#define PFX "nic_id " + +/******************************************************************************* + * Sysfs constant strings used to get PCI vendor, and device ID's + ******************************************************************************/ +const char uio_vendor_id_template[] = "/sys/class/uio/uio%d/device/vendor"; +const char uio_subvendor_id_template[] = "/sys/class/uio/uio%d/device/subsystem_vendor"; +const char uio_device_id_template[] = "/sys/class/uio/uio%d/device/device"; +const char uio_subdevice_id_template[] = "/sys/class/uio/uio%d/device/subsystem_device"; +const char uio_device_symlink_template[] = "/sys/class/uio/uio%d/device"; + +/** + * get_id() - Utility function to read hex values from sysfs + * @param nic - NIC device to use + * @param sysfs_template - sysfs path template to use + * @param sysfs_template_size - sysfs path template size in bytes + * @parm id - this is the value returned from the sysfs entry + * @return 0 on success <0 on failure + */ +static int get_id(nic_t *nic, + const char *sysfs_template, + const size_t sysfs_template_size, + uint32_t *id) +{ + int rc = 0; + FILE *fp; + size_t chars_read; + char buf[7]; + char *path; + size_t path_size; + + path_size = sysfs_template_size + 4; + path = malloc(path_size); + if(path == NULL) + { + LOG_ERR("Could not allocate memory for %s", sysfs_template); + return -ENOMEM; + } + + snprintf(path, path_size, sysfs_template, nic->uio_minor); + + fp = fopen(path, "r"); + if(fp == NULL) + { + LOG_ERR("Could not open path: %s", path); + rc = -EIO; + goto error_fopen; + } + + chars_read = fread(buf, sizeof(buf), 1, fp); + if(chars_read != 1) + { + LOG_ERR("Could not read from: %s", path); + rc = -EIO; + goto error; + } + + chars_read = sscanf(buf, "%x", id); + if(chars_read != 1) + { + LOG_ERR("Could interpret value: %s from: %s", + buf, path); + rc = -EIO; + goto error; + } + +error: + fclose(fp); + +error_fopen: + free(path); + + return rc; +} + +static int get_vendor(nic_t *nic, + uint32_t *id) +{ + return get_id(nic, + uio_vendor_id_template, sizeof(uio_vendor_id_template), + id); +} + +static int get_subvendor(nic_t *nic, + uint32_t *id) +{ + return get_id(nic, + uio_subvendor_id_template, + sizeof(uio_subvendor_id_template), + id); +} + +static int get_device(nic_t *nic, + uint32_t *id) +{ + return get_id(nic, + uio_device_id_template, + sizeof(uio_device_id_template), + id); +} + +static int get_subdevice(nic_t *nic, + uint32_t *id) +{ + return get_id(nic, + uio_subdevice_id_template, + sizeof(uio_subdevice_id_template), + id); +} + +int get_bus_slot_func_num(nic_t *nic, + uint32_t *bus, + uint32_t *slot, + uint32_t *func) +{ + size_t size; + char *path, *tok, *tok2; + int path_tokens, i; + size_t path_size; + char *read_pci_bus_slot_func_str; + char pci_bus_slot_func_str[32]; + int rc; + char *saveptr; + + path_size = sizeof(uio_device_symlink_template) + 4; + path = malloc(path_size); + if(path == NULL) + { + LOG_ERR("Could not allocate path memory for %s", + uio_device_symlink_template); + rc = -ENOMEM; + goto error_alloc_path; + } + + read_pci_bus_slot_func_str = malloc(128); + if(read_pci_bus_slot_func_str == NULL) + { + LOG_ERR(PFX "Could not allocate read pci bus memory for %s", + nic->log_name, uio_device_symlink_template); + rc = -ENOMEM; + goto error_alloc_read_pci_bus; + } + + snprintf(path, path_size, uio_device_symlink_template, nic->uio_minor); + + size = readlink(path, read_pci_bus_slot_func_str, 128); + if(size == -1) { + LOG_ERR(PFX "%s: Error with %s: %s", + nic->log_name, path, strerror(errno)); + rc = errno; + goto error; + } + + if(size > ((128) - 1)) { + read_pci_bus_slot_func_str[128 - 1] = '\0'; + LOG_ERR(PFX "%s: not enough space (%d) for reading PCI " + "slot:bus.func %s: %s", + nic->log_name, size, path, strerror(errno)); + rc = -EIO; + goto error; + } + + /* readlink() doesn't NULL terminate the string */ + read_pci_bus_slot_func_str[size] = '\0'; + + path_tokens = 0; + tok = strtok_r(read_pci_bus_slot_func_str, "/", &saveptr); + while (tok != NULL) { + path_tokens++; + tok = strtok_r(NULL, "/", &saveptr); + } + + size = readlink(path, read_pci_bus_slot_func_str, 128); + if(size == -1) { + LOG_ERR(PFX "%s: Error with %s: %s", + nic->log_name, path, strerror(errno)); + rc = errno; + goto error; + } + + if(size > ((128) - 1)) { + read_pci_bus_slot_func_str[128 - 1] = '\0'; + LOG_ERR(PFX "%s: not enough space for reading PCI " + "slot:bus.func %s: %s", + nic->log_name, path, strerror(errno)); + rc = -EIO; + goto error; + } + + /* readlink() doesn't NULL terminate the string */ + read_pci_bus_slot_func_str[size] = '\0'; + + tok = strtok_r(read_pci_bus_slot_func_str, "/", &saveptr); + for (i=0; ilog_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + tok = strtok_r(NULL, ":", &saveptr); + if(tok == NULL) { + LOG_ERR(PFX "%s: Error parsing slot: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + sscanf(tok, "%x", bus); + + /* Need to extract the next token "xx.x" */ + tok = strtok_r(NULL, ":", &saveptr); + if(tok == NULL) { + LOG_ERR(PFX "%s: Error extracing bus.func: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + tok2 = strtok_r(tok, ".", &saveptr); + if(tok2 == NULL) { + LOG_ERR(PFX "%s: Error parsing bus: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + sscanf(tok2, "%x", slot); + + tok2 = strtok_r(NULL, ".", &saveptr); + if(tok2 == NULL) { + LOG_ERR(PFX "%s: Error parsing func: %s", + nic->log_name, pci_bus_slot_func_str); + rc = -EIO; + goto error; + } + + sscanf(tok2, "%x", func); + LOG_INFO(PFX "%s: is found at %02x:%02x.%02x", nic->log_name, + *bus, *slot, *func); + + free(read_pci_bus_slot_func_str); + + rc = 0; + +error: +error_alloc_read_pci_bus: + free(path); + +error_alloc_path: + + return rc; +} + +/** + * find_set_nic_lib() - Match the NIC library to the NIC + * @param nic - NIC device to determine which NIC library to use + * @return 0 on success <0 on failure + */ +int find_set_nic_lib(nic_t *nic) +{ + uint32_t vendor; + uint32_t subvendor; + uint32_t device; + uint32_t subdevice; + + uint32_t pci_bus; + uint32_t pci_slot; + uint32_t pci_func; + int rc = 0; + + nic_lib_handle_t *handle; + struct pci_device_id *pci_entry; + + rc = get_vendor(nic, &vendor); + if( rc != 0) + { + LOG_ERR("%s: Could not get vendor id [0x%x]", + nic->log_name, rc); + return rc; + } + + rc = get_subvendor(nic, &subvendor); + if( rc != 0) + { + LOG_ERR("%s: Could not get subvendor id [0x%x]", + nic->log_name, rc); + return rc; + } + + rc = get_device(nic, &device); + if( rc != 0) + { + LOG_ERR("%s: Could not get device id [0x%x]", + nic->log_name, rc); + return rc; + } + + rc = get_subdevice(nic, &subdevice); + if( rc != 0) + { + LOG_ERR("%s: Could not get subdevice id [0x%x]", + nic->log_name, rc); + return rc; + } + + get_bus_slot_func_num(nic, &pci_bus, &pci_slot, &pci_func); + + LOG_DEBUG("%s: Looking for device vendor: " + "0x%x subvendor: 0x%x device: 0x%x subdevice: 0x%x", + nic->log_name, vendor, subvendor, device, subdevice); + + rc = find_nic_lib_using_pci_id(vendor, device, subvendor, subdevice, + &handle, &pci_entry); + + if(rc != 0) { + LOG_WARN(PFX "%s: Couldn't find proper NIC library", + nic->log_name); + return rc; + } + + nic->nic_library = handle; + nic->pci_id = pci_entry; + + /* Prepare the NIC library op table */ + nic->ops = handle->ops; + + return 0; +} diff --git a/brcm_iscsi_uio/src/unix/nic_id.h b/brcm_iscsi_uio/src/unix/nic_id.h new file mode 100644 index 0000000..de1a744 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic_id.h @@ -0,0 +1,22 @@ +/* nic_id.h: NIC uIP NetLink user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#ifndef __NIC_ID_H__ +#define __NIC_ID_H__ + +int find_set_nic_lib(nic_t *nic); + +int get_bus_slot_func_num(nic_t *nic, + uint32_t *bus, + uint32_t *slot, + uint32_t *func); + +#endif /* __NIC_ID_H__ */ diff --git a/brcm_iscsi_uio/src/unix/nic_nl.c b/brcm_iscsi_uio/src/unix/nic_nl.c new file mode 100644 index 0000000..c09cd8e --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic_nl.c @@ -0,0 +1,462 @@ +/* nic_nl.c: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uip_arp.h" +#include "logger.h" +#include "options.h" +#include "uevent.h" + +#include "nic.h" +#include "nic_nl.h" +#include "nic_utils.h" + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "NIC_NL " + +static void *nlm_recvbuf = NULL; +static void *nlm_sendbuf = NULL; + +static struct sockaddr_nl src_addr; + +const static struct sockaddr_nl dest_addr = { + .nl_family = AF_NETLINK, + .nl_pid = 0, /* kernel */ + .nl_groups = 0, /* unicast */ +}; + +#define POLL_NL 0 +#define POLL_MAX 1 + +/* Netlink */ +int nl_sock = INVALID_FD; + +static int +nl_read(int ctrl_fd, char *data, int size, int flags) +{ + int rc; + struct iovec iov; + struct msghdr msg; + + iov.iov_base = data; + iov.iov_len = size; + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + src_addr.nl_groups = 1; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name= (void*)&src_addr; + msg.msg_namelen = sizeof(src_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + rc = recvmsg(ctrl_fd, &msg, flags); + + return rc; +} + +static int +nlpayload_read(int ctrl_fd, char *data, int count, int flags) +{ + int rc; + struct iovec iov; + struct msghdr msg; + + iov.iov_base = nlm_recvbuf; + iov.iov_len = NLMSG_SPACE(count); + memset(iov.iov_base, 0, iov.iov_len); + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + src_addr.nl_groups = 1; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name= (void*)&src_addr; + msg.msg_namelen = sizeof(src_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + rc = recvmsg(ctrl_fd, &msg, flags); + + memcpy(data, NLMSG_DATA(iov.iov_base), count); + + return rc; +} + +static int +kwritev(int fd, enum iscsi_uevent_e type, struct iovec *iovp, int count) +{ + int i, rc; + struct nlmsghdr *nlh; + struct msghdr msg; + struct iovec iov; + int datalen = 0; + + for (i = 0; i < count; i++) { + datalen += iovp[i].iov_len; + } + + nlh = nlm_sendbuf; + memset(nlh, 0, NLMSG_SPACE(datalen)); + + nlh->nlmsg_len = NLMSG_SPACE(datalen); + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = type; + + datalen = 0; + for (i = 0; i < count; i++) { + memcpy(NLMSG_DATA(nlh) + datalen, iovp[i].iov_base, + iovp[i].iov_len); + datalen += iovp[i].iov_len; + } + iov.iov_base = (void*)nlh; + iov.iov_len = nlh->nlmsg_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name= (void*)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + do { + rc = sendmsg(fd, &msg, 0); + if (rc == -ENOMEM) { + LOG_ERR(PFX "sendmsg: alloc_skb() failed"); + sleep(1); + } else if (rc < 0) { + LOG_ERR(PFX "sendmsg: bug?: on %d %s[0x%x]", + fd, strerror(errno), errno); + sleep(1); + } + } while ((rc < 0) && (event_loop_stop == 0)); + + return rc; +} + +/* + * __kipc_call() should never block. Therefore + * Netlink's xmit logic is serialized. This means we do not allocate on + * xmit path. Instead we reuse nlm_sendbuf buffer. + * + * Transport must assure non-blocking operations for: + * + * - session_create() + * - conn_create() + * - conn_bind() + * _ set_param() + * - conn_start() + * - conn_stop() + * + * Its OK to block for cleanup for short period of time in operatations for: + * + * - conn_destroy() + * - session_destroy() + * + * FIXME: interface needs to be extended to allow longer blocking on + * cleanup. (Dima) + */ +int +__kipc_call(int fd, void *iov_base, int iov_len) +{ + int rc; + struct iovec iov; + struct iscsi_uevent *ev = iov_base; + enum iscsi_uevent_e type = ev->type; + int wait_response; + + /* Sanity check */ + if(iov_base == NULL) + return -EINVAL; + + iov.iov_base = iov_base; + iov.iov_len = iov_len; + + rc = kwritev(fd, type, &iov, 1); + + wait_response = 0; + do { + rc = nlpayload_read(fd, (void*)ev, sizeof(*ev), MSG_PEEK); + if (rc < 0) { + LOG_ERR(PFX "Error reading resp to reply: %s[%d]", + strerror(rc), rc); + return rc; + } + + if (ev->type != type) { + LOG_DEBUG(PFX "expecting event %d, got %d, handling...", + type, ev->type); + if (ev->type == ISCSI_KEVENT_IF_ERROR) { + if ((rc = nlpayload_read(fd, (void*)ev, + sizeof(*ev), 0)) < 0) { + return rc; + } + if (ev->iferror == -ENOSYS) { + /* not fatal so let caller handle log */ + LOG_DEBUG(PFX "Recieved iferror %d: %s", + ev->iferror, + strerror(ev->iferror)); + } else if (ev->iferror < 0) { + LOG_ERR("Received iferror %d: %s", + ev->iferror, + strerror(ev->iferror)); + } else { + LOG_ERR("Received iferror %d", + ev->iferror); + } + return ev->iferror; + } + } else if (ev->type == ISCSI_UEVENT_GET_STATS) { + /* kget_stats() will read */ + return 0; + } else { + if ((rc = nlpayload_read(fd, (void*)ev, + sizeof(*ev), 0)) < 0) { + return rc; + } + break; + } + + wait_response++; + } while ((ev->type != type) && + (event_loop_stop == 0) && + (wait_response < MAX_COUNT_NIC_NL_RESP)); + + return rc; +} + +static int ctldev_handle() +{ + nic_t *nic = NULL; + int rc; + size_t ev_size; + struct iscsi_uevent *ev; + char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))]; + struct nlmsghdr *nlh; + char *data; + uint8_t *payload; + struct iscsi_path *path; + char *msg_type_str; + uint32_t host_no; + + /* Take a quick peek at what how much uIP will need to read */ + if ((rc = nl_read(nl_sock, nlm_ev, + NLMSG_SPACE(sizeof(struct iscsi_uevent)), MSG_PEEK)) < 0) { + LOG_ERR("can not read nlm_ev, error %d", rc); + return rc; + } + nlh = (struct nlmsghdr *)nlm_ev; + + data = (char *) malloc(nlh->nlmsg_len); + if(data == NULL) { + LOG_ERR("Couldn't allocate %d bytes for Netlink iSCSI message\n", + nlh->nlmsg_len); + return -ENOMEM; + } + + memset(data, 0, nlh->nlmsg_len); + ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + if ((rc = nl_read(nl_sock, data, (int) nlh->nlmsg_len, 0)) < 0) { + LOG_ERR("can not read nlm_ev, error %d", rc); + goto error; + } + + ev = (struct iscsi_uevent *)NLMSG_DATA(data); + switch (ev->type) { + case ISCSI_KEVENT_PATH_REQ: + msg_type_str = "path_req"; + + if((ev_size - sizeof(ev)) != sizeof(*path)) + LOG_WARN("Didn't get iscsi_path size(%d) expected %d", + ev_size - sizeof(ev), sizeof(*path)); + host_no = ev->r.req_path.host_no; + + break; + case ISCSI_KEVENT_IF_DOWN: + msg_type_str = "if_down"; + + host_no = ev->r.notify_if_down.host_no; + break; + default: + /* We don't care about other iSCSI Netlink messages */ + LOG_DEBUG(PFX "Received ev->type: 0x%x", ev->type); + rc = 0; + goto error; + } + + /* This is a message that drivers should be interested in */ + LOG_INFO("Received: '%s': host_no: %d", msg_type_str, host_no); + + rc = from_host_no_find_associated_eth_device(host_no, &nic); + if(rc != 0) { + LOG_ERR(PFX "Dropping msg, couldn't find nic with host no:%d\n", + host_no); + goto error; + } + + /* Ensure that the NIC is RUNNING */ + if(((nic->state & NIC_RUNNING) != NIC_RUNNING) && + (ev->type != ISCSI_KEVENT_IF_DOWN)) { + LOG_WARN(PFX "%s: is not running so can't issue neigh req, " + "cmd: 0x%d", + nic->log_name, ev->type); + goto error; + } + + ev = (struct iscsi_uevent *)NLMSG_DATA(data); + payload = (uint8_t *) ((uint8_t *)ev) + sizeof(*ev); + path = (struct iscsi_path *)payload; + + if(nic->ops) { + switch (ev->type) { + case ISCSI_KEVENT_PATH_REQ: + /* pass the request up to the user space + * library driver */ + if(nic->ops->handle_iscsi_path_req) { + nic->ops->handle_iscsi_path_req(nic, + nl_sock, ev, + path, ev_size); + } + break; + case ISCSI_KEVENT_IF_DOWN: + nic_remove(nic, 0); + + break; + } + } + + rc = 0; + +error: + free(data); + + return rc; +} + +/** + * nic_nl_open() - This is called when opening/creating the Netlink listening + * thread + * @param dev - CNIC UIO device to create a NetLink listener on + * @return 0 on success, <0 on failure + */ +int nic_nl_open() +{ + int rc; + struct pollfd poll_array[POLL_MAX]; + memset(poll_array, 0, sizeof(poll_array)); + + nlm_sendbuf = calloc(1, NLM_BUF_DEFAULT_MAX); + if (!nlm_sendbuf) { + LOG_ERR(PFX "can't allocate nlm_sendbuf"); + rc = -ENOMEM; + goto error; + } + + nlm_recvbuf = calloc(1, NLM_BUF_DEFAULT_MAX); + if (!nlm_recvbuf) { + LOG_ERR(PFX "can't allocate nlm_recvbuf"); + rc = -ENOMEM; + goto error; + } + + nl_sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ISCSI); + if (nl_sock < 0) { + LOG_ERR(PFX "can not create NETLINK_ISCSI socket"); + rc = -ENOMEM; + goto error; + } + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + src_addr.nl_groups = ISCSI_NL_GRP_UIP; + + while ((!event_loop_stop) && (bnx2i_loaded == 0)) { + rc = bind(nl_sock, + (struct sockaddr *)&src_addr, sizeof(src_addr)); + if (rc == 0) + break; + + LOG_ERR(PFX "waiting binding to NETLINK_ISCSI socket"); + + pthread_mutex_lock(&bnx2i_module_loaded_mutex); + pthread_cond_wait(&bnx2i_module_loaded_cond, + &bnx2i_module_loaded_mutex); + pthread_mutex_unlock(&bnx2i_module_loaded_mutex); + + sleep(1); + } + + if(event_loop_stop) { + rc = -EINVAL; + goto error; + } + + LOG_INFO(PFX "Netlink to CNIC on pid %d is ready", src_addr.nl_pid); + + poll_array[POLL_NL].fd = nl_sock; + poll_array[POLL_NL].events = POLLIN; + + while (!event_loop_stop) { + int res = poll(poll_array, POLL_MAX, NL_POLL_RESOLUTION); + if (res > 0) { + if (poll_array[POLL_NL].revents) + ctldev_handle(); + } else if (res < 0) { + if (errno == EINTR) { + LOG_DEBUG(PFX "event_loop interrupted"); + } else { + LOG_ERR(PFX "got poll() error (%d), errno (%d), " + "exiting", res, errno); + break; + } + } + } + + LOG_INFO(PFX "Netlink thread exit'ing"); + rc = 0; + +error: + if(nlm_sendbuf) { + free(nlm_sendbuf); + nlm_sendbuf = NULL; + } + + if(nlm_recvbuf) { + free(nlm_recvbuf); + nlm_recvbuf = NULL; + } + + return 0; +} diff --git a/brcm_iscsi_uio/src/unix/nic_nl.h b/brcm_iscsi_uio/src/unix/nic_nl.h new file mode 100644 index 0000000..7325ee2 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic_nl.h @@ -0,0 +1,20 @@ +/* nic_nl.h: NIC uIP NetLink user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#ifndef __NIC_NL_H__ +#define __NIC_NL_H__ + +int nic_nl_open(); +void nic_nl_close(); + +int __kipc_call(int fd, void *iov_base, int iov_len); + +#endif /* __NIC_NL_H__ */ diff --git a/brcm_iscsi_uio/src/unix/nic_utils.c b/brcm_iscsi_uio/src/unix/nic_utils.c new file mode 100644 index 0000000..04b88da --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic_utils.c @@ -0,0 +1,1377 @@ +/* nic_util.c: shared NIC utility functions + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "nic.h" +#include "nic_id.h" +#include "nic_vlan.h" +#include "nic_utils.h" +#include "options.h" + +#define PFX "nic_utils " + +/****************************************************************************** + * String constants + *****************************************************************************/ +static const char nic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name"; +static const char cnic_sysfs_uio_event_template[] = "/sys/class/uio/uio%d/event"; +static const char base_uio_sysfs_name[] = "/sys/class/uio/"; +static const char uio_name[] = "uio"; + +static const char uio_base_dir[] = "/dev/uio"; +static const char uio_udev_path_template[] = "/dev/uio%hd"; + + +static const char base_iscsi_host_name[] = "/sys/class/iscsi_host/"; +static const char host_template[] = "host%d"; +static const char iscsi_host_path_template[] = "/sys/class/iscsi_host/host%d"; +static const char iscsi_host_path_netdev_template[] = "/sys/class/iscsi_host/host%d/netdev"; + +/****************************************************************************** + * Autodiscovery of iscsi_hosts + *****************************************************************************/ +static int filter_host_name(const struct dirent *entry) +{ + if((memcmp(entry->d_name, "host", 4) == 0)) + return 1; + else + return 0; +} + +int nic_discover_iscsi_hosts() +{ + struct dirent **files; + int count; + int i; + int rc; + + count = scandir(base_iscsi_host_name, &files, filter_host_name, + alphasort); + + switch(count) { + case 0: + /* Currently there are no iSCSI hosts */ + rc = 0; + break; + + case -1: + LOG_WARN(PFX "Error when scanning path: %s[%s]", + base_iscsi_host_name, strerror(errno)); + rc = -EINVAL; + break; + + default: + /* There are iSCSI hosts */ + for(i=0; id_name, host_template, &host_no); + nic_t *nic; + + LOG_INFO(PFX "Found host[%d]: %s", + host_no, files[i]->d_name); + + /* Build the path to determine netdev name */ + snprintf(temp_path, sizeof(temp_path), + iscsi_host_path_netdev_template, host_no); + + rc = capture_file(&raw, &raw_size, temp_path); + if(rc != 0) + continue; + + rc = from_host_no_find_associated_eth_device(host_no, + &nic); + if(rc != 0) { + LOG_INFO(PFX "NIC not found creating an instance for host_no: %d", host_no); + nic = nic_init(); + if(nic == NULL) { + LOG_ERR(PFX "%s: Couldn't allocate space for %s", + raw); + + return -ENOMEM; + } + + /* Normalize the string */ + if(raw[raw_size - 1] == '\n') + raw[raw_size - 1] = '\0'; + + strncpy(nic->eth_device_name, raw, raw_size); + nic->config_device_name = nic->eth_device_name; + nic->log_name = nic->eth_device_name; + } else { + LOG_INFO(PFX "%s: NIC found host_no: %d", + nic->log_name, host_no); + + } + + + free(raw); + } + + /* Cleanup the scandir() call */ + for(i=0; imac_addr, ETH_ALEN); + multicast_addr.addr[0] = 0x33; + multicast_addr.addr[1] = 0x33; + multicast_addr.addr[2] = 0xff; + + /* Prepare the request */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, nic->eth_device_name, + sizeof(nic->eth_device_name)); + memcpy(ifr.ifr_hwaddr.sa_data, multicast_addr.addr, ETH_ALEN); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + LOG_ERR(PFX "%s: Couldn't create socket to %s " + "multicast address: %s", + nic->log_name, + cmd == SIOCADDMULTI ? "added" : "delete", + strerror(errno)); + return errno; + } + + if (ioctl(fd, cmd, (char*)&ifr) != 0) { + LOG_ERR("%s: Couldn't issue ioctl socket to %s " + "multicast address: %s", + nic->log_name, + cmd == SIOCADDMULTI ? "add" : "delete", + strerror(errno)); + rc = errno; + goto error; + } + + LOG_INFO(PFX "%s: %s address %02x:%02x:%02x:%02x:%02x:%02x " + "to multicast list for %s", + nic->log_name, + cmd == SIOCADDMULTI ? "Added" : "Deleted", + multicast_addr.addr[0], multicast_addr.addr[1], + multicast_addr.addr[2], multicast_addr.addr[3], + multicast_addr.addr[4], multicast_addr.addr[5], + nic->eth_device_name); + + if(cmd == SIOCADDMULTI) { + nic->flags |= NIC_ADDED_MULICAST; + } else { + nic->flags &= ~NIC_ADDED_MULICAST; + } + +error: + close(fd); + + return rc; +} + +/** + * enable_multicast() - This fuction is used to enable + * the listening of multicast addresses for a given network interface + * @param nic - NIC device to enable multicast on + * @return 0 for success or <0 for failure + */ +int enable_multicast(nic_t *nic) +{ + return nic_util_enable_disable_multicast(nic, SIOCADDMULTI); +} + +/** + * disable_multicast() - This fuction is used to disable + * the listening of multicast addresses for a given network interface + * @param dev - NIC device to disable multicast on + * @return 0 for success or <0 for failure + */ +int disable_multicast(nic_t *nic) +{ + return nic_util_enable_disable_multicast(nic, SIOCDELMULTI); +} + +/******************************************************************************* + * Finding associated UIO/physical network interfaces + ******************************************************************************/ +static int filter_net_name(const struct dirent *entry) +{ + if((memcmp(entry->d_name, "net:", 4) == 0)) + return 1; + else + return 0; +} + +static char * extract_net_name(struct dirent **files) +{ + return strstr(files[0]->d_name, ":"); +} + +static int filter_dot_out(const struct dirent *entry) +{ + if((memcmp(entry->d_name, ".", 1) == 0)) + return 0; + else + return 1; +} + +static char * extract_none(struct dirent **files) +{ + return (files[0]->d_name); +} + +/** + * from_host_no_find_nic() - Given the host number + * this function will try to find the assoicated nic interface + * @param host_no - minor number of the UIO device + * @param nic - pointer to the NIC will set if successful + * @return 0 on success, <0 on error + */ +int from_host_no_find_associated_eth_device(int host_no, + nic_t **nic) +{ + nic_t *current_nic = nic_list; + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + + char temp_path[sizeof(iscsi_host_path_netdev_template) + 8]; + int rc = -EIO; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + iscsi_host_path_netdev_template, host_no); + + rc = capture_file(&raw, &raw_size, temp_path); + if(rc != 0) + { + goto error; + } + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while(*raw_tmp != '\n') + raw_tmp++; + *raw_tmp = '\0'; + + rc = -EIO; + + pthread_mutex_lock(&nic_list_mutex); + current_nic = nic_list; + while(current_nic != NULL) + { + if (strcmp(raw, current_nic->eth_device_name) == 0) { + *nic = current_nic; + rc = 0; + break; + } + + current_nic = current_nic->next; + } + pthread_mutex_unlock(&nic_list_mutex); + + free(raw); + + error: + return rc; +} + + +/******************************************************************************* + * NIC packet handling functions + ******************************************************************************/ +/** + * from_uio_find_associated_eth_device() - Given the uio minor number + * this function will try to find the assoicated phyisical network + * interface + * @param uio_minor - minor number of the UIO device + * @param name - char buffer which will be filled if successful + * @param name_size - size of the name buffer + * @return >0 minor number <0 an error + */ +int from_uio_find_associated_eth_device(int uio_minor, + char *name, + size_t name_size) +{ + char *path; + int rc; + int count; + struct dirent **files; + char *parsed_name; + int i; + int path_iterator; + char *search_paths[] = { "/sys/class/uio/uio%i/device/", + "/sys/class/uio/uio%i/device/net" }; + int (*search_filters[])(const struct dirent *) = { + filter_net_name, + filter_dot_out, + }; + char *(*extract_name[])(struct dirent **files) = { + extract_net_name, + extract_none, + }; + int extract_name_offset[] = { 1, 0 }; + + path = malloc(PATH_MAX); + if (path == NULL) { + LOG_ERR(PFX "Could not allocate memory for path"); + rc = -ENOMEM; + goto error; + } + + for(path_iterator = 0; + path_iterator < sizeof(search_paths) / sizeof(search_paths[0]); + path_iterator++) { + /* Build the path to determine uio name */ + rc = sprintf(path, search_paths[path_iterator], + uio_minor); + count = scandir(path, &files, + search_filters[path_iterator], + alphasort); + + switch(count) { + case 1: + parsed_name = (*extract_name[path_iterator])(files); + if(parsed_name == NULL) { + LOG_WARN(PFX "Couldn't find delimiter in: %s", + files[0]->d_name); + + break; + } + + strncpy(name, + parsed_name + + extract_name_offset[path_iterator], + name_size); + + free(files[0]); + free(files); + + rc = 0; + break; + + case 0: + rc = -EINVAL; + break; + + case -1: + LOG_WARN(PFX "Error when scanning path: %s[%s]", + path, strerror(errno)); + rc = -EINVAL; + break; + + default: + LOG_WARN(PFX "Too many entries when looking for device: %s", + path); + + /* Cleanup the scandir() call */ + for(i=0; id_name, uio_name, sizeof(uio_name) -1) == 0)) + return 1; + else + return 0; +} + +/** + * from_netdev_name_find_nic() - This is used to find the NIC device given + * the netdev name + * @param interface_name - name of the interface to search on + * @param nic - pointer of the pointer to the NIC + * @return 0 on success, <0 on failure + */ +int from_netdev_name_find_nic(char *interface_name, + nic_t **nic) +{ + nic_t *current_nic; + + pthread_mutex_lock(&nic_list_mutex); + current_nic = nic_list; + while(current_nic != NULL) + { + if (strcmp(interface_name, current_nic->eth_device_name) == 0) { + break; + } + + current_nic = current_nic->next; + } + pthread_mutex_unlock(&nic_list_mutex); + + if(current_nic == NULL) + return -EINVAL; + + *nic = current_nic; + return 0; +} + +/** + * from_phys_name_find_assoicated_uio_device() - This is used to find the + * uio minor + * when given a network interface name + * @param interface_name - network interface name to search for + * @return >0 minor number <0 an error + */ +int from_phys_name_find_assoicated_uio_device(nic_t *nic) +{ + char *path = NULL; + int count; + struct dirent **files; + int i; + int rc; + char *interface_name = nic->config_device_name; + + if(interface_name == NULL) + interface_name = nic->eth_device_name; + + count = scandir(base_uio_sysfs_name, + &files, filter_uio_name, alphasort); + + switch(count) { + case 0: + LOG_WARN(PFX "Couldn't find %s to determine uio minor", + interface_name); + return-EINVAL; + + case -1: + LOG_WARN(PFX "Error when scanning for %s in path: %s [%s]", + interface_name, + base_uio_sysfs_name, strerror(errno)); + return -EINVAL; + } + + path = malloc(PATH_MAX); + if (path == NULL) { + LOG_ERR(PFX "Could not allocate memory for path"); + return -ENOMEM; + } + + /* Run through the contents of the filtered files to see if the + * network interface name matches that of the uio device */ + for(i=0; i d_name, "uio%d", &uio_minor); + if(rc != 1) + { + LOG_WARN("Could not parse: %s", files[i]->d_name); + continue; + } + + rc = from_uio_find_associated_eth_device(uio_minor, + eth_name, + sizeof(eth_name)); + if( rc != 0) + { + LOG_WARN("uio minor: %d not valid [%d}", uio_minor, rc); + continue; + } + + if(strncmp(eth_name, interface_name, sizeof(eth_name)) == 0) + { + memcpy(nic->eth_device_name, + eth_name, + sizeof(nic->eth_device_name)); + + LOG_INFO(PFX "%s associated with uio%d", + nic->eth_device_name, uio_minor); + + rc = uio_minor; + goto done; + } + } + + LOG_WARN("Could not find assoicate uio device with %s", + interface_name); + + rc = -EINVAL; +done: + if(path != NULL) + free(path); + + for(i=0; iuio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if(rc != 0) + { + goto error; + } + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while(*raw_tmp != '\n') + raw_tmp++; + *raw_tmp = '\0'; + + /* If the nic library is not set then check if there is a library + * which matches the library name */ + if(nic->nic_library == NULL) { + NIC_LIBRARY_EXIST_T exist; + + exist = does_nic_uio_name_exist(raw); + if (exist == NIC_LIBRARY_DOESNT_EXIST) { + LOG_ERR(PFX "%s: could not find library: %s ", + nic->log_name, raw); + rc = -EIO; + } + } else { + char *library_name; + size_t library_name_size; + + /* Get the string name from the NIC library */ + (*nic->ops->lib_ops.get_library_name)(&library_name, + &library_name_size); + + if (strcmp(raw, library_name) != 0) { + LOG_ERR(PFX "%s: uio names not equal: " + "expecting %s got %s from %s", + nic->log_name, library_name, raw, temp_path); + rc = -EIO; + } + } + + free(raw); + + LOG_INFO(PFX "%s: Verified is a cnic_uio device", nic->log_name); + + error: + return rc; +} + + +/** + * nic_fill_name() - This will initialize all the hardware resources underneath + * a struct cnic_uio device + * @param nic - The nic device to attach the hardware with + * @return 0 on success, on failure a errno will be returned + */ +int nic_fill_name(nic_t *nic) +{ + int rc; + + if((nic->config_device_name != NULL) && + (memcmp(uio_base_dir, nic->config_device_name, + sizeof(uio_base_dir) - 1) == 0)) { + uint16_t uio_minor; + char eth_name[sizeof(nic->eth_device_name)]; + + /* Determine the minor number for the UIO device */ + rc = sscanf(nic->config_device_name, uio_udev_path_template, + &uio_minor); + if(rc != 1) + { + LOG_WARN(PFX "%s: Could not parse for minor number", + nic->uio_device_name); + return -EINVAL; + } else { + nic->uio_minor = uio_minor; + } + + nic->uio_device_name = nic->config_device_name; + + /* Determine the assoicated physical network interface */ + rc = from_uio_find_associated_eth_device(nic->uio_minor, + eth_name, + sizeof(eth_name)); + if(rc != 0) { + LOG_WARN(PFX "%s: Couldn't find associated eth device", + nic->uio_device_name); + } else { + memcpy(nic->eth_device_name, + eth_name, sizeof(eth_name)); + } + + LOG_INFO(PFX "%s: configured for uio device for %s", + nic->log_name, + nic->uio_device_name); + + } else { + LOG_INFO(PFX "looking for uio device for %s", + nic->config_device_name); + + rc = from_phys_name_find_assoicated_uio_device(nic); + if(rc < 0) { + /* TODO wait for ethernet interface to appear */ + LOG_ERR(PFX "Could not determine UIO name"); + } + + nic->uio_minor = rc; + + nic->uio_device_name = malloc(sizeof(uio_udev_path_template) + 8); + if(nic->uio_device_name == NULL) + { + LOG_INFO(PFX "%s: Couldn't malloc space for uio name", + nic->log_name); + return -ENOMEM; + } + + snprintf(nic->uio_device_name, + sizeof(uio_udev_path_template) + 8, + uio_udev_path_template, nic->uio_minor); + + nic->flags |= NIC_UIO_NAME_MALLOC; + } + + return 0; +} + +void prepare_nic(nic_t *nic) +{ + int rc; + NIC_LIBRARY_EXIST_T exist; + + nic_fill_name(nic); + + /* No assoicated library, we can skip it */ + if(nic->library_name != NULL) { + /* Check that we have the proper NIC library loaded */ + exist = does_nic_library_exist(nic->library_name); + if(exist == NIC_LIBRARY_DOESNT_EXIST) { + LOG_ERR(PFX "NIC library doesn't exists: %s", + nic->library_name); + goto error; + } + } + + /* Determine the NIC library to use based on the PCI Id */ + rc = find_set_nic_lib(nic); + if (rc != 0) { + LOG_ERR(PFX "%s: Couldn't find NIC library", + nic->log_name); + goto error; + } + + LOG_INFO("%s: found NIC '%s'", + nic->log_name, + nic->pci_id->device_name); + + if(nic->thread == INVALID_THREAD) { + struct timespec ts; + struct timeval tp; + + /* Try to spin up the nic thread */ + rc = pthread_create(&nic->thread, NULL, nic_loop, nic); + if (rc != 0) { + LOG_ERR(PFX "%s: Couldn't create thread for nic", + nic->log_name); + goto error; + } + + /* Convert from timeval to timespec */ + rc = gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec += 5; /* TODO: hardcoded wait for 5 seconds */ + + /* Wait for the device to be disabled */ + pthread_mutex_lock(&nic->nic_loop_started_mutex); + rc = pthread_cond_timedwait(&nic->nic_loop_started_cond, + &nic->nic_loop_started_mutex, &ts); + pthread_mutex_unlock(&nic->nic_loop_started_mutex); + } + + LOG_INFO("Created nic thread: %s", nic->log_name); + +error: + return; +} + +/******************************************************************************* + * Functions used to enable/disable the NIC + ******************************************************************************/ +/** + * nic_enable() - Function used to enable the NIC + * @param nic - NIC to enable + * @return 0 on success, <0 on failure + */ +int nic_enable(nic_t *nic) +{ + if( (nic->flags & NIC_DISABLED) && + (nic->state & NIC_STOPPED)) + { + struct timespec ts; + struct timeval tp; + int rc; + + /* Signal the device to enable itself */ + pthread_mutex_lock(&nic->enable_wait_mutex); + pthread_cond_signal(&nic->enable_wait_cond); + pthread_mutex_unlock(&nic->enable_wait_mutex); + + nic->flags &= ~NIC_DISABLED; + nic->flags |= NIC_ENABLED; + + /* Convert from timeval to timespec */ + rc = gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec += 5; /* TODO: hardcoded wait for 5 seconds */ + + /* Wait for the device to be disabled */ + pthread_mutex_lock(&nic->enable_done_mutex); + rc = pthread_cond_timedwait(&nic->enable_done_cond, + &nic->enable_done_mutex, &ts); + pthread_mutex_unlock(&nic->enable_done_mutex); + + if(rc == 0) { + LOG_DEBUG(PFX "%s: device enabled", nic->log_name); + + return 0; + } else { + LOG_ERR(PFX "%s: waiting to finish nic_enable err:%s", + nic->log_name, strerror(rc)); + return rc; + } + } + else + { + LOG_INFO(PFX "%s: device already enabled: flag: " + "0x%x state: 0x%x", + nic->log_name, nic->flags, nic->state); + return -EALREADY; + } +} + +/** + * nic_disable() - Function used to disable the NIC + * @param nic - NIC to disble + * @return 0 on success, <0 on failure + */ +int nic_disable(nic_t *nic) +{ + if( (nic->flags & NIC_ENABLED) && + (nic->state & NIC_RUNNING)) + { + nic->flags &= ~NIC_ENABLED; + nic->flags |= NIC_DISABLED; + nic->state &= ~NIC_RUNNING; + nic->state |= NIC_STOPPED; + + /* Wait for the device to be disabled */ + pthread_mutex_lock(&nic->disable_wait_mutex); + pthread_cond_wait(&nic->disable_wait_cond, + &nic->disable_wait_mutex); + pthread_mutex_unlock(&nic->disable_wait_mutex); + + LOG_DEBUG(PFX "%s: device disabled", nic->log_name); + + return 0; + } + else + { + LOG_WARN(PFX "%s: device already disabled: ", + nic->log_name, nic->flags, nic->state); + return -EALREADY; + } +} + +void nic_close_all() +{ + nic_t *nic; + + pthread_mutex_lock(&nic_list_mutex); + + /* Start the shutdown process */ + nic = nic_list; + while (nic != NULL) { + nic_close(nic, 1); + nic = nic->next; + } + pthread_mutex_unlock(&nic_list_mutex); + + LOG_INFO(PFX "All CNIC threads have exited"); +} + + +/****************************************************************************** + * Routines to read initialized UIO values from sysfs + *****************************************************************************/ +/** + * determine_initial_uio_events() - This utility function will + * determine the number of uio events that have occured on the + * given device. This value is read from the UIO sysfs entry + * @param dev - device to read from + * @param num_of_event - number of UIO events + * @return 0 is success, <0 failure + */ +int detemine_initial_uio_events(nic_t *nic, + uint32_t *num_of_events) +{ + char *raw = NULL; + uint32_t raw_size = 0; + ssize_t elements_read; + char temp_path[sizeof(cnic_sysfs_uio_event_template) + 8]; + int rc; + + /* Capture RX buffer size */ + snprintf(temp_path, sizeof(temp_path), + cnic_sysfs_uio_event_template, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if(rc != 0) + { + goto error; + } + + elements_read = sscanf(raw, "%d", num_of_events); + if(elements_read != 1) + { + LOG_ERR(PFX "%s: Couldn't parse UIO events size from %s", + nic->log_name, temp_path); + rc =-EIO; + goto error; + } + + rc = 0; +error: + if(raw != NULL) + free(raw); + + return rc; +} + + + + +/** + * add_vlan_interfaces() - Using the associated ethernet device name, this + * function will traverse through the local VLAN table + * determine if the associated interface is a VLAN + * then will create network interfaces for all VLAN ID + * @param dev - struct cnic_uio device to assign the VLAN ID + * @return 0 on success, <0 on failure + */ +int add_vlan_interfaces(nic_t *nic) +{ + int rc; + struct vlan_handle handle; + struct vlan_found_handle found_handle; + + init_vlan_table(&handle); + rc = capture_vlan_table(&handle); + if(rc != 0) { + LOG_ERR(PFX "Failed to capture VLAN table"); + rc = -EIO; + goto error_vlan_handle; + } + + rc = init_vlan_found_handle(&found_handle, &handle); + if(rc != 0) { + LOG_ERR(PFX "Failed to init VLAN found handle"); + goto error_vlan_found_handle; + } + + rc = find_vlans_using_phy_interface(&handle, + &found_handle, + nic->eth_device_name); + if(rc > 0) + { + int i; + + for(i=0; ifound == VLAN_ENTRY_FOUND) + { + nic_iface = nic_iface_init(nic); + if(nic_iface == NULL) + { + LOG_ERR("Couldn't allocate space for net_iface"); + continue; + } + + nic_iface->vlan_id = found_handle.handle->entries->vlan_id; + + rc = nic_add_nic_iface(nic, nic_iface); + } + } + } + + release_vlan_found_handle(&found_handle); + +error_vlan_found_handle: + + release_vlan_table(&handle); + +error_vlan_handle: + + return rc; +} + +/** + * nic_set_all_nic_iface_mac_to_parent() - This is a utility function used to + * intialize all the MAC addresses of the network interfaces for a given + * CNIC UIO device + * @param dev - CNIC UIO device to initialize + */ +void nic_set_all_nic_iface_mac_to_parent(nic_t *nic) +{ + nic_interface_t *current; + + pthread_mutex_lock(&nic->nic_iface_mutex); + + current = nic->nic_iface; + while(current != NULL) + { + /* Set the initial MAC address of this interface to the parent + * adapter */ + memcpy(current->mac_addr, nic->mac_addr, 6); + + current = current->next; + } + + pthread_mutex_unlock(&nic->nic_iface_mutex); +} + +/******************************************************************************* + * NIC packet handling functions + ******************************************************************************/ +/** + * nic_alloc_packet_buffer() - Used to allocate a packet buffer used to + * send a TX packet later + * @param nic - nic device to send the packet on + * @param nic_iface - nic interface to send out on + * @param buf - pointer to the buffer to send + * @param buf_size - size in bytes of the buffer to send + * @return pointer to the allocated packet buffer + * NULL if memory could not be allocated + */ +static packet_t * nic_alloc_packet_buffer(nic_t *nic, + nic_interface_t *nic_iface, + uint8_t *buf, + size_t buf_size) +{ + packet_t *pkt; + + pkt = malloc(sizeof(*pkt) + buf_size); + if(pkt == NULL) { + LOG_ERR(PFX "%s: Couldn't allocate space for packet buffer\n"); + return NULL; + } + + pkt->next = NULL; + pkt->nic = nic; + pkt->nic_iface = nic_iface; + pkt->buf_size = buf_size; + memcpy(pkt->buf, buf, buf_size); + + return pkt; +} + +/** + * nic_queue_tx_packet() - Used to queue a TX packet buffer to send later + * @param nic - NIC device to send the packet on + * @param nic_iface - NIC interface to send on the packet on + * @param pkt - packet to queue + * @return 0 if successful or <0 if unsuccessful + */ +int nic_queue_tx_packet(nic_t *nic, + nic_interface_t *nic_iface, + packet_t *pkt) +{ + packet_t *queued_pkt; + + queued_pkt = nic_alloc_packet_buffer(nic, nic_iface, + pkt->buf, pkt->buf_size); + if(queued_pkt == NULL) + { + LOG_ERR(PFX "%s: Couldn't allocate tx packet to queue"); + return -ENOMEM; + } + + if(nic->tx_packet_queue == NULL) { + nic->tx_packet_queue = queued_pkt; + } else { + packet_t *current_pkt; + + current_pkt = nic->tx_packet_queue; + while(current_pkt->next != NULL) + { + current_pkt = current_pkt->next; + } + + current_pkt->next = queued_pkt; + } + + LOG_DEBUG(PFX "%s: tx packet queued", nic->log_name); + + return 0; +} + + +/** + * nic_dequeue_tx_packet() - Used pop a TX packet buffer of the TX + * @param dev - cnic_uio device to send the packet on + * @param buf - pointer to the buffer to send + * @param buf_size - size in bytes of the buffer to send + * @return NULL if there are no more TX packet buffers to send + * pointer to the packet buffer which is detached from the device + */ +packet_t * nic_dequeue_tx_packet(nic_t *nic) +{ + packet_t *pkt; + + pkt = nic->tx_packet_queue; + + /* There is a packet buffer to send, time to detach it from the + * cnic_uio device */ + if(pkt != NULL) { + nic->tx_packet_queue = pkt->next; + pkt->next = NULL; + } + + return pkt; +} + + +/******************************************************************************* + * NIC interface management utility functions + ******************************************************************************/ +/** + * nic_find_nic_iface() - This function is used to find an interface from the + * NIC + * @param vlan_id - VLAN id to look for + * @return nic_iface - if found network interface with the given VLAN ID + * if not found a NULL is returned + */ +nic_interface_t * nic_find_nic_iface(nic_t *nic, + uint16_t vlan_id) +{ + nic_interface_t *current; + + pthread_mutex_lock(&nic->nic_iface_mutex); + + current = nic->nic_iface; + while(current != NULL) + { + if(current->vlan_id == vlan_id) + { + pthread_mutex_unlock(&nic->nic_iface_mutex); + return current; + } + + current = current->next; + } + + pthread_mutex_unlock(&nic->nic_iface_mutex); + + return NULL; +} + + +/******************************************************************************* + * Packet management utility functions + ******************************************************************************/ +/** + * get_next_packet_in_queue() - This function will return the next packet in + * the queue + * @param queue - the queue to pull the packet from + * @return the packet in the queue + */ +static packet_t * get_next_packet_in_queue(packet_t **queue) +{ + packet_t *pkt; + + if(*queue == NULL) + return NULL; + + pkt = *queue; + *queue = pkt->next; + + return pkt; +} + +/** + * get_next_tx_packet() - This function will return the next packet in + * the TX queue + * @param nic - NIC to pull the TX packet from + * @return the packet in hte queue + */ +packet_t * get_next_tx_packet(nic_t *nic) +{ + return get_next_packet_in_queue(&nic->tx_packet_queue); +} + +/** + * get_next_free_packet() - This function will return the next packet in + * the free queue + * @param nic - NIC to pull the RX packet from + * @return the packet in hte queue + */ +packet_t * get_next_free_packet(nic_t *nic) +{ + packet_t *pkt; + pthread_mutex_lock(&nic->free_packet_queue_mutex); + pkt = get_next_packet_in_queue(&nic->free_packet_queue); + pthread_mutex_unlock(&nic->free_packet_queue_mutex); + + if(pkt != NULL) + reset_packet(pkt); + + return pkt; +} + +/** + * put_packet_in_queue() - This function will place the packet in the given + * queue + * @param pkt - the packet to place + * @param queue - the queue to place the packet + * @return the packet in the queue + */ +static void put_packet_in_queue(packet_t *pkt, + packet_t **queue) +{ + if(*queue == NULL) { + *queue = pkt; + } else { + pkt->next = *queue; + *queue = pkt; + } +} + +/** + * put_packet_in_tx_queue() - This function will place the packet in + * the TX queue + * @param pkt - packet to place + * @param nic - NIC to pull the TX packet from + * @return the packet in hte queue + */ +void put_packet_in_tx_queue(packet_t *pkt, + nic_t *nic) +{ + return put_packet_in_queue(pkt, + &nic->tx_packet_queue); +} + +/** + * put_packet_in_free_queue() - This function will place the packet in + * the RX queue + * @param pkt - packet to place + * @param nic - NIC to pull the RX packet from + * @return the packet in hte queue + */ +void put_packet_in_free_queue(packet_t *pkt, + nic_t *nic) +{ + pthread_mutex_lock(&nic->free_packet_queue_mutex); + put_packet_in_queue(pkt, + &nic->free_packet_queue); + pthread_mutex_unlock(&nic->free_packet_queue_mutex); +} + + +uint32_t calculate_default_netmask(uint32_t ip_addr) +{ + uint32_t netmask; + + if (IN_CLASSA(ntohl(ip_addr))) + netmask = htonl(IN_CLASSA_NET); + else if (IN_CLASSB(ntohl(ip_addr))) + netmask = htonl(IN_CLASSB_NET); + else if (IN_CLASSC(ntohl(ip_addr))) + netmask = htonl(IN_CLASSC_NET); + else { + LOG_ERR("Unable to guess netmask for address %x\n", + &ip_addr); + return -1; + } + + return netmask; +} + + + +/******************************************************************************* + * File Management + ******************************************************************************/ + /** + * determine_file_size_read() - when fstat doesn't work on filepath + * within the /proc filesytem, we need to read/count the size of the file + * until we hit a EOF + * @parm filepath - path of the file in which to determine the filesize in + * bytes + * @return file size in bytes, <0 on failure + */ +int determine_file_size_read(const char *filepath) +{ + size_t total_size = 0; + ssize_t size = 1; + int fd; + char buf[1024]; + + fd = open(filepath, O_RDONLY); + if(fd == -1) { + LOG_ERR("Could not open file: %s", filepath); + return -1; + } + + while(size > 0) + { + size = read(fd, buf, sizeof(buf)); + + switch(size) { + case 0: + break; + case -1: + LOG_ERR("Error reading file to determine size: %s", + strerror(errno)); + total_size = -1; + break; + default: + total_size += size; + break; + } + } + + close(fd); + + return total_size; +} + +/** + * capture_file() - Used to capture a file into a buffer + * @param raw - This pointer will be set to the buffer which will hold the + * file contents + * @param raw_size - This is the size of the buffer returned + * @param path - The file path to capture the data from + * @return 0 is returned on success, <0 is returned on failure + */ +int capture_file(char **raw, uint32_t *raw_size, const char *path) +{ + FILE *fp; + size_t read_size; + int rc = 0; + int file_size; + + file_size = determine_file_size_read(path); + if(file_size < 0) + { + LOG_ERR("Could not determine size %s", + path); + return -EIO; + } + + fp = fopen(path, "r"); + if(fp == NULL) { + LOG_ERR("Could not open path %s", + path); + return -EIO; + } + + *raw = malloc(file_size); + if(*raw == NULL) { + LOG_ERR("Could not malloc space for capture %s", + path); + rc = -ENOMEM; + goto error; + } + + read_size = fread(*raw, file_size, 1, fp); + if(read_size < 0) { + LOG_ERR("Could not read capture, path: %s len: %d", + path, file_size); + free(*raw); + *raw = NULL; + rc = errno; + } + else + { + *raw_size = file_size; + } +error: + fclose(fp); + + LOG_INFO("Done capturing %s", path); + + return rc; +} diff --git a/brcm_iscsi_uio/src/unix/nic_utils.h b/brcm_iscsi_uio/src/unix/nic_utils.h new file mode 100644 index 0000000..a36457a --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic_utils.h @@ -0,0 +1,55 @@ +/* nic_util.h: NIC utility functions + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ +#ifndef __NIC_UTILS_H__ +#define __NIC_UTILS_H__ + +#include "nic.h" + +/****************************************************************************** + * Function Prototype + ******************************************************************************/ +int nic_discover_iscsi_hosts(); + +int enable_mutlicast(nic_t *nic); +int disable_mutlicast(nic_t *nic); + +int from_netdev_name_find_nic(char *interface_name, + nic_t **nic); + +int from_host_no_find_associated_eth_device(int host_no, + nic_t **nic); + +int from_phys_name_find_assoicated_uio_device(nic_t *nic); + +int nic_queue_tx_packet(nic_t *nic, + nic_interface_t *nic_iface, + packet_t *pkt); + +packet_t * nic_dequeue_tx_packet(nic_t *nic); +nic_interface_t * nic_find_nic_iface(nic_t *nic, + uint16_t vlan_id); +int add_vlan_interfaces(nic_t *nic); + +int nic_verify_uio_sysfs_name(nic_t *nic); +void nic_close_all(); + +uint32_t calculate_default_netmask(uint32_t ip_addr); + +void prepare_nic(nic_t *nic); + +int nic_enable(nic_t *nic); +int nic_disable(nic_t *nic); + +int determine_file_size_read(const char *filepath); +int capture_file(char **raw, uint32_t *raw_size, const char *path); + + +#endif /* __NIC_UTILS_H__ */ diff --git a/brcm_iscsi_uio/src/unix/nic_vlan.c b/brcm_iscsi_uio/src/unix/nic_vlan.c new file mode 100644 index 0000000..4f28f22 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic_vlan.c @@ -0,0 +1,311 @@ +/* nic_vlan.c: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "logger.h" +#include "nic.h" +#include "nic_utils.h" +#include "nic_vlan.h" + +/******************************************************************************* + * Constants + ******************************************************************************/ +#define PFX "vlan" + +static const char proc_vlan_config_path[] = "/proc/net/vlan/config"; + +/******************************************************************************* + * Resolving Found VLAN's for CNIC + ******************************************************************************/ +int init_vlan_found_handle(struct vlan_found_handle *found_handle, + struct vlan_handle *handle) +{ + memset(found_handle, 0, sizeof(*found_handle)); + + found_handle->entries = malloc(found_handle->num_of_entries * + sizeof(struct vlan_found_entry)); + if(found_handle->entries == NULL) + { + LOG_ERR("Could not allocate space for found entries"); + return -ENOMEM; + } + + found_handle->handle = handle; + found_handle->num_of_entries = handle->num_of_entries; + + memset(found_handle->entries, 0, found_handle->num_of_entries * + sizeof(struct vlan_found_entry)); + + handle->outstanding_found_handles++; + + return 0; +} + +void release_vlan_found_handle(struct vlan_found_handle *found_handle) +{ + if(found_handle->entries != NULL) { + free(found_handle->entries); + found_handle->entries = NULL; + } + + found_handle->num_of_entries = 0; + + found_handle->handle->outstanding_found_handles--; + + found_handle->handle = NULL; + +} + +/******************************************************************************* + * Resolving VLAN's for CNIC + ******************************************************************************/ +/** + * init_vlan_handle() - Used to initialize struct ipv4_route_handle so + * that is can be used + * @param handle - Pointer to struct ipv4_route_handle to initialize + * @return 0 on success and <0 on failure + */ +void init_vlan_table(struct vlan_handle *handle) +{ + handle->entries = NULL; + handle->num_of_entries = 0; +} + +/** + * parse_vlan_table() - Given the raw dump of a Linux vlan table, this + * function will parse the into entries held by + * struct vlan_handle + * @param handle - struct vlan_handle used to hold the parsed contents + * @param raw - buffer to parse the contents from + * @param raw_size - size of the buffer in bytes + * @return 0 on success, <0 on failure + */ +int parse_vlan_table(struct vlan_handle *handle, + char *raw, uint32_t raw_size) +{ + FILE *fp; + int i; + char *token; + size_t size; + int rc; + + token = raw; + + /* determine the number of entries */ + while(*token != '\0') + { + if(*token == '\n') + handle->num_of_entries++; + + token++; + } + + /* There are 2 lines which describe the vlan table + * This lines need to be skipped with counting */ + handle->num_of_entries -= 2; + + LOG_INFO("Number of vlan entries: %d", handle->num_of_entries); + + size = handle->num_of_entries * sizeof(struct vlan_entry); + handle->entries = malloc(size); + if(handle->entries == NULL) { + LOG_ERR("Couldn't malloc space to parse vlan table. entires: %d size: %d", + handle->num_of_entries, size); + return -ENOMEM; + } + + fp = fmemopen(raw, raw_size, "r"); + if(fp == NULL) { + LOG_ERR("Could not open raw dump of vlan table"); + rc = errno; + goto fmemopen_error; + } + + if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */ + LOG_ERR("Empty or missing line, or read error"); + rc = -EIO; + goto error; + } + + if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the second line. */ + LOG_ERR("Empty or missing line, or read error"); + rc = -EIO; + goto error; + } + + i = 0; + /* Time to parse the routing table */ + while(1) + { + struct vlan_entry *entry = &handle->entries[i]; + int r; + + r = fscanf(fp, "%15s |%hu |%15s", + entry->vlan_iface_name, + &entry->vlan_id, + entry->phy_iface_name); + if (r != 3) { + if (feof(fp)) { /* EOF with no (nonspace) chars read. */ + break; + } + + LOG_WARN("Parsing error: parsed %d elements", + r); + break; + } + + i++; + + LOG_DEBUG("Vlan %d: vlan iface:%s vlan id:%d phys iface:%s", + i, + entry->vlan_iface_name, + entry->vlan_id, + entry->phy_iface_name); + } + + fclose(fp); + + return 0; + +error: + fclose(fp); + +fmemopen_error: + if(handle->entries != NULL) + free(handle->entries); + + return rc; +} + +/** + * capture_vlan_table() - This function will snapshot the Linux vlan + * routing table for further processing + * @param handle - struct vlan_handle used to hold the routing context + * @return 0 on success, <0 on failure + */ +int capture_vlan_table(struct vlan_handle *handle) +{ + char *raw = NULL; + uint32_t raw_size = 0; + int rc; + + rc = capture_file(&raw, &raw_size, proc_vlan_config_path); + if(rc != 0) + { + goto error; + } + + rc = parse_vlan_table(handle, raw, raw_size); + if(rc != 0) + { + goto error; + } + +error: + if(raw != NULL) + free(raw); + + return rc; +} + +/** + * release_vlan_table() - This function will free all resources used by + * the handle + * @param handle - struct vlan_handle used to hold the routing context + */ +void release_vlan_table(struct vlan_handle *handle) +{ + if(handle->entries != NULL) { + free(handle->entries); + handle->entries = NULL; + } + + handle->num_of_entries = 0; +} + +/** + * find_phy_using_vlan_interface() - Given the interface name determine VLAN + * tag ID to match either the physical or VLAN interface name + * @param vlan_iface_name - VLAN interface used to find the physical + * interface + * @param phy_iface_name - returned value is the physical interface name + * @param vlan_id - returned value is the VLAN id + * @return 1 is returned if the interface is a VLAN, 0 if the interface is not + * <0 is returned if there is an error + */ +int find_phy_using_vlan_interface(struct vlan_handle *handle, + char *vlan_iface_name, + char **phy_iface_name, uint16_t *vlan_id) +{ + int i, rc = 0; + + for(i=0; i < handle->num_of_entries; i++) + { + struct vlan_entry *entry = &handle->entries[i]; + + /* Compare VLAN interface names to find a match */ + if(strcmp(entry->vlan_iface_name, vlan_iface_name) == 0) + { + *phy_iface_name = entry->phy_iface_name; + *vlan_id = entry->vlan_id; + rc = 1; + break; + } + } + + return rc; +} + +/** + * find_vlans_using_phy_interface() - Given the physical interface name this + * function will determine the VLAN interface name and VLAN ID + * @param iface_name - physical interface used to find the vlan interface + * @param vlan_iface_name - returned value is the VLAN interface name + * @return The number of VLAN interfaces found + */ +int find_vlans_using_phy_interface(struct vlan_handle *handle, + struct vlan_found_handle *found_handle, + char *phy_iface_name) +{ + int i, num_found = 0; + + for(i=0; i < handle->num_of_entries; i++) + { + struct vlan_entry *entry = &handle->entries[i]; + + /* Compare interface names to find a match */ + if(strcmp(entry->phy_iface_name, phy_iface_name) == 0) + { + found_handle->entries[i].found = VLAN_ENTRY_FOUND; + num_found++; + } + } + + return num_found; +} diff --git a/brcm_iscsi_uio/src/unix/nic_vlan.h b/brcm_iscsi_uio/src/unix/nic_vlan.h new file mode 100644 index 0000000..b586f19 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/nic_vlan.h @@ -0,0 +1,65 @@ +/* nic_vlan.h: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#ifndef __NIC_VLAN_H__ +#define __NIC_VLAN_H__ + +#include + + +/* Used to hold entries in the vlan table */ +struct vlan_entry { + char vlan_iface_name[16]; + char phy_iface_name[16]; + uint16_t vlan_id; +}; + +struct vlan_handle +{ + struct vlan_entry *entries; + uint32_t num_of_entries; + + uint32_t outstanding_found_handles; +}; + +struct vlan_found_entry +{ +#define VLAN_ENTRY_FOUND 1 +#define VLAN_ENTRY_NOT_FOUND 0 + uint8_t found; +}; + +struct vlan_found_handle +{ + struct vlan_handle *handle; + uint32_t num_of_entries; + struct vlan_found_entry *entries; +}; + +/******************************************************************************* + * Function Prototypes + ******************************************************************************/ +void init_vlan_table(struct vlan_handle *handle); +int capture_vlan_table(struct vlan_handle *handle); +void release_vlan_table(struct vlan_handle *handle); + +int find_phy_using_vlan_interface(struct vlan_handle *handle, + char *vlan_iface_name, + char **phy_iface_name, uint16_t *vlan_id); +int find_vlans_using_phy_interface(struct vlan_handle *handle, + struct vlan_found_handle *found_handle, + char *phy_iface_name); + +int init_vlan_found_handle(struct vlan_found_handle *found_handle, + struct vlan_handle *handle); +void release_vlan_found_handle(struct vlan_found_handle *found_handle); + +#endif /* __NIC_VLAN_H__ */ diff --git a/brcm_iscsi_uio/src/unix/options.h b/brcm_iscsi_uio/src/unix/options.h new file mode 100644 index 0000000..876391c --- /dev/null +++ b/brcm_iscsi_uio/src/unix/options.h @@ -0,0 +1,88 @@ +/* options.c: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + +#include +#include +#include + +/****************************************************************************** + * Constants which are tuned at compile time by the user + *****************************************************************************/ + +/** + * MAX_COUNT_NIC_NL_RESP - This is the maximum number of polls uIP will + * try for a kernel response after a PATH_REQ + */ +#define MAX_COUNT_NIC_NL_RESP 5 + +/** + * ENABLE_LOG_UEVENT - By defining ENABLE_LOG_UEVENT this will log all the + * uevents from the kernel that the uIP application + * sees. This is useful for debugging purposes + */ +#undef ENABLE_LOG_UEVENT + +/** + * NLM_BUF_DEFAULT_MAX - This is the buffer size allocated for the send/receive + * buffers used by the uIP Netlink subsystem. This + * value is in bytes. + */ +#define NLM_BUF_DEFAULT_MAX 8192 /* bytes */ + +/** + * NL_POLL_RESOLUTION - This defines the number of milliseconds between + * each polling of the Netlink socket. + */ +#define NL_POLL_RESOLUTION 250 /* milliseconds */ + +/****************************************************************************** + * Non adjustable constants + *****************************************************************************/ + +#define APP_NAME "uIP" +/* BUILD_DATE is automatically generated from the Makefile */ + + +#define DEBUG_OFF 0x1 +#define DEBUG_ON 0x2 + +#define INVALID_FD -1 +#define INVALID_THREAD -1 + +struct options { + char debug; + + /* Time the userspace daemon was started */ + time_t start_time; +}; + +extern int event_loop_stop; +extern struct options opt; + +#ifdef WORDS_BIGENDIAN +#define ntohll(x) (x) +#define htonll(x) (x) +#else +#define ntohll(x) bswap_64(x) +#define htonll(x) bswap_64(x) +#endif + +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) + +/* taken from Linux kernel, include/linux/compiler-gcc.h */ +/* Optimization barrier */ +/* The "volatile" is due to gcc bugs */ +#define barrier() __asm__ __volatile__("": : :"memory") + +#endif diff --git a/brcm_iscsi_uio/src/unix/packet.c b/brcm_iscsi_uio/src/unix/packet.c new file mode 100644 index 0000000..53f3a6b --- /dev/null +++ b/brcm_iscsi_uio/src/unix/packet.c @@ -0,0 +1,106 @@ +/* packet.c: packet management + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include + +#include "logger.h" +#include "packet.h" +#include "nic.h" + +/** + * alloc_packet() - Function used to allocate memory for a packet + * @param max_buf_size - max packet size + * @param priv_size - size of the assoicated private data + * @return NULL if failed, on success return a pointer to the packet + */ +struct packet * alloc_packet(size_t max_buf_size, + size_t priv_size) +{ + struct packet *pkt; + void *priv; + + pkt = malloc(max_buf_size + sizeof(struct packet)); + if(pkt == NULL) { + LOG_ERR("Could not allocate any memory for packet"); + return NULL; + } + + priv = malloc(priv_size); + if(priv == NULL) { + LOG_ERR("Could not allocate any memory for private structure"); + goto free_pkt; + } + + pkt->max_buf_size = max_buf_size; + pkt->priv = priv; + + return pkt; + +free_pkt: + free(pkt); + + return NULL; +} + +void free_packet(struct packet *pkt) +{ + if(pkt->priv != NULL) + free(pkt->priv); + + free(pkt); +} + +/** + * reset_packet() - This will reset the packet fields to default values + * @param pkt - the packet to reset + */ +void reset_packet(packet_t *pkt) +{ + pkt->next = NULL; + + pkt->flags = 0; + pkt->vlan_tag = 0; + + pkt->buf_size = 0; + + pkt->data_link_layer = NULL; + pkt->network_layer = NULL; +} + +int alloc_free_queue(nic_t *nic, + size_t num_of_packets) +{ + int rc, i; + + pthread_mutex_lock(&nic->free_packet_queue_mutex); + for(i=0; inext = nic->free_packet_queue; + nic->free_packet_queue = pkt; + } + + rc = num_of_packets; + +done: + pthread_mutex_unlock(&nic->free_packet_queue_mutex); + + return i; +} diff --git a/brcm_iscsi_uio/src/unix/packet.h b/brcm_iscsi_uio/src/unix/packet.h new file mode 100644 index 0000000..4c98b62 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/packet.h @@ -0,0 +1,48 @@ +/* packet.h: packet definitions + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include + +#ifndef __PACKET_H__ +#define __PACKET_H__ + +#include "nic.h" + +struct nic; +struct nic_interface; + +typedef struct packet { + struct packet *next; + + uint32_t flags; +#define VLAN_TAGGED 0x0001 + uint16_t vlan_tag; + + size_t max_buf_size; + size_t buf_size; + + uint8_t *data_link_layer; + uint8_t *network_layer; + + struct nic *nic; + struct nic_interface *nic_iface; + + void *priv; + uint8_t buf[]; +} packet_t; + +/****************************************************************************** + * Packet Function Declarations + *****************************************************************************/ +int alloc_free_queue(struct nic *, size_t num_of_packets); +void reset_packet(packet_t *pkt); + +#endif /* __PACKET_H__ */ diff --git a/brcm_iscsi_uio/src/unix/uevent.c b/brcm_iscsi_uio/src/unix/uevent.c new file mode 100644 index 0000000..7d76f79 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/uevent.c @@ -0,0 +1,579 @@ +/* uevent.c: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "nic.h" +#include "nic_vlan.h" +#include "nic_utils.h" +#include "logger.h" +#include "options.h" +#include "uevent.h" + +/****************************************************************************** + * Listening for uevents for cnic and bnx2i + ******************************************************************************/ +static int uevent_netlink_sock = -1; +static pthread_t uevent_watch_thread = INVALID_THREAD; + +/* Used to notify when the cnic module is loaded */ +pthread_mutex_t cnic_module_loaded_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cnic_module_loaded_cond = PTHREAD_COND_INITIALIZER; +int cnic_loaded = 0; + +/* Used to notify when the bnx2i module is loaded */ +pthread_mutex_t bnx2i_module_loaded_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t bnx2i_module_loaded_cond = PTHREAD_COND_INITIALIZER; +int bnx2i_loaded = 0; + +/****************************************************************************** + * uevent Contants + ******************************************************************************/ +#define PFX "uevent " + +static const char add_cnic_module_str[] = "add@/module/cnic"; +static const char remove_cnic_module_str[] = "remove@/module/cnic"; +static const char add_uio_module_str[] = "add@/class/uio/uio"; +static const char sscanf_uio_module_str[] = "add@/class/uio/uio%d"; + +static const char uevent_action_key[] = "ACTION="; +static const char uevent_devpath_key[] = "DEVPATH="; +static const char uevent_subsystem_key[] = "SUBSYSTEM="; +static const char uevent_driver_key[] = "DRIVER="; +static const char uevent_seqnum_key[] = "SEQNUM="; +static const char uevent_devpath_old_key[] = "DEVPATH_OLD="; +static const char uevent_physdevpath_key[] = "PHYSDEVPATH="; +static const char uevent_physdevbus_key[] = "PHYSDEVBUS="; +static const char uevent_physdevdriver_key[] = "PHYSDEVDRIVER="; +static const char uevent_major_key[] = "MAJOR="; +static const char uevent_minor_key[] = "MINOR="; +static const char uevent_timeout_key[] = "TIMEOUT="; +static const char uevent_interface_key[] = "INTERFACE="; +static const char uevent_ifindex_key[] = "IFINDEX="; + +static const char uevent_add[] = "add"; +static const char uevent_remove[] = "remove"; +static const char uevent_uio[] = "uio"; +static const char uevent_bnx2i[] = "bnx2i"; + +static const char uevent_cnic_devpath[] = "/module/cnic"; +static const char uevent_cnic_subsystem[] = "module"; + +static const char uevent_bnx2i_devpath[] = "/module/bnx2i"; +static const char uevent_bnx2i_subsystem[] = "module"; + +static const char uevent_net_devpath[] = "/class/net"; +static const char uevent_net_subsystem[] = "net"; + +static const char uio_udev_path_template[] = "/dev/uio%d"; + +/****************************************************************************** + * uevent Functions + ******************************************************************************/ +static void parse_uevent(char *buf, int buf_len, struct parsed_uevent *event) +{ + int i; + int bufpos; + + bufpos = 0; + + event->init = buf; + + for (i = 0; (bufpos < buf_len); i++) { + char *key = &buf[bufpos]; + + if (memcmp(key, + uevent_action_key, + sizeof(uevent_action_key) - 1) == 0) { + event->action = &key[sizeof(uevent_action_key) - 1]; + LOG_UEVENT("%s%s", uevent_action_key, event->action); + } else if (memcmp(key, + uevent_devpath_key, + sizeof(uevent_devpath_key) - 1) == 0) { + event->devpath = &key[sizeof(uevent_devpath_key) - 1]; + LOG_UEVENT("%s%s", uevent_devpath_key, event->devpath); + + } else if (memcmp(key, + uevent_subsystem_key, + sizeof(uevent_subsystem_key) - 1) == 0) { + event->subsystem = + &key[sizeof(uevent_subsystem_key) - 1]; + LOG_UEVENT("%s%s", uevent_subsystem_key, + event->subsystem); + } else + if (memcmp + (key, uevent_driver_key, + sizeof(uevent_driver_key) - 1) == 0) { + event->driver = &key[sizeof(uevent_driver_key) - 1]; + LOG_UEVENT("%s%s", uevent_driver_key, event->driver); + } else if (memcmp(key, + uevent_seqnum_key, + sizeof(uevent_seqnum_key) - 1) == 0) { + event->seqnum = &key[sizeof(uevent_seqnum_key) - 1]; + LOG_UEVENT("%s%s", uevent_seqnum_key, event->seqnum); + } else if (memcmp(key, + uevent_devpath_old_key, + sizeof(uevent_devpath_old_key) - 1) == 0) { + event->devpath_old = + &key[sizeof(uevent_devpath_old_key)] - 1; + LOG_UEVENT("%s%s", uevent_devpath_old_key, + event->devpath_old); + } else + if (memcmp + (key, uevent_physdevpath_key, + sizeof(uevent_physdevpath_key) - 1) == 0) { + event->physdevpath = + &key[sizeof(uevent_physdevpath_key) - 1]; + LOG_UEVENT("%s%s", uevent_physdevpath_key, + event->physdevpath); + } else + if (memcmp + (key, uevent_physdevbus_key, + sizeof(uevent_physdevbus_key) - 1) == 0) { + event->physdevbus = + &key[sizeof(uevent_physdevbus_key) - 1]; + LOG_UEVENT("%s%s", uevent_physdevbus_key, + event->physdevbus); + } else + if (memcmp + (key, uevent_physdevdriver_key, + sizeof(uevent_physdevdriver_key) - 1) == 0) { + event->physdevdriver = + &key[sizeof(uevent_physdevdriver_key) - 1]; + LOG_UEVENT("%s%s", uevent_physdevdriver_key, + event->physdevdriver); + } else + if (memcmp + (key, uevent_major_key, + sizeof(uevent_major_key) - 1) == 0) { + event->major = &key[sizeof(uevent_major_key) - 1]; + LOG_UEVENT("%s%s", uevent_major_key, event->major); + } else if (memcmp(key, + uevent_minor_key, + sizeof(uevent_minor_key) - 1) == 0) { + event->minor = &key[sizeof(uevent_minor_key) - 1]; + LOG_UEVENT("%s%s", uevent_minor_key, event->minor); + } else if (memcmp(key, + uevent_timeout_key, + sizeof(uevent_timeout_key) - 1) == 0) { + event->timeout = &key[sizeof(uevent_timeout_key) - 1]; + LOG_UEVENT("%s%s", uevent_timeout_key, event->timeout); + } else if (memcmp(key, + uevent_interface_key, + sizeof(uevent_interface_key) - 1) == 0) { + event->interface = &key[sizeof(uevent_interface_key) - 1]; + LOG_UEVENT("%s%s", uevent_interface_key, + event->interface); + } else if (memcmp(key, + uevent_ifindex_key, + sizeof(uevent_ifindex_key) - 1) == 0) { + event->ifindex = &key[sizeof(uevent_ifindex_key) - 1]; + LOG_UEVENT("%s%s", uevent_ifindex_key, event->ifindex); + } else { + LOG_INFO(PFX "Unknown: %s", key); + } + + bufpos += strlen(key) + 1; + } +} + +/****************************************************************************** + * wakeup_cnic_dev() - This function will notify all those waiting on the + * uio_wait_event conditionial. This should trigger + * all threads waiting on this event. + * @param event - The uevent which comes from the kernel + * @param return - 0 on success, <0 on failure + *****************************************************************************/ +static int wakeup_cnic_dev(struct parsed_uevent *event) +{ + int minor; + nic_t *nic; + int rc; + + rc = sscanf(event->minor, "%d", &minor); + if(rc == 1) { + LOG_INFO(PFX "New uio device registered: minor: %d", minor); + } else { + LOG_INFO(PFX "Couldn't parse minor number: %s", event->minor); + return -EIO; + } + + pthread_mutex_lock(&nic_list_mutex); + nic = nic_list; + while (nic != NULL) { + if (nic->uio_minor == minor) { + /* TODO: we need to wait for all the UIO entries to + * appear in sysfs. Need to determine a discrete way + * to determine if this entry extsts */ + struct timespec sleep_req, sleep_rem; + + sleep_req.tv_sec = 1; + sleep_req.tv_nsec = 5000000; + + nanosleep(&sleep_req, &sleep_rem); + + /* Ensure that this is still a bnx2_cnic device */ + rc = nic_verify_uio_sysfs_name(nic); + if (rc != 0) { + LOG_WARN(PFX "%s: Could not verify device", + nic->log_name); + } + + nic->log_name = nic->eth_device_name; + + /* Ensure all the NIC fields are initialized */ + prepare_nic(nic); + + if (nic->flags & NIC_UNITIALIZED) { + LOG_INFO(PFX "toggling cnic: %s to start", + nic->log_name); + pthread_mutex_lock(&nic->uio_wait_mutex); + pthread_cond_broadcast(&nic->uio_wait_cond); + pthread_mutex_unlock(&nic->uio_wait_mutex); + } + + break; + } + + nic = nic->next; + } + pthread_mutex_unlock(&nic_list_mutex); + + if (nic == NULL) { + int uio_minor; + char eth_name[IFNAMSIZ]; + + /* time to alloc a new cnic/uio device */ + LOG_INFO(PFX "Couldn't find dev instance"); + + nic = nic_init(); + if(nic == NULL) + { + LOG_ERR(PFX "Could not allocate memory for device"); + + rc =-ENOMEM; + goto error; + } + + nic->uio_minor = minor; + + /* Malloc space for the uio name */ + nic->uio_device_name = malloc(sizeof(uio_udev_path_template) + 8); + if(nic->uio_device_name == NULL) + { + LOG_ERR(PFX "Could not allocate space for device name"); + + rc = -ENOMEM; + goto error; + } + + snprintf(nic->uio_device_name, + sizeof(uio_udev_path_template) + 8, + uio_udev_path_template, minor); + + nic->flags |= NIC_UIO_NAME_MALLOC; + + rc = from_uio_find_associated_eth_device(uio_minor, + nic->eth_device_name, + sizeof(nic->eth_device_name)); + + nic->config_device_name = nic->eth_device_name; + nic->log_name = nic->eth_device_name; + + /* Ensure all the NIC fields are initialized */ + prepare_nic(nic); + } + + rc = 0; + +error: + return rc; +} + +/****************************************************************************** + * close_cnic_dev() - This function will notify all those waiting on the + * uio_wait_event conditionial. This should trigger + * all threads waiting on this event. + * @param event - The uevent which comes from the kernel + * @param return - 0 on success, <0 on failure + *****************************************************************************/ +static int close_cnic_dev(struct parsed_uevent *event) +{ + int minor; + nic_t *nic; + int rc; + + rc = sscanf(event->minor, "%d", &minor); + if(rc == 1) { + LOG_INFO(PFX "Removing uio device: minor: %d", minor); + } else { + LOG_INFO(PFX "Couldn't parse minor number: %s", event->minor); + return -EIO; + } + + pthread_mutex_lock(&nic_list_mutex); + nic = nic_list; + while (nic != NULL) { + if (nic->uio_minor == minor) { + nic_remove(nic, 1); + + rc = 0; + break; + } + + nic = nic->next; + } + pthread_mutex_unlock(&nic_list_mutex); + + if(nic == NULL) + { + LOG_INFO(PFX "Couldn't find nic to close"); + return -EINVAL; + } + + return rc; +} + +/** + * uevent_close() - This function is called when exiting the uevent watch + * loop. This function will clean up the Netlink socket + * watching for uevents from the kernel. + */ +static void uevent_close(void *arg) +{ + if (uevent_netlink_sock != -1) + close(uevent_netlink_sock); +} + +/** + * uevent_watch_loop() - This function will use Netlink to watch for uevents + * coming from the kernel. This operates much like + * udev. + */ +static void *uevent_watch_loop(void *arg) +{ + int rc; + sigset_t set; + + pthread_cleanup_push(uevent_close, NULL); + + sigfillset(&set); + rc = pthread_sigmask(SIG_BLOCK, &set, NULL); + if (rc != 0) { + LOG_ERR(PFX "Couldn't set signal mask for the uevent thread"); + } + + while (1) { + ssize_t size; + char buffer[1024]; + struct parsed_uevent event; + + size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0); + + if ((size_t) size > sizeof(buffer) - 1) + size = sizeof(buffer) - 1; + buffer[size] = '\0'; + + memset(&event, 0, sizeof(event)); + parse_uevent(buffer, size, &event); + + /* check if a uio device has been added */ + if ((memcmp(event.action, + uevent_add, sizeof(uevent_add)) == 0) && + (memcmp(event.subsystem, + uevent_uio, sizeof(uevent_uio)) == 0)) { + /* Found a cnic device */ + wakeup_cnic_dev(&event); + + /* check if a uio device has been removed */ + } else if ((memcmp(event.action, + uevent_remove, sizeof(uevent_remove)) == 0)&& + (memcmp(event.subsystem, + uevent_uio, sizeof(uevent_uio)) == 0)) { + /* Found a cnic device */ + close_cnic_dev(&event); + + /* check if a bnx2i device has been added */ + } else if ((memcmp(event.action, + uevent_add, sizeof(uevent_add)) == 0) && + (memcmp(event.subsystem, + uevent_bnx2i, sizeof(uevent_bnx2i)) == 0)) { + /* Found a bnx2i device */ + pthread_mutex_lock(&bnx2i_module_loaded_mutex); + bnx2i_loaded = 1; + + pthread_cond_broadcast(&bnx2i_module_loaded_cond); + pthread_mutex_unlock(&bnx2i_module_loaded_mutex); + + /* check if a bnx2i device has been removed */ + } else if ((memcmp(event.action, + uevent_remove, sizeof(uevent_remove)) == 0)&& + (memcmp(event.subsystem, + uevent_uio, sizeof(uevent_bnx2i)) == 0)) { + /* Found a cnic device */ + pthread_mutex_lock(&bnx2i_module_loaded_mutex); + bnx2i_loaded = 0; + + pthread_cond_broadcast(&bnx2i_module_loaded_cond); + pthread_mutex_unlock(&bnx2i_module_loaded_mutex); + + } else if ((memcmp(event.action, + uevent_add, sizeof(uevent_add)) == 0) && + (memcmp(event.devpath, + uevent_cnic_devpath, + sizeof(uevent_cnic_devpath)) == 0) && + (memcmp(event.subsystem, + uevent_cnic_subsystem, + sizeof(uevent_cnic_subsystem)) == 0)) { + + LOG_INFO("CNIC module has been loaded"); + pthread_mutex_lock(&cnic_module_loaded_mutex); + pthread_cond_broadcast(&cnic_module_loaded_cond); + cnic_loaded = 1; + pthread_mutex_unlock(&cnic_module_loaded_mutex); + } else if ((memcmp(event.action, + uevent_add, sizeof(uevent_add)) == 0) && + (memcmp(event.devpath, + uevent_net_devpath, + sizeof(uevent_net_devpath) -1 ) == 0) && + (memcmp(event.subsystem, + uevent_net_subsystem, + sizeof(uevent_net_subsystem)) == 0)) { + uint16_t vlan_id; + int rc; + char *vlan_iface_name; + struct vlan_handle handle; + + LOG_DEBUG("New NIC interface %s has been discovered", + event.interface); + + init_vlan_table(&handle); + rc = capture_vlan_table(&handle); + if(rc != 0) { + LOG_ERR(PFX "Failed to capture VLAN table"); + } + + rc = find_phy_using_vlan_interface(&handle, + event.interface, + &vlan_iface_name, + &vlan_id); + if(rc == 1) + { + LOG_DEBUG("Interface %s is a vlan", + event.interface); + } + + release_vlan_table(&handle); + } + } + + pthread_cleanup_pop(0); + + pthread_exit(NULL); +} + +/******************************************************************************* + * Public Functions + ******************************************************************************/ +/** + * init_uevent_netlink_sock() - This is used to initialize the NetLink + * connection to the kernel to listen for uevents + * @return 0 on success, <0 on failure + */ +int init_uevent_netlink_sock() +{ + struct sockaddr_nl snl; + const int buffersize = 16 * 1024 * 1024; + int rc; + + memset(&snl, 0x00, sizeof(struct sockaddr_nl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = getpid(); + snl.nl_groups = 1; + + uevent_netlink_sock = socket(PF_NETLINK, + SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (uevent_netlink_sock == -1) { + LOG_ERR(PFX "error getting socket: %s", strerror(errno)); + return -EIO; + } + + /* set receive buffersize */ + setsockopt(uevent_netlink_sock, + SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); + + rc = bind(uevent_netlink_sock, + (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)); + if (rc < 0) { + LOG_ERR(PFX "bind failed: %s", strerror(errno)); + goto error; + } + + /* Spin up the thread used to watch */ + rc = pthread_create(&uevent_watch_thread, NULL, + uevent_watch_loop, NULL); + if (rc != 0) { + LOG_ERR(PFX "Could not create thread for watching uevents"); + goto error; + } + + LOG_INFO(PFX "Listening for uevents"); + + return 0; + + error: + close(uevent_netlink_sock); + uevent_netlink_sock = -1; + return rc; +} + +/** + * cleanup_uevent_netlink_sock() - This is used to close the NetLink + * connection to the kernel listening for + * uevents + * @return 0 on success, <0 on failure + */ +int cleanup_uevent_netlink_sock() +{ + if (uevent_watch_thread != INVALID_THREAD) { + int rc; + + /* Notify that the uevent thread needs to stop */ + rc = pthread_cancel(uevent_watch_thread); + if (rc != 0) { + LOG_ERR(PFX "Could not cancel uevent thread: %s", + strerror(rc)); + } + + /* Wait for the uevent thread to stop */ + rc = pthread_join(uevent_watch_thread, NULL); + if (rc != 0) { + LOG_ERR(PFX "Could not kill uevent thread"); + } + + uevent_watch_thread = INVALID_THREAD; + } + + LOG_INFO(PFX "uevent thread closed"); + + return 0; +} diff --git a/brcm_iscsi_uio/src/unix/uevent.h b/brcm_iscsi_uio/src/unix/uevent.h new file mode 100644 index 0000000..98484e4 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/uevent.h @@ -0,0 +1,54 @@ +/* uevent.h: CNIC UIO uIP user space stack + * + * Copyright (c) 2004-2008 Broadcom Corporation + * + * 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. + * + * Written by: Benjamin Li (benli@broadcom.com) + */ + +#ifndef __UEVENT_H__ +#define __UEVENT_H__ + +#include + +#ifdef ENABLE_LOG_UEVENT +#define LOG_UEVENT LOG_DEBUG +#else +#define LOG_UEVENT(fmt, args...) +#endif + +extern pthread_mutex_t cnic_module_loaded_mutex; +extern pthread_cond_t cnic_module_loaded_cond; +extern int cnic_loaded; + +extern pthread_mutex_t bnx2i_module_loaded_mutex; +extern pthread_cond_t bnx2i_module_loaded_cond; +extern int bnx2i_loaded; + +struct parsed_uevent { + char *init; + char *action; + char *devpath; + char *subsystem; + char *driver; + char *seqnum; + char *devpath_old; + char *physdevpath; + char *physdevbus; + char *physdevdriver; + char *major; + char *minor; + char *timeout; + + /* Seen when network interfaces appear */ + char *interface; + char *ifindex; +}; + +int init_uevent_netlink_sock(); +int cleanup_uevent_netlink_sock(); + +#endif /* __UEVENT_H__ */ diff --git a/brcm_iscsi_uio/src/unix/uip-conf.h b/brcm_iscsi_uio/src/unix/uip-conf.h new file mode 100644 index 0000000..a277fe3 --- /dev/null +++ b/brcm_iscsi_uio/src/unix/uip-conf.h @@ -0,0 +1,159 @@ +/** + * \addtogroup uipopt + * @{ + */ + +/** + * \name Project-specific configuration options + * @{ + * + * uIP has a number of configuration options that can be overridden + * for each project. These are kept in a project-specific uip-conf.h + * file and all configuration names have the prefix UIP_CONF. + */ + +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: uip-conf.h,v 1.6 2006/06/12 08:00:31 adam Exp $ + */ + +/** + * \file + * An example uIP configuration file + * \author + * Adam Dunkels + */ + +#ifndef __UIP_CONF_H__ +#define __UIP_CONF_H__ + +#include + +/** + * 8 bit datatype + * + * This typedef defines the 8-bit type used throughout uIP. + * + * \hideinitializer + */ +typedef uint8_t u8_t; + +/** + * 16 bit datatype + * + * This typedef defines the 16-bit type used throughout uIP. + * + * \hideinitializer + */ +typedef uint16_t u16_t; + +/** + * 32 bit datatype + * + * This typedef defines the 16-bit type used throughout uIP. + * + * \hideinitializer + */ +typedef uint32_t u32_t; + +/** + * Statistics datatype + * + * This typedef defines the dataype used for keeping statistics in + * uIP. + * + * \hideinitializer + */ +typedef uint64_t uip_stats_t; + +/** + * Maximum number of TCP connections. + * + * \hideinitializer + */ +#define UIP_CONF_MAX_CONNECTIONS 40 + +/** + * Maximum number of listening TCP ports. + * + * \hideinitializer + */ +#define UIP_CONF_MAX_LISTENPORTS 40 + +/** + * uIP buffer size. + * + * \hideinitializer + */ +#define UIP_CONF_BUFFER_SIZE 420 + +/** + * CPU byte order. + * + * \hideinitializer + */ +#define UIP_CONF_BYTE_ORDER LITTLE_ENDIAN + +/** + * Logging on or off + * + * \hideinitializer + */ +#define UIP_CONF_LOGGING 1 + +/** + * UDP support on or off + * + * \hideinitializer + */ +#define UIP_CONF_UDP 1 + +/** + * UDP checksums on or off + * + * \hideinitializer + */ +#define UIP_CONF_UDP_CHECKSUMS 1 + +/** + * uIP statistics on or off + * + * \hideinitializer + */ +#define UIP_CONF_STATISTICS 1 + + +#define UIP_CONF_IPV6 0 + +#endif /* __UIP_CONF_H__ */ + +/** @} */ +/** @} */ diff --git a/usr/Makefile b/usr/Makefile index 4d3c71f..3d1a1d6 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -41,7 +41,7 @@ ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iface.o idbm.o sy COMMON_SRCS = $(ISCSI_LIB_SRCS) # core initiator files INITIATOR_SRCS = initiator.o scsi.o actor.o event_poll.o mgmt_ipc.o isns.o \ - cxgb3i.o be2iscsi.o transport.o + cxgb3i.o be2iscsi.o transport.o uip_mgmt_ipc.o # fw boot files FW_BOOT_SRCS = $(wildcard ../utils/fwparam_ibft/*.o) diff --git a/usr/initiator.c b/usr/initiator.c index 8735af9..52008ca 100644 --- a/usr/initiator.c +++ b/usr/initiator.c @@ -45,6 +45,7 @@ #include "iscsi_sysfs.h" #include "iscsi_settings.h" #include "iface.h" +#include "host.h" #include "sysdeps.h" #define ISCSI_CONN_ERR_REOPEN_DELAY 3 @@ -713,6 +714,37 @@ static int iscsi_conn_connect(struct iscsi_conn *conn, queue_task_t *qtask) return 0; } +static int __set_net_config(struct iscsi_transport *t, + iscsi_session_t *session, + struct iface_rec *iface) +{ + if (t->template->set_net_config) { + /* uip needs the netdev name */ + struct host_info hinfo; + int hostno, rc; + + /* this assumes that the netdev or hw address is going to be + set */ + hostno = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc); + if (rc) { + log_debug(4, "Couldn't get host no.\n"); + return rc; + } + + /* uip needs the netdev name */ + if (!strlen(iface->netdev)) { + memset(&hinfo, 0, sizeof(hinfo)); + hinfo.host_no = hostno; + iscsi_sysfs_get_hostinfo_by_host_no(&hinfo); + strcpy(iface->netdev, hinfo.iface.netdev); + } + + return t->template->set_net_config(t, iface, session); + } + + return 0; +} + static void __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop, int redirected) @@ -754,6 +786,11 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop, if (!redirected) session->reopen_cnt++; + /* uIP will needs to be re-triggered on the connection re-open */ + if (__set_net_config(conn->session->t, conn->session, + &conn->session->nrec.iface) != 0) + goto queue_reopen; + if (iscsi_conn_connect(conn, qtask)) { delay = ISCSI_CONN_ERR_REOPEN_DELAY; goto queue_reopen; @@ -2103,6 +2140,10 @@ static int iface_set_param(struct iscsi_transport *t, struct iface_rec *iface, session->conn[0].bind_ep = 1; session->hostno = hostno; + rc = __set_net_config(t, session, iface); + if (rc != 0) + return rc; + rc = __iscsi_host_set_param(t, session->hostno, ISCSI_HOST_PARAM_IPADDRESS, iface->ipaddress, ISCSI_STRING); diff --git a/usr/transport.c b/usr/transport.c index ebdfb1b..ad4ac52 100644 --- a/usr/transport.c +++ b/usr/transport.c @@ -25,6 +25,7 @@ #include "log.h" #include "util.h" #include "iscsi_sysfs.h" +#include "uip_mgmt_ipc.h" #include "cxgb3i.h" #include "be2iscsi.h" @@ -58,6 +59,7 @@ struct iscsi_transport_template bnx2i = { .ep_connect = ktransport_ep_connect, .ep_poll = ktransport_ep_poll, .ep_disconnect = ktransport_ep_disconnect, + .set_net_config = uip_broadcast_params, }; struct iscsi_transport_template be2iscsi = { diff --git a/usr/transport.h b/usr/transport.h index 5ceedb3..2ec903c 100644 --- a/usr/transport.h +++ b/usr/transport.h @@ -35,6 +35,9 @@ struct iscsi_transport_template { int (*ep_poll) (struct iscsi_conn *conn, int timeout_ms); void (*ep_disconnect) (struct iscsi_conn *conn); void (*create_conn) (struct iscsi_conn *conn); + int (*set_net_config) (struct iscsi_transport *t, + struct iface_rec *iface, + struct iscsi_session *session); }; /* represents data path provider */ diff --git a/usr/uip_mgmt_ipc.c b/usr/uip_mgmt_ipc.c new file mode 100644 index 0000000..a34a943 --- /dev/null +++ b/usr/uip_mgmt_ipc.c @@ -0,0 +1,41 @@ +/* + * uIP iSCSI Daemon/Admin Management IPC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ + +#include + +#include "log.h" +#include "uip_mgmt_ipc.h" +#include "util.h" + +int uip_broadcast_params(struct iscsi_transport *t, + struct iface_rec *iface, + struct iscsi_session *session) +{ + struct iscsid_uip_broadcast broadcast; + + log_debug(3, "broadcasting to uip\n"); + + memset(&broadcast, 0, sizeof(broadcast)); + + broadcast.header.command = ISCSID_UIP_IPC_GET_IFACE; + broadcast.header.payload_len = sizeof(*iface); + + memcpy(&broadcast.u.iface_rec, iface, sizeof(*iface)); + + return uip_broadcast(&broadcast, + sizeof(iscsid_uip_broadcast_header_t) + + sizeof(*iface)); +} diff --git a/usr/uip_mgmt_ipc.h b/usr/uip_mgmt_ipc.h new file mode 100644 index 0000000..dd49c0b --- /dev/null +++ b/usr/uip_mgmt_ipc.h @@ -0,0 +1,71 @@ +/* + * uIP iSCSI Daemon/Admin Management IPC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ +#ifndef UIP_MGMT_IPC_H +#define UIP_MGMT_IPC_H + +#include "types.h" +#include "iscsi_if.h" +#include "config.h" +#include "mgmt_ipc.h" + +#include "initiator.h" +#include "transport.h" + +#define ISCSID_UIP_NAMESPACE "ISCSID_UIP_ABSTRACT_NAMESPACE" + +typedef enum iscsid_uip_cmd { + ISCSID_UIP_IPC_UNKNOWN = 0, + ISCSID_UIP_IPC_GET_IFACE = 1, + + __ISCSID_UIP_IPC_MAX_COMMAND +} iscsid_uip_cmd_e; + +typedef struct iscsid_uip_broadcast_header { + iscsid_uip_cmd_e command; + uint32_t payload_len; +} iscsid_uip_broadcast_header_t; + +/* IPC Request */ +typedef struct iscsid_uip_broadcast { + struct iscsid_uip_broadcast_header header; + + union { + /* messages */ + struct ipc_broadcast_iface_rec { + struct iface_rec rec; + } iface_rec; + } u; +} iscsid_uip_broadcast_t; + +typedef enum iscsid_uip_mgmt_ipc_err { + ISCSID_UIP_MGMT_IPC_OK = 0, + ISCISD_UIP_MGMT_IPC_ERR = 1, + ISCISD_UIP_MGMT_IPC_ERR_NOT_FOUND = 2, + ISCISD_UIP_MGMT_IPC_ERR_NOMEM = 3, +} iscsid_uip_mgmt_ipc_err_e; + +/* IPC Response */ +typedef struct iscsid_uip_mgmt_rsp { + iscsid_uip_cmd_e command; + iscsid_uip_mgmt_ipc_err_e err; +} iscsid_uip_rsp_t; + +extern int uip_broadcast_params(struct iscsi_transport *t, + struct iface_rec *iface, + struct iscsi_session *session); + + +#endif /* UIP_MGMT_IPC_H */ diff --git a/usr/util.c b/usr/util.c index e72ed15..390c895 100644 --- a/usr/util.c +++ b/usr/util.c @@ -23,6 +23,7 @@ #include "idbm.h" #include "iface.h" #include "session_info.h" +#include "uip_mgmt_ipc.h" void daemon_init(void) { @@ -201,6 +202,44 @@ static void iscsid_startup(void) #define MAXSLEEP 128 +static mgmt_ipc_err_e ipc_connect(int *fd, char *unix_sock_name) +{ + int nsec; + struct sockaddr_un addr; + + *fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (*fd < 0) { + log_error("can not create IPC socket (%d)!", errno); + return MGMT_IPC_ERR_ISCSID_NOTCONN; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + memcpy((char *) &addr.sun_path + 1, unix_sock_name, + strlen(unix_sock_name)); + /* + * Trying to connect with exponential backoff + */ + for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) { + if (connect(*fd, (struct sockaddr *) &addr, sizeof(addr)) == 0) + /* Connection established */ + return MGMT_IPC_OK; + + /* If iscsid isn't there, there's no sense + * in retrying. */ + if (errno == ECONNREFUSED) + break; + + /* + * Delay before trying again + */ + if (nsec <= MAXSLEEP/2) + sleep(nsec); + } + log_error("can not connect to iSCSI daemon (%d)!", errno); + return MGMT_IPC_ERR_ISCSID_NOTCONN; +} + static mgmt_ipc_err_e iscsid_connect(int *fd, int start_iscsid) { int nsec; @@ -340,6 +379,72 @@ int iscsid_req_by_sid(iscsiadm_cmd_e cmd, int sid) return iscsid_req_wait(cmd, fd); } +static mgmt_ipc_err_e uip_connect(int *fd) +{ + return ipc_connect(fd, ISCSID_UIP_NAMESPACE); +} + +int uip_broadcast(void *buf, size_t buf_len) +{ + int err; + int fd; + iscsid_uip_rsp_t rsp; + int flags; + int count; + + err = uip_connect(&fd); + if (err) { + log_warning("uIP daemon is not up"); + return err; + } + + /* Send the data to uIP */ + if ((err = write(fd, buf, buf_len)) != buf_len) { + log_error("got write error (%d/%d), daemon died?", + err, errno); + close(fd); + return -EIO; + } + + /* Set the socket to a non-blocking read, this way if there are + * problems waiting for uIP, iscsid can bailout early */ + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) + flags = 0; + err = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if(err != 0) { + log_error("could not set uip broadcast to non-blocking: %d", + errno); + close(fd); + return -EIO; + } + +#define MAX_UIP_BROADCAST_READ_TRIES 3 + for(count = 0; count < MAX_UIP_BROADCAST_READ_TRIES; count++) { + /* Wait for the response */ + err = read(fd, &rsp, sizeof(rsp)); + if (err == sizeof(rsp)) { + log_debug(3, "Broadcasted to uIP with length: %ld\n", + buf_len); + break; + } else if((err == -1) && (errno == EAGAIN)) { + usleep(250000); + continue; + } else { + log_error("Could not read response (%d/%d), daemon died?", + err, errno); + break; + } + } + + if(count == MAX_UIP_BROADCAST_READ_TRIES) + log_error("Could not broadcast to uIP"); + + close(fd); + + return 0; +} + void idbm_node_setup_defaults(node_rec_t *rec) { int i; diff --git a/usr/util.h b/usr/util.h index 1d94496..d69d6c4 100644 --- a/usr/util.h +++ b/usr/util.h @@ -35,5 +35,6 @@ extern int __iscsi_match_session(struct node_rec *rec, char *targetname, extern char *strstrip(char *s); extern char *get_global_string_param(char *pathname, const char *key); +extern int uip_broadcast(void *buf, size_t buf_len); #endif