diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bdd3f87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# standard autotools stuff +.deps/ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +config.guess +config.log +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +missing + +# other autoconf generated files +libhbalinux.spec + +# compile generated files +*.o +*.lo +*.la +.libs + +# build.sh generated files +libhbalinux-*.tar.gz +libhbalinux-*.rpm diff --git a/CONFIGURE b/CONFIGURE new file mode 100644 index 0000000..d3c5b40 --- /dev/null +++ b/CONFIGURE @@ -0,0 +1,237 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007 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. + +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. + +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 the options to `configure', and exit. + +`--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. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..1837b0a --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d60ba7d --- /dev/null +++ b/INSTALL @@ -0,0 +1,25 @@ +This document explains how to build the Linux vendor +library for the HBA API. + +## +# libhbalinux +############## + +DEPENDENCIES + +* HBA API Wrapper Library + +* autoconf +* autotools +* sysconftool +* automake +* libtool +* libpciaccess-devel + +PROCESS + +1) Bootstrap, configure, make and make install + # ./bootstrap.sh + # rpm --eval "%configure" | sh + # make + # make install diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..1349e7b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,34 @@ +AM_CFLAGS = $(HBAAPI_CFLAGS) $(PCIACCESS_CFLAGS) +AM_LDFLAGS= $(PCIACCESS_LIBS) + +lib_LTLIBRARIES = libhbalinux.la +libhbalinux_la_SOURCES = adapt.c adapt_impl.h api_lib.h bind.c bind_impl.h \ +fc_scsi.h fc_types.h lib.c lport.c net_types.h pci.c rport.c scsi.c sg.c \ +utils.c utils.h +libhbalinux_la_LDFLAGS = -version-info 2:2:0 +libhbalinux_la_LIBADD = $(PCIACCESS_LIBS) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libhbalinux.pc + +dist_noinst_DATA = README COPYING INSTALL libhbalinux.pc.in libhbalinux.spec + +install-data-hook: libhbalinux.la + . $${PWD}/$<; \ + ORG=org.open-fcoe.libhbalinux; \ + LIB=${libdir}/$${dlname}; \ + STR="$$ORG $$LIB"; \ + CONF=${sysconfdir}/hba.conf; \ + if test -f $$CONF; then \ + grep -E -q ^[[:space:]]*$$ORG[[:space:]]+$$LIB $$CONF; \ + if test $$? -ne 0; then \ + echo $$STR >> $$CONF; \ + else \ + echo "** $$CONF already configured"; \ + echo "** system configuration not updated"; \ + fi; \ + else \ + echo "** WARNING: $$CONF does not exist"; \ + echo "** system configuration not updated"; \ + fi + diff --git a/README b/README new file mode 100644 index 0000000..d1d71f4 --- /dev/null +++ b/README @@ -0,0 +1,62 @@ + Linux* HBAAPI Vendor Library + ============================ + +Introduction +------------ + +HBAAPI stands for Host Bus Adapter API (Applications Programming Interface). +It is a C-level shared library to manage Fibre Channel Host Bus Adapters. +The HBAAPI library source code may be downloaded from http://sourceforge.net/. +For a detailed description of HBAAPI please see the FC-HBA working draft in +http://www.t11.org/index.html. The HBAAPI shared library will also be built +when the vendor library is built, and will be named as libHBAAPI.so. + +The HBAAPI vendor library is a shared library with functions that support +the API routines in the HBAAPI library. The HBAAPI vendor library will be +named as libhbalinux.so and is loaded by the HBAAPI library as a dynamic +library when the HBAAPI library is initialized. The vendor library invokes +the /sys file system for information of FCoE network adapters, local ports, +remote ports and discovered LUNs. It also gets adapter information with +the assistance from libpciaccess while the information are not available in +/sys. The ioctl calls are only used for SG_IO to issue SCSI commands to +generic scsi block devices. No ioctl are called to the libfc.ko or fcoe.ko +modules. + +When applications are developed to link with libHBAAPI.so, they may +indirectly invoke libhbalinux.so behind the libHBAAPI.so. For instructions +of how to build and install the libraries, please see the file INSTALL. + + +In This Release +--------------- + +This release of the vendor library is implemented in association with the +version 2.2 (August 2002) of HBAAPI code from Source Forge. + +The supported API's in this release are: + + HBA_GetVersion + HBA_LoadLibrary + HBA_FreeLibrary + HBA_RegisterLibrary + HBA_GetNumberOfAdapters + HBA_GetAdapterName + HBA_OpenAdapter + HBA_CloseAdapter + HBA_GetAdapterAttributes + HBA_GetAdapterPortAttributes + HBA_GetPortStatistics + HBA_GetFC4Statistics + HBA_GetFcpTargetMapping + HBA_GetFcpTargetMappingV2 + HBA_SendScsiInquiry + HBA_SendReportLUNs + HBA_SendReadCapacity + HBA_ScsiInquiryV2 + HBA_ScsiReportLUNsV2 + HBA_ScsiReadCapacityV2 + +Libhbalinux is maintained at www.Open-FCoE.org and the latest version can +be obtained there. Questions, comments and contributions should take place +on the development mailing list at www.Open-FCoE.org as well. + diff --git a/adapt.c b/adapt.c new file mode 100644 index 0000000..cdeba76 --- /dev/null +++ b/adapt.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "utils.h" +#include "api_lib.h" +#include "adapt_impl.h" + +static struct sa_table adapter_table; +static const u_int32_t adapter_handle_offset = 0x100; + +#define HBA_SHORT_NAME_LIMIT 64 + +/* + * Support for adapter information. + */ +HBA_UINT32 +adapter_get_count(void) +{ + return adapter_table.st_limit; +} + +/* + * Get adapter name. + */ +HBA_STATUS +adapter_get_name(HBA_UINT32 index, char *buf) +{ + HBA_STATUS status; + struct adapter_info *ap; + + status = HBA_STATUS_ERROR_ILLEGAL_INDEX; + ap = sa_table_lookup(&adapter_table, index); + if (ap != NULL) { + snprintf(buf, HBA_SHORT_NAME_LIMIT, + "%s-%u", ap->ad_name, index); + status = HBA_STATUS_OK; + } + return status; +} + +/* + * Add an adapter to the table. + */ +HBA_STATUS +adapter_create(struct adapter_info *ap) +{ + int index; + + index = sa_table_append(&adapter_table, ap); + if (index < 0) + return HBA_STATUS_ERROR; + ap->ad_index = index; + return HBA_STATUS_OK; +} + +void +adapter_destroy(struct adapter_info *ap) +{ + sa_table_destroy_all(&ap->ad_ports); + free(ap); +} + +void +adapter_destroy_all(void) +{ + struct adapter_info *ap; + int i; + + for (i = 0; i < adapter_table.st_limit; i++) { + ap = adapter_table.st_table[i]; + if (ap) { + adapter_table.st_table[i] = NULL; + adapter_destroy(ap); + } + } + sa_table_destroy(&adapter_table); +} + +struct adapter_info * +adapter_open_handle(HBA_HANDLE handle) +{ + return sa_table_lookup(&adapter_table, handle - + adapter_handle_offset); +} + +struct port_info * +adapter_get_port(HBA_HANDLE handle, HBA_UINT32 port) +{ + struct adapter_info *ap; + struct port_info *pp = NULL; + + ap = adapter_open_handle(handle); + if (ap) + pp = sa_table_lookup(&ap->ad_ports, port); + return pp; +} + +struct port_info * +adapter_get_rport(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 rport) +{ + struct port_info *pp; + struct port_info *rp = NULL; + + pp = adapter_get_port(handle, port); + if (pp) { + get_rport_info(pp); + rp = sa_table_lookup(&pp->ap_rports, rport); + } + return rp; +} + +/* + * Get the Nth discovered port information. + */ +struct port_info * +adapter_get_rport_n(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 n) +{ + struct port_info *pp; + struct port_info *rp = NULL; + + pp = adapter_get_port(handle, port); + if (pp) { + get_rport_info(pp); + rp = sa_table_lookup_n(&pp->ap_rports, n); + } + return rp; +} + +static void * +adapter_target_match(void *rp_arg, void *target_arg) +{ + struct port_info *rp = rp_arg; + + if (rp->ap_scsi_target != *(u_int32_t *)target_arg) + rp_arg = NULL; + return rp_arg; +} + +/* + * Get the rport by scsi_target number. + */ +struct port_info * +adapter_get_rport_target(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 n) +{ + struct port_info *pp; + struct port_info *rp = NULL; + + pp = adapter_get_port(handle, port); + if (pp) { + get_rport_info(pp); + rp = sa_table_search(&pp->ap_rports, + adapter_target_match, &n); + } + return rp; +} + +static void * +adapter_wwpn_match(void *rp_arg, void *wwpn_arg) +{ + struct port_info *rp = rp_arg; + + if (memcmp(&rp->ap_attr.PortWWN, wwpn_arg, sizeof(HBA_WWN)) != 0) + rp_arg = NULL; + return rp_arg; +} + +struct port_info * +adapter_get_rport_by_wwn(struct port_info *pp, HBA_WWN wwpn) +{ + struct port_info *rp; + + get_rport_info(pp); + rp = sa_table_search(&pp->ap_rports, adapter_wwpn_match, &wwpn); + return rp; +} + +static void * +adapter_fcid_match(void *rp_arg, void *fcid_arg) +{ + struct port_info *rp = rp_arg; + + if (rp->ap_attr.PortFcId != *(fc_fid_t *)fcid_arg) + rp_arg = NULL; + return rp_arg; +} + +struct port_info * +adapter_get_rport_by_fcid(struct port_info *pp, fc_fid_t fcid) +{ + struct port_info *rp; + + get_rport_info(pp); + rp = sa_table_search(&pp->ap_rports, adapter_fcid_match, &fcid); + return rp; +} + +/* + * Open adapter by name. + */ +HBA_HANDLE +adapter_open(char *name) +{ + char buf[256]; + HBA_HANDLE i; + HBA_STATUS status; + + for (i = 0; i < adapter_table.st_limit; i++) { + status = adapter_get_name(i, buf); + if (status != HBA_STATUS_OK) + return 0; + if (!strcmp(buf, name)) + return adapter_handle_offset + i; + } + return 0; +} + +/* + * Get port by WWPN. + * Returns NULL if WWN not unique. + * If countp is non-NULL, the int it points to will be set to the + * number found so that the caller can tell if the WWN was ambiguous. + */ +struct port_info * +adapter_get_port_by_wwn(HBA_HANDLE handle, HBA_WWN wwn, int *countp) +{ + struct adapter_info *ap; + struct port_info *pp_found = NULL; + struct port_info *pp; + int count = 0; + int p; + + ap = adapter_open_handle(handle); + if (ap != NULL) { + for (p = 0; p < ap->ad_ports.st_limit; p++) { + pp = ap->ad_ports.st_table[p]; + if (pp && + !memcmp(&pp->ap_attr.PortWWN, &wwn, sizeof(wwn))) { + count++; + pp_found = pp; + } + } + } + if (count > 1) + pp_found = NULL; + if (countp != NULL) + *countp = count; + return pp_found; +} + +/* + * Open adapter by WWN. + */ +HBA_STATUS +adapter_open_by_wwn(HBA_HANDLE *phandle, HBA_WWN wwn) +{ + struct adapter_info *ap; + struct port_info *pp; + HBA_HANDLE found_handle = 0; + int count = 0; + HBA_STATUS status; + int i; + int p; + + for (i = 0; i < adapter_table.st_limit; i++) { + ap = adapter_table.st_table[i]; + if (!ap) + continue; + if (memcmp(&ap->ad_attr.NodeWWN, &wwn, sizeof(wwn)) == 0) { + count++; + found_handle = ap->ad_index + adapter_handle_offset; + } else { + for (p = 0; p < ap->ad_ports.st_limit; p++) { + pp = ap->ad_ports.st_table[p]; + if (!pp) + continue; + if (memcmp(&pp->ap_attr.PortWWN, + &wwn, sizeof(wwn)) == 0) { + count++; + found_handle = ap->ad_index + + adapter_handle_offset; + } + } + } + } + + *phandle = HBA_HANDLE_INVALID; + if (count == 1) { + status = HBA_STATUS_OK; + *phandle = found_handle; + } else if (count > 1) { + status = HBA_STATUS_ERROR_AMBIGUOUS_WWN; + } else { + status = HBA_STATUS_ERROR_ILLEGAL_WWN; + } + return status; +} + +/* + * Close adapter. + */ +void +adapter_close(HBA_HANDLE handle) +{ +} + +/* + * Get adapter attributes. + */ +HBA_STATUS +adapter_get_attr(HBA_HANDLE handle, HBA_ADAPTERATTRIBUTES *pattr) +{ + struct adapter_info *ap; + + ap = adapter_open_handle(handle); + if (ap) { + *pattr = ap->ad_attr; /* struct copy */ + return HBA_STATUS_OK; + } + return HBA_STATUS_ERROR; +} + +/* + * Get adapter port attributes. + */ +HBA_STATUS +adapter_get_port_attr(HBA_HANDLE handle, HBA_UINT32 port, + HBA_PORTATTRIBUTES *pattr) +{ + struct port_info *pp; + + pp = adapter_get_port(handle, port); + if (pp) { + *pattr = pp->ap_attr; /* struct copy */ + return HBA_STATUS_OK; + } + return HBA_STATUS_ERROR; +} + +/* + * Get discovered (remote) port attributes. + */ +HBA_STATUS +adapter_get_rport_attr(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 rport, + HBA_PORTATTRIBUTES *pattr) +{ + struct port_info *rp; + + rp = adapter_get_rport_n(handle, port, rport); + if (rp) { + *pattr = rp->ap_attr; /* struct copy */ + return HBA_STATUS_OK; + } + return HBA_STATUS_ERROR; +} + +/* + * Get adapter port attributes. + */ +HBA_STATUS +adapter_get_port_attr_by_wwn(HBA_HANDLE handle, HBA_WWN wwn, + HBA_PORTATTRIBUTES *pattr) +{ + struct adapter_info *ap; + struct port_info *pp; + struct port_info *pp_found = NULL; + u_int32_t p; + int count = 0; + HBA_STATUS status; + + ap = adapter_open_handle(handle); + if (ap != NULL) { + for (p = 0; p < ap->ad_ports.st_limit; p++) { + pp = ap->ad_ports.st_table[p]; + if (pp == NULL) + continue; + if (!memcmp(&pp->ap_attr.PortWWN, &wwn, sizeof(wwn))) { + count++; + pp_found = pp; + } + pp = sa_table_search(&pp->ap_rports, + adapter_wwpn_match, + &wwn); + if (pp) { + count++; + pp_found = pp; + } + } + } + pp = pp_found; + if (pp != NULL) { + if (count > 1) { + status = HBA_STATUS_ERROR_AMBIGUOUS_WWN; + } else { + *pattr = pp->ap_attr; /* struct copy */ + status = HBA_STATUS_OK; + } + } else { + status = HBA_STATUS_ERROR_ILLEGAL_WWN; + } + return status; +} + diff --git a/adapt_impl.h b/adapt_impl.h new file mode 100644 index 0000000..d86c2f8 --- /dev/null +++ b/adapt_impl.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _ADAPT_IMPL_H_ +#define _ADAPT_IMPL_H_ + +#define SYSFS_HOST_DIR "/sys/class/fc_host" +#define SYSFS_HBA_DIR "/sys/class/net" +#define SYSFS_LUN_DIR "/sys/class/scsi_device" +#define SYSFS_MODULE "/driver/module" +#define SYSFS_MODULE_VER "driver/module/version" +#define SYSFS_RPORT_ROOT "/sys/class/fc_remote_ports" +#define SYSFS_RPORT_DIR "rport-%u:%u-%u" /* host, chan, rport */ + +struct hba_info { + u_int32_t domain; + u_int32_t bus; + u_int32_t dev; + u_int32_t func; + u_int32_t vendor_id; + u_int32_t subsystem_vendor_id; + u_int32_t subsystem_device_id; + u_int32_t device_id; + u_int32_t device_class; + u_int32_t irq; + char Manufacturer[64]; + char SerialNumber[64]; + char Model[256]; + char ModelDescription[256]; + char HardwareVersion[256]; + char OptionROMVersion[256]; + char FirmwareVersion[256]; + u_int32_t VendorSpecificID; + u_int32_t NumberOfPorts; +}; + +#define MAX_DRIVER_NAME_LEN 20 +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) + +HBA_STATUS sysfs_get_port_stats(char *dir, HBA_PORTSTATISTICS *sp); +HBA_STATUS sysfs_get_port_fc4stats(char *dir, HBA_FC4STATISTICS *fc4sp); + +extern struct sa_nameval port_states_table[]; +extern struct sa_nameval port_speeds_table[]; +extern void adapter_scan(void); +extern int sys_read_wwn(const char *, const char *, HBA_WWN *); +extern HBA_STATUS find_pci_device(struct hba_info *); + +/* + * per-adapter interface. + */ + +/* + * Information about a particular adapter. + */ +struct adapter_info { + u_int32_t ad_index; /* adapter's library index */ + u_int32_t ad_kern_index; /* adapter's kernel index */ + const char *ad_name; /* adapter driver name */ + struct sa_table ad_ports; /* table of ports */ + u_int32_t ad_port_count; /* adapter's number of ports */ + HBA_ADAPTERATTRIBUTES ad_attr; /* HBA-API attributes */ +}; + +/* + * Information about a port on an adapter or a discovered remote port. + */ +struct port_info { + struct adapter_info *ap_adapt; + u_int32_t ap_index; + u_int32_t ap_disc_index; /* discovered port index */ + u_int32_t ap_scsi_target; /* SCSI target index (rports) */ + u_int32_t ap_kern_hba; /* kernel HBA index (rports) */ + struct sa_table ap_rports; /* discovered ports */ + HBA_PORTATTRIBUTES ap_attr; /* HBA-API port attributes */ + char host_dir[80]; /* sysfs directory save area */ +}; + +/* + * Internal functions. + */ +HBA_UINT32 adapter_get_count(void); +HBA_STATUS adapter_get_name(HBA_UINT32 index, char *); +struct port_info *adapter_get_port_by_wwn(HBA_HANDLE, HBA_WWN, int *countp); +HBA_STATUS adapter_create(struct adapter_info *); +void adapter_destroy(struct adapter_info *); +void adapter_destroy_all(void); +struct adapter_info *adapter_open_handle(HBA_HANDLE); +struct port_info *adapter_get_port(HBA_HANDLE, HBA_UINT32 port); +struct port_info *adapter_get_rport(HBA_HANDLE, HBA_UINT32, HBA_UINT32); +struct port_info *adapter_get_rport_n(HBA_HANDLE, HBA_UINT32, HBA_UINT32); +struct port_info *adapter_get_rport_target(HBA_HANDLE, HBA_UINT32, HBA_UINT32); +struct port_info *adapter_get_rport_by_wwn(struct port_info *, HBA_WWN); +struct port_info *adapter_get_rport_by_fcid(struct port_info *, fc_fid_t); +void get_rport_info(struct port_info *); +void sg_get_dev_id(const char *name, char *buf, size_t result_len); +void copy_wwn(HBA_WWN *dest, fc_wwn_t src); +int is_wwn_nonzero(HBA_WWN *wwn); +HBA_STATUS sg_issue_read_capacity(const char *, void *, HBA_UINT32 *, + HBA_UINT8 *, void *, HBA_UINT32 *); +HBA_STATUS sg_issue_report_luns(const char *, void *, HBA_UINT32 *, + HBA_UINT8 *, void *, HBA_UINT32 *); + +/* + * Library functions. + */ +HBA_HANDLE adapter_open(char *name); +HBA_STATUS adapter_open_by_wwn(HBA_HANDLE *, HBA_WWN); +void adapter_close(HBA_HANDLE); +HBA_STATUS adapter_get_attr(HBA_HANDLE, HBA_ADAPTERATTRIBUTES *); +HBA_STATUS adapter_get_port_attr(HBA_HANDLE, HBA_UINT32 port, + HBA_PORTATTRIBUTES *); +HBA_STATUS adapter_get_port_attr_by_wwn(HBA_HANDLE, HBA_WWN, + HBA_PORTATTRIBUTES *); +HBA_STATUS adapter_get_rport_attr(HBA_HANDLE, HBA_UINT32 port, + HBA_UINT32 rport, HBA_PORTATTRIBUTES *); +HBA_STATUS get_port_statistics(HBA_HANDLE, HBA_UINT32 port, + HBA_PORTSTATISTICS *); +HBA_STATUS get_port_fc4_statistics(HBA_HANDLE, HBA_WWN, + HBA_UINT8 fc4_type, HBA_FC4STATISTICS *); +HBA_STATUS scsi_read_capacity_v1(HBA_HANDLE, HBA_WWN, HBA_UINT64, + void *, HBA_UINT32, void *, HBA_UINT32); +HBA_STATUS scsi_read_capacity_v2(HBA_HANDLE, HBA_WWN, HBA_WWN, + HBA_UINT64, void *, HBA_UINT32 *, HBA_UINT8 *, + void *, HBA_UINT32 *); +HBA_STATUS scsi_inquiry_v1(HBA_HANDLE, HBA_WWN, HBA_UINT64, HBA_UINT8, + HBA_UINT32, void *, HBA_UINT32, void *, HBA_UINT32); +HBA_STATUS scsi_inquiry_v2(HBA_HANDLE, HBA_WWN, HBA_WWN, HBA_UINT64, + HBA_UINT8, HBA_UINT8, void *, HBA_UINT32 *, + HBA_UINT8 *, void *, HBA_UINT32 *); +HBA_STATUS scsi_report_luns_v1(HBA_HANDLE, HBA_WWN, + void *, HBA_UINT32, void *, HBA_UINT32); +HBA_STATUS scsi_report_luns_v2(HBA_HANDLE, HBA_WWN, HBA_WWN, + void *, HBA_UINT32 *, HBA_UINT8 *, + void *, HBA_UINT32 *); +HBA_STATUS sg_issue_inquiry(const char *, HBA_UINT8, HBA_UINT8, + void *, HBA_UINT32 *, HBA_UINT8 *, void *, HBA_UINT32 *); + +void adapter_init(void); +void adapter_shutdown(void); + +/* struct port_stats; */ + +#endif /* _ADAPT_IMPL_H_ */ diff --git a/api_lib.h b/api_lib.h new file mode 100644 index 0000000..d625a79 --- /dev/null +++ b/api_lib.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _API_LIB_H_ +#define _API_LIB_H_ + +/* + * Definitions used by the OpenFC-specific library for the SNIA HBA-API. + */ +#define HBA_API_VENDOR "Open-FC.org" +#define HBA_API_VERSION "2.2" +#define HBA_API_VENDOR_RURL "org.open-fc" /* reversed URL */ + +#endif /* _API_LIB_H_ */ diff --git a/bind.c b/bind.c new file mode 100644 index 0000000..25b3ceb --- /dev/null +++ b/bind.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "utils.h" +#include "api_lib.h" +#include "adapt_impl.h" +#include "bind_impl.h" + +/* + * Binding capabilities we understand. + */ +#define BINDING_CAPABILITIES (HBA_CAN_BIND_TO_D_ID | \ + HBA_CAN_BIND_TO_WWPN | \ + HBA_CAN_BIND_TO_WWNN) +#define SYSFS_BIND "tgtid_bind_type" + +/* + * Name-value strings for kernel bindings. + * The first word of the strings must exactly match those in + * Linux's drivers/scsi/scsi_transport_fc + */ +static struct sa_nameval binding_types_table[] = { + { "none", 0 }, + { "wwpn (World Wide Port Name)", HBA_CAN_BIND_TO_WWPN }, + { "wwnn (World Wide Node Name)", HBA_CAN_BIND_TO_WWNN }, + { "port_id (FC Address)", HBA_CAN_BIND_TO_D_ID }, + { NULL, 0 } +}; + +/* + * Context for LUN binding reader. + */ +struct binding_context { + HBA_HANDLE oc_handle; + int oc_kern_hba; /* kernel HBA number */ + int oc_port; + int oc_target; + int oc_lun; + u_int32_t oc_count; + u_int32_t oc_limit; + u_int32_t oc_ver; + void *oc_entries; + HBA_STATUS oc_status; + char oc_sg[32]; /* SCSI-generic dev name */ + HBA_SCSIID *oc_scp; /* place for OS device name */ + struct port_info *oc_rport; /* target remote port, if known */ + char oc_path[256]; /* parent dir save area */ +}; + +/* + * Get binding capability. + * We currently don't have a way to get this from the driver. + * Instead, we hardcode what we know about Linux's capabilities. + * We don't care which HBA is specified, except to return the correct error. + */ +HBA_STATUS +get_binding_capability(HBA_HANDLE handle, HBA_WWN wwn, HBA_BIND_CAPABILITY *cp) +{ + struct port_info *pp; + int count = 0; + + pp = adapter_get_port_by_wwn(handle, wwn, &count); + if (count > 1) + return HBA_STATUS_ERROR_AMBIGUOUS_WWN; + else if (pp == NULL) + return HBA_STATUS_ERROR_ILLEGAL_WWN; + *cp = BINDING_CAPABILITIES; + return HBA_STATUS_OK; +} + +/* + * Get binding support. + */ +HBA_STATUS +get_binding_support(HBA_HANDLE handle, HBA_WWN wwn, HBA_BIND_CAPABILITY *cp) +{ + struct port_info *pp; + char dir[50]; + char bind[50]; + int count = 0; + + pp = adapter_get_port_by_wwn(handle, wwn, &count); + if (count > 1) + return HBA_STATUS_ERROR_AMBIGUOUS_WWN; + else if (pp == NULL) + return HBA_STATUS_ERROR_ILLEGAL_WWN; + snprintf(dir, sizeof(dir), SYSFS_HOST_DIR "/host%u", pp->ap_kern_hba); + if (sa_sys_read_line(dir, SYSFS_BIND, bind, sizeof(bind)) == 0) + sa_enum_encode(binding_types_table, bind, cp); + return HBA_STATUS_OK; +} + +/* + * Set binding support. + */ +HBA_STATUS +set_binding_support(HBA_HANDLE handle, HBA_WWN wwn, HBA_BIND_CAPABILITY flags) +{ + struct port_info *pp; + int count = 0; + char dir[50]; + char buf[50]; + const char *bind; + + pp = adapter_get_port_by_wwn(handle, wwn, &count); + if (count > 1) + return HBA_STATUS_ERROR_AMBIGUOUS_WWN; + if (pp == NULL) + return HBA_STATUS_ERROR_ILLEGAL_WWN; + if ((flags & BINDING_CAPABILITIES) != flags) + return HBA_STATUS_ERROR_NOT_SUPPORTED; + bind = sa_enum_decode(buf, sizeof(buf), binding_types_table, flags); + snprintf(dir, sizeof(dir), SYSFS_HOST_DIR "/host%u", pp->ap_kern_hba); + if (strstr(bind, "Unknown") != NULL) + return HBA_STATUS_ERROR_NOT_SUPPORTED; + if (sa_sys_write_line(dir, SYSFS_BIND, bind) == 0) + return HBA_STATUS_ERROR_INCAPABLE; + return HBA_STATUS_OK; +} + +static int +get_deprecated_device_name(struct dirent *dp, void *arg) +{ + struct binding_context *cp = arg; + + if (strstr(cp->oc_path, "block")) + snprintf(cp->oc_scp->OSDeviceName, + sizeof(cp->oc_scp->OSDeviceName), + "/dev/%s", dp->d_name); + if (strstr(cp->oc_path, "scsi_generic")) + snprintf(cp->oc_sg, sizeof(cp->oc_sg), + "/dev/%s", dp->d_name); + return 0; +} + +static int +get_binding_os_names(struct dirent *dp, void *arg) +{ + struct binding_context *cp = arg; + char *name = dp->d_name; + char *sep; + char buf[sizeof(cp->oc_scp->OSDeviceName)]; + + sep = strchr(name, ':'); + if (dp->d_type == DT_LNK && sep != NULL) { + *sep = '\0'; /* replace colon */ + if (strcmp(name, "block") == 0) { + snprintf(cp->oc_scp->OSDeviceName, + sizeof(cp->oc_scp->OSDeviceName), + "/dev/%s", sep + 1); + } else if (strcmp(name, "scsi_generic") == 0) { + snprintf(cp->oc_sg, + sizeof(cp->oc_sg), + "/dev/%s", sep + 1); + } + *sep = ':'; /* not really needed */ + } else if (dp->d_type == DT_DIR && sep == NULL) { + if ((!strcmp(name, "block")) || + (!strcmp(name, "scsi_generic"))) { + /* save the original path */ + sa_strncpy_safe(buf, sizeof(buf), + cp->oc_path, sizeof(cp->oc_path)); + + snprintf(cp->oc_path, sizeof(cp->oc_path), + "%s/%s", buf, name); + sa_dir_read(cp->oc_path, + get_deprecated_device_name, cp); + + /* restore the original path */ + sa_strncpy_safe(cp->oc_path, sizeof(cp->oc_path), + buf, sizeof(buf)); + } + } + return 0; +} + +static int +get_binding_target_mapping(struct dirent *dp, void *ctxt_arg) +{ + struct binding_context *cp = ctxt_arg; + struct port_info *pp; + HBA_FCPSCSIENTRY *sp; + HBA_FCPSCSIENTRYV2 *s2p; + HBA_SCSIID *scp = NULL; + HBA_FCPID *fcp = NULL; + HBA_LUID *luid = NULL; + char name[50]; + u_int32_t hba = -1; + u_int32_t port = -1; + u_int32_t tgt = -1; + u_int32_t lun = -1; + + /* + * Parse directory entry name to see if it matches + * :::. + */ + if (sscanf(dp->d_name, "%u:%u:%u:%u", &hba, &port, &tgt, &lun) != 4) + return 0; + + if (hba != cp->oc_kern_hba || + (port != cp->oc_port && cp->oc_port != -1) || + (tgt != cp->oc_target && cp->oc_target != -1) || + (lun != cp->oc_lun && cp->oc_lun != -1)) { + return 0; + } + + /* + * Name matches. Add to count and to mapping list if there's room. + */ + if (cp->oc_count < cp->oc_limit) { + + switch (cp->oc_ver) { + case 1: + sp = &((HBA_FCPSCSIENTRY *) + cp->oc_entries)[cp->oc_count]; + scp = &sp->ScsiId; + fcp = &sp->FcpId; + luid = NULL; + break; + case 2: + s2p = &((HBA_FCPSCSIENTRYV2 *) + cp->oc_entries)[cp->oc_count]; + scp = &s2p->ScsiId; + fcp = &s2p->FcpId; + luid = &s2p->LUID; + break; + default: + fprintf(stderr, "*** Fatal! ***\n"); + break; + } + pp = cp->oc_rport; + if (pp == NULL) + pp = adapter_get_rport_target(cp->oc_handle, + port, tgt); + if (pp != NULL) { + fcp->FcId = pp->ap_attr.PortFcId; + fcp->NodeWWN = pp->ap_attr.NodeWWN; + fcp->PortWWN = pp->ap_attr.PortWWN; + fcp->FcpLun = (HBA_UINT64) lun; + } + + /* + * Find OS device name by searching for symlink block: + * and SG name by searching for scsi_generic: + * in device subdirectory. + */ + snprintf(name, sizeof(name), + SYSFS_LUN_DIR "/%s/device", dp->d_name); + cp->oc_sg[0] = '\0'; + cp->oc_scp = scp; + scp->OSDeviceName[0] = '\0'; + /* save a copy of the dir name */ + sa_strncpy_safe(cp->oc_path, sizeof(cp->oc_path), + name, sizeof(name)); + sa_dir_read(name, get_binding_os_names, cp); + scp->ScsiBusNumber = hba; + scp->ScsiTargetNumber = tgt; + scp->ScsiOSLun = lun; + + /* + * find the LUN ID information by using scsi_generic I/O. + */ + if (luid != NULL && cp->oc_sg[0] != '\0') + sg_get_dev_id(cp->oc_sg, luid->buffer, + sizeof(luid->buffer)); + } + cp->oc_count++; + return 0; +} + +/* + * Get FCP target mapping. + */ +HBA_STATUS +get_binding_target_mapping_v1(HBA_HANDLE handle, HBA_FCPTARGETMAPPING *map) +{ + struct binding_context ctxt; + struct adapter_info *ap; + + ap = adapter_open_handle(handle); + if (ap == NULL) + return HBA_STATUS_ERROR_INVALID_HANDLE; + memset(&ctxt, 0, sizeof(ctxt)); + ctxt.oc_handle = handle; + ctxt.oc_kern_hba = ap->ad_kern_index; + ctxt.oc_port = -1; + ctxt.oc_target = -1; + ctxt.oc_lun = -1; + ctxt.oc_limit = map->NumberOfEntries; + ctxt.oc_ver = 1; + ctxt.oc_entries = map->entry; + ctxt.oc_status = HBA_STATUS_OK; + memset(map->entry, 0, sizeof(map->entry[0]) * ctxt.oc_limit); + sa_dir_read(SYSFS_LUN_DIR, get_binding_target_mapping, &ctxt); + map->NumberOfEntries = ctxt.oc_count; + if (ctxt.oc_status == HBA_STATUS_OK && ctxt.oc_count > ctxt.oc_limit) + ctxt.oc_status = HBA_STATUS_ERROR_MORE_DATA; + return ctxt.oc_status; +} + +/* + * Get FCP target mapping. + */ +HBA_STATUS +get_binding_target_mapping_v2(HBA_HANDLE handle, HBA_WWN wwn, + HBA_FCPTARGETMAPPINGV2 *map) +{ + struct binding_context ctxt; + struct adapter_info *ap; + struct port_info *pp; + + pp = adapter_get_port_by_wwn(handle, wwn, NULL); + if (pp == NULL) + return HBA_STATUS_ERROR_INVALID_HANDLE; + ap = pp->ap_adapt; + if (ap == NULL) + return HBA_STATUS_ERROR_INVALID_HANDLE; + memset(&ctxt, 0, sizeof(ctxt)); + ctxt.oc_handle = handle; + ctxt.oc_kern_hba = ap->ad_kern_index; + ctxt.oc_port = pp->ap_index; + ctxt.oc_target = -1; + ctxt.oc_lun = -1; + ctxt.oc_limit = map->NumberOfEntries; + ctxt.oc_ver = 2; + ctxt.oc_entries = map->entry; + ctxt.oc_status = HBA_STATUS_OK; + memset(map->entry, 0, sizeof(map->entry[0]) * ctxt.oc_limit); + sa_dir_read(SYSFS_LUN_DIR, get_binding_target_mapping, &ctxt); + map->NumberOfEntries = ctxt.oc_count; + if (ctxt.oc_status == HBA_STATUS_OK && ctxt.oc_count > ctxt.oc_limit) + ctxt.oc_status = HBA_STATUS_ERROR_MORE_DATA; + return ctxt.oc_status; +} + +/* + * Get LUN scsi-generic device name. + */ +int +get_binding_sg_name(struct port_info *lp, HBA_WWN disc_wwpn, + HBA_UINT64 fc_lun, char *buf, size_t len) +{ + struct binding_context ctxt; + struct port_info *rp; + HBA_FCPSCSIENTRYV2 entry; + + /* + * find discovered (remote) port. + */ + rp = adapter_get_rport_by_wwn(lp, disc_wwpn); + if (rp == NULL) + return HBA_STATUS_ERROR_ILLEGAL_WWN; + + memset(&ctxt, 0, sizeof(ctxt)); + memset(&entry, 0, sizeof(entry)); + ctxt.oc_rport = rp; + ctxt.oc_kern_hba = rp->ap_kern_hba; + ctxt.oc_port = rp->ap_index; + ctxt.oc_target = rp->ap_scsi_target; + if (ctxt.oc_target == -1) + return ENOENT; + ctxt.oc_lun = (int) fc_lun; + ctxt.oc_limit = 1; + ctxt.oc_ver = 1; + ctxt.oc_entries = &entry; + sa_dir_read(SYSFS_LUN_DIR, get_binding_target_mapping, &ctxt); + if (ctxt.oc_count != 1) + return ENOENT; + sa_strncpy_safe(buf, len, ctxt.oc_sg, sizeof(ctxt.oc_sg)); + return 0; +} diff --git a/bind_impl.h b/bind_impl.h new file mode 100644 index 0000000..7bcff02 --- /dev/null +++ b/bind_impl.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _BIND_IMPL_H_ +#define _BIND_IMPL_H_ + +HBA_STATUS get_binding_capability(HBA_HANDLE, HBA_WWN, HBA_BIND_CAPABILITY *); +HBA_STATUS get_binding_support(HBA_HANDLE, HBA_WWN, HBA_BIND_CAPABILITY *); +HBA_STATUS set_binding_support(HBA_HANDLE, HBA_WWN, HBA_BIND_CAPABILITY); +HBA_STATUS get_binding_target_mapping_v1(HBA_HANDLE, HBA_FCPTARGETMAPPING *); +HBA_STATUS get_binding_target_mapping_v2(HBA_HANDLE, HBA_WWN, + HBA_FCPTARGETMAPPINGV2 *); +int get_binding_sg_name(struct port_info *, + HBA_WWN, HBA_UINT64, char *, size_t); + +#endif /* _BIND_IMPL_H_ */ diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..361d5c5 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,2 @@ +#! /bin/sh +exec autoreconf --install -s diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..95950d9 --- /dev/null +++ b/build.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# This script parses the package name and version out of configure.ac, +# uses them to assemble the names of the distribution tar.gz and source rpm, +# then calls make to generate those files. +# Any arguments will be passed on to configure when building the tarball. + +AC_INIT=`grep AC_INIT configure.ac` +PACKAGE=`echo ${AC_INIT} | awk 'BEGIN { FS="[][]" }; { print $2 };'` +VERSION=`echo ${AC_INIT} | awk 'BEGIN { FS="[][]" }; { print $4 };'` + +tgz=${PACKAGE}-${VERSION}.tar.gz +srpm=${PACKAGE}-${VERSION}-1.src.rpm + +rm -f ${tgz} ${srpm} + +make -f - <build_date) == NULL) + memset(&ap->build_date, 0, sizeof(ap->build_date)); + strcpy(ap->VName, HBA_API_VENDOR); + strcpy(ap->VVersion, HBA_API_VERSION); + return HBA_STATUS_OK; +} +#endif + +/* + * initialize the library after load. + */ +static HBA_STATUS load_library(void) +{ + adapter_init(); + return HBA_STATUS_OK; +} + +static HBA_STATUS free_library(void) +{ + adapter_shutdown(); + adapter_destroy_all(); + return HBA_STATUS_OK; +} + +static HBA_ENTRYPOINTSV2 vendor_lib_entrypoints = { + .GetVersionHandler = get_library_version, + .LoadLibraryHandler = load_library, + .FreeLibraryHandler = free_library, + .GetNumberOfAdaptersHandler = adapter_get_count, + .GetAdapterNameHandler = adapter_get_name, + .OpenAdapterHandler = adapter_open, + .CloseAdapterHandler = adapter_close, + .GetAdapterAttributesHandler = adapter_get_attr, + .GetAdapterPortAttributesHandler = adapter_get_port_attr, + .GetPortStatisticsHandler = get_port_statistics, + .GetDiscoveredPortAttributesHandler = adapter_get_rport_attr, + + .GetPortAttributesByWWNHandler = NULL, + /* adapter_get_port_attr_by_wwn, */ + /* Next function deprecated but still supported */ + .SendCTPassThruHandler = NULL, + .RefreshInformationHandler = NULL, + .ResetStatisticsHandler = NULL, + /* Next function deprecated but still supported */ + .GetFcpTargetMappingHandler = get_binding_target_mapping_v1, + /* Next function depricated but still supported */ + .GetFcpPersistentBindingHandler = NULL, + .GetEventBufferHandler = NULL, + .SetRNIDMgmtInfoHandler = NULL, + .GetRNIDMgmtInfoHandler = NULL, + /* Next function deprecated but still supported */ + .SendRNIDHandler = NULL, + .ScsiInquiryHandler = scsi_inquiry_v1, + .ReportLUNsHandler = scsi_report_luns_v1, + .ReadCapacityHandler = scsi_read_capacity_v1, + + /* V2 handlers */ + .OpenAdapterByWWNHandler = NULL, + /* adapter_open_by_wwn, */ + .GetFcpTargetMappingV2Handler = get_binding_target_mapping_v2, + .SendCTPassThruV2Handler = NULL, + .RefreshAdapterConfigurationHandler = NULL, + .GetBindingCapabilityHandler = NULL, + /* get_binding_capability, */ + .GetBindingSupportHandler = NULL, + /* get_binding_support, */ + .SetBindingSupportHandler = NULL, + /* set_binding_support, */ + .SetPersistentBindingV2Handler = NULL, + .GetPersistentBindingV2Handler = NULL, + .RemovePersistentBindingHandler = NULL, + .RemoveAllPersistentBindingsHandler = NULL, + .SendRNIDV2Handler = NULL, + .ScsiInquiryV2Handler = scsi_inquiry_v2, + .ScsiReportLUNsV2Handler = scsi_report_luns_v2, + .ScsiReadCapacityV2Handler = scsi_read_capacity_v2, + .GetVendorLibraryAttributesHandler = NULL, + /* get_vendor_lib_attrs, */ + .RemoveCallbackHandler = NULL, + .RegisterForAdapterAddEventsHandler = NULL, + .RegisterForAdapterEventsHandler = NULL, + .RegisterForAdapterPortEventsHandler = NULL, + .RegisterForAdapterPortStatEventsHandler = NULL, + .RegisterForTargetEventsHandler = NULL, + .RegisterForLinkEventsHandler = NULL, + .SendRPLHandler = NULL, + .SendRPSHandler = NULL, + .SendSRLHandler = NULL, + .SendLIRRHandler = NULL, + .GetFC4StatisticsHandler = get_port_fc4_statistics, + .GetFCPStatisticsHandler = NULL, + .SendRLSHandler = NULL, +}; + +/** + * Function called by a version 1 common HBAAPI library to get our entry points. + * + * @arg ep pointer to entrypoints structure where we store our + * function pointers. + * @returns HBA_STATUS. + */ +HBA_STATUS HBA_RegisterLibrary(HBA_ENTRYPOINTS *ep) +{ + memcpy(ep, &vendor_lib_entrypoints, sizeof(HBA_ENTRYPOINTS)); + return HBA_STATUS_OK; +} + +/** + * Function called by the common HBAAPI library to get our entry points. + * + * @arg ep pointer to entrypoints structure where we store our + * function pointers. + * @returns HBA_STATUS. + */ +HBA_STATUS HBA_RegisterLibraryV2(HBA_ENTRYPOINTSV2 *ep) +{ + *ep = vendor_lib_entrypoints; /* structure copy */ + return HBA_STATUS_OK; +} + diff --git a/libhbalinux.pc.in b/libhbalinux.pc.in new file mode 100644 index 0000000..b716efe --- /dev/null +++ b/libhbalinux.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libhbalinux +Description: SNIA HBA API vendor library for Linux +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -ldl diff --git a/libhbalinux.spec.in b/libhbalinux.spec.in new file mode 100644 index 0000000..344c166 --- /dev/null +++ b/libhbalinux.spec.in @@ -0,0 +1,53 @@ +Name: libhbalinux +Version: @PACKAGE_VERSION@ +Release: 1%{?dist} +Summary: FC-HBAAPI implementation using scsi_transport_fc interfaces + +Group: System Environment/Libraries +License: LGPLv2 +URL: http://www.open-fcoe.org +Source0: http://www.open-fcoe.org/openfc/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +BuildRequires: libHBAAPI-devel libpciaccess-devel +Requires: libHBAAPI + +%description +SNIA HBAAPI vendor library built on top of the scsi_transport_fc interfaces + +%prep +%setup -q + + +%build +%configure --disable-static +make %{?_smp_mflags} + + +%install +rm -rf $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT +find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' +find $RPM_BUILD_ROOT -name '*.so' -exec rm -f {} ';' + + +%clean +rm -rf $RPM_BUILD_ROOT + + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + + +%files +%defattr(-,root,root,-) +%doc README +%doc COPYING +%{_libdir}/*.so.* + + +%changelog +* Mon Mar 2 2009 Chris Leech - 1.0.7-1 +- initial build + diff --git a/lport.c b/lport.c new file mode 100644 index 0000000..bc34078 --- /dev/null +++ b/lport.c @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "utils.h" +#include "api_lib.h" +#include "adapt_impl.h" + +#ifndef HBA_STATUS_ERROR_ILLEGAL_FCID +#define HBA_STATUS_ERROR_ILLEGAL_FCID 33 /* defined after HBA-API 2.2 */ +#endif +#define SEND_CT_TIMEOUT (3 * 1000) /* timeout in milliseconds */ + +/* + * The following are temporary settings until we can find a way to + * collect these information. + */ +#define HBA_ROM_VERSION "" +#define HBA_FW_VERSION "" +#define HBA_VENDOR_SPECIFIC_ID 0 + +/* + * table of /sys port types strings to HBA-API values. + */ +struct sa_nameval port_types_table[] = { + { "Unknown", HBA_PORTTYPE_UNKNOWN }, + { "Other", HBA_PORTTYPE_OTHER }, + { "Not Present", HBA_PORTTYPE_NOTPRESENT }, + { "NPort (fabric via point-to-point)", HBA_PORTTYPE_NPORT }, + { "NLPort (fabric via loop)", HBA_PORTTYPE_NLPORT }, + { "LPort (private loop)", HBA_PORTTYPE_LPORT }, + { "Point-To-Point (direct nport connection)", HBA_PORTTYPE_PTP }, + { "NPIV VPORT", HBA_PORTTYPE_NPORT }, + { NULL, 0 } +}; + +/* + * table of /sys port state strings to HBA-API values. + */ +struct sa_nameval port_states_table[] = { + { "Not Present", HBA_PORTSTATE_UNKNOWN }, + { "Online", HBA_PORTSTATE_ONLINE }, + { "Offline", HBA_PORTSTATE_OFFLINE }, + { "Blocked", HBA_PORTSTATE_UNKNOWN }, + { "Bypassed", HBA_PORTSTATE_BYPASSED }, + { "Diagnostics", HBA_PORTSTATE_DIAGNOSTICS }, + { "Linkdown", HBA_PORTSTATE_LINKDOWN }, + { "Error", HBA_PORTSTATE_ERROR }, + { "Loopback", HBA_PORTSTATE_LOOPBACK }, + { "Deleted", HBA_PORTSTATE_UNKNOWN }, + { NULL, 0 } +}; + +/* + * table of /sys port speed strings to HBA-API values. + */ +struct sa_nameval port_speeds_table[] = { + { "Unknown", HBA_PORTSPEED_UNKNOWN }, + { "1 Gbit", HBA_PORTSPEED_1GBIT }, + { "2 Gbit", HBA_PORTSPEED_2GBIT }, + { "4 Gbit", HBA_PORTSPEED_4GBIT }, + { "10 Gbit", HBA_PORTSPEED_10GBIT }, + { "8 Gbit", HBA_PORTSPEED_8GBIT }, + { "16 Gbit", HBA_PORTSPEED_16GBIT }, + { "32 Gbit", HBA_PORTSPEED_32GBIT }, + { "20 Gbit", HBA_PORTSPEED_20GBIT }, + { "40 Gbit", HBA_PORTSPEED_40GBIT }, + { "Not Negotiated", HBA_PORTSPEED_NOT_NEGOTIATED }, + { NULL, 0 } +}; + +/* + * parse strings from /sys port speed/support_speeds files + * and convert them to bitmasks for the HBA_PORTSPEED supported + * Format expected: "1 Gbit[, 10 Gbit]", etc. + */ +static int sys_read_speed(const char *dir, const char *file, char *buf, + size_t buflen, HBA_PORTSPEED *speeds) +{ + int rc = 0; + u_int32_t val = 0; + int len = 0; + char *cp; + struct sa_nameval *tp = port_speeds_table; + + rc = sa_sys_read_line(dir, file, buf, buflen); + if (rc == 0 && strstr(buf, "Unknown") == NULL) { + for (cp = buf; *cp != '\0';) { + for (; tp->nv_name != NULL; tp++) { + len = strlen(tp->nv_name); + if (strncasecmp(tp->nv_name, cp, len) == 0) { + val |= tp->nv_val; + cp += len; + break; + } + } + if (*cp == '\0') + break; + if (*cp == ',') { + cp++; + if (*cp == ' ') + cp++; + } + else + break; /* invalid string */ + } + } + + *speeds = val; + + return rc; +} + +/* + * Code for OpenFC-supported adapters. + */ + +static int +counting_rports(struct dirent *dp, void *arg) +{ + int *count = (int *)arg; + + if (!strstr(dp->d_name, "rport-")) + return HBA_STATUS_OK; + (*count)++; + return HBA_STATUS_OK; +} + +static int +check_ifindex(struct dirent *dp, void *arg) +{ + char *ifindex = (char *)arg; + char hba_dir[256]; + char buf[256]; + int rc; + + snprintf(hba_dir, sizeof(hba_dir), + SYSFS_HBA_DIR "/%s", dp->d_name); + memset(buf, 0, sizeof(buf)); + rc = sa_sys_read_line(hba_dir, "ifindex", buf, sizeof(buf) - 1); + if (rc) + return 0; + if (!strncmp(ifindex, buf, sizeof(buf))) { + strcpy(arg, dp->d_name); + return 1; + } + return 0; +} + +/* + * find_phys_if - find the regular network interface name that + * has the ifindex that matches the specified iflink. + * This ifname will be used to find the PCI info + * of a VLAN interface. + * hba_dir: hba_dir of VLAN interface. + * buf: returns ifname of regular network interface. + */ +static int +find_phys_if(char *hba_dir, char *buf, size_t len) +{ + int rc; + + rc = sa_sys_read_line(hba_dir, "iflink", buf, len); + if (rc) + return 1; + /* + * Search for the regular network interface and + * return the interface name in the buf. + */ + sa_dir_read(SYSFS_HBA_DIR, check_ifindex, buf); + return 0; +} + +static int +sysfs_scan(struct dirent *dp, void *arg) +{ + HBA_ADAPTERATTRIBUTES *atp; + HBA_PORTATTRIBUTES *pap; + HBA_WWN wwnn; + struct hba_info hba_info; + struct adapter_info *ap; + struct port_info *pp; + char host_dir[80], hba_dir[80], drv_dir[80]; + char dev_dir[128]; + char ifname[20], buf[256]; + char *driverName; + int data[32], rc, i; + char *cp; + char *saveptr; /* for strtok_r */ + unsigned int ifindex; + unsigned int iflink; + + memset(&hba_info, 0, sizeof(hba_info)); + + /* + * Create a new HBA entry (ap) for the local port + * We will create a new HBA entry for each local port. + */ + ap = malloc(sizeof(*ap)); + if (!ap) { + fprintf(stderr, "%s: malloc failed, errno=0x%x\n", + __func__, errno); + return HBA_STATUS_ERROR; + } + memset(ap, 0, sizeof(*ap)); + ap->ad_kern_index = atoi(dp->d_name + sizeof("host") - 1); + ap->ad_port_count = 1; + + /* atp points to the HBA attributes structure */ + atp = &ap->ad_attr; + + /* + * Create a new local port entry + */ + pp = malloc(sizeof(*pp)); + if (pp == NULL) { + fprintf(stderr, + "%s: malloc for local port %d failed," + " errno=0x%x\n", __func__, + ap->ad_port_count - 1, errno); + free(ap); + return 0; + } + + memset(pp, 0, sizeof(*pp)); + pp->ap_adapt = ap; + pp->ap_index = ap->ad_port_count - 1; + pp->ap_kern_hba = atoi(dp->d_name + sizeof("host") - 1); + + /* pap points to the local port attributes structure */ + pap = &pp->ap_attr; + + /* Construct the host directory name from the input name */ + snprintf(host_dir, sizeof(host_dir), + SYSFS_HOST_DIR "/%s", dp->d_name); + + rc = sa_sys_read_line(host_dir, "symbolic_name", buf, sizeof(buf)); + + /* Get PortSymbolicName */ + sa_strncpy_safe(pap->PortSymbolicName, sizeof(pap->PortSymbolicName), + buf, sizeof(buf)); + + /* Skip the HBA if it isn't OpenFC */ + cp = strstr(pap->PortSymbolicName, " over "); + if (!cp) + goto skip; + + /* + * See if /device is a PCI symlink. + * If not, try it as a net device. + */ + snprintf(dev_dir, sizeof(dev_dir), "%s/device", host_dir); + i = readlink(dev_dir, buf, sizeof(buf) - 1); + if (i < 0) + i = 0; + buf[i] = '\0'; + + if (strstr(buf, "devices/pci") && !strstr(buf, "/net/")) { + snprintf(hba_dir, sizeof(hba_dir), "%s/device/..", host_dir); + } else { + /* assume a net device */ + cp += 6; + sa_strncpy_safe(ifname, sizeof(ifname), cp, strlen(cp)); + snprintf(hba_dir, sizeof(hba_dir), + SYSFS_HBA_DIR "/%s", ifname); + /* + * Try as VLAN device or other virtual net device. + * If this is the case, ifindex and iflink will be different. + * iflink is the ifindex of the physical device. + */ + rc = sa_sys_read_u32(hba_dir, "ifindex", &ifindex); + if (rc < 0) + goto skip; + rc = sa_sys_read_u32(hba_dir, "iflink", &iflink); + if (rc < 0) + goto skip; + if (ifindex != iflink) { + rc = find_phys_if(hba_dir, buf, sizeof(buf)); + if (rc) + goto skip; + strncpy(ifname, buf, sizeof(ifname)); + } + + snprintf(hba_dir, sizeof(hba_dir), + SYSFS_HBA_DIR "/%s/device", ifname); + i = readlink(hba_dir, buf, sizeof(buf) - 1); + if (i < 0) + goto skip; + buf[i] = '\0'; + } + + /* + * Assume a PCI symlink value is in buf. + * Back up to the last path component that looks like a PCI element. + * A sample link value is like: + * ../../devices/pci*.../0000:03:00.0 + */ + rc = 0; + do { + cp = strrchr(buf, '/'); + if (!cp) + break; + rc = sscanf(cp + 1, "%x:%x:%x.%x", + &hba_info.domain, &hba_info.bus, + &hba_info.dev, &hba_info.func); + if (rc == 4) + break; + *cp = '\0'; + } while (cp && cp > buf); + + if (rc != 4) + goto skip; + + /* + * Save the host directory and the hba directory + * in local port structure + */ + sa_strncpy_safe(pp->host_dir, sizeof(pp->host_dir), + host_dir, sizeof(host_dir)); + + /* Get NodeWWN */ + rc = sys_read_wwn(pp->host_dir, "node_name", &wwnn); + memcpy(&pap->NodeWWN, &wwnn, sizeof(wwnn)); + + /* Get PortWWN */ + rc = sys_read_wwn(pp->host_dir, "port_name", &pap->PortWWN); + + /* Get PortFcId */ + rc = sa_sys_read_u32(pp->host_dir, "port_id", &pap->PortFcId); + + /* Get PortType */ + rc = sa_sys_read_line(pp->host_dir, "port_type", buf, sizeof(buf)); + rc = sa_enum_encode(port_types_table, buf, &pap->PortType); + + /* Get PortState */ + rc = sa_sys_read_line(pp->host_dir, "port_state", buf, sizeof(buf)); + rc = sa_enum_encode(port_states_table, buf, &pap->PortState); + + /* Get PortSpeed */ + rc = sys_read_speed(pp->host_dir, "speed", + buf, sizeof(buf), + &pap->PortSpeed); + + /* Get PortSupportedSpeed */ + rc = sys_read_speed(pp->host_dir, "supported_speeds", + buf, sizeof(buf), + &pap->PortSupportedSpeed); + + /* Get PortMaxFrameSize */ + rc = sa_sys_read_line(pp->host_dir, "maxframe_size", buf, sizeof(buf)); + sscanf(buf, "%d", &pap->PortMaxFrameSize); + + /* Get PortSupportedFc4Types */ + rc = sa_sys_read_line(pp->host_dir, "supported_fc4s", buf, sizeof(buf)); + sscanf(buf, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], + &data[6], &data[7], &data[8], &data[9], &data[10], &data[11], + &data[12], &data[13], &data[14], &data[15], &data[16], + &data[17], &data[18], &data[19], &data[20], &data[21], + &data[22], &data[23], &data[24], &data[25], &data[26], + &data[27], &data[28], &data[29], &data[30], &data[31]); + for (i = 0; i < 32; i++) + pap->PortSupportedFc4Types.bits[i] = data[i]; + + /* Get PortActiveFc4Types */ + rc = sa_sys_read_line(pp->host_dir, "active_fc4s", buf, sizeof(buf)); + sscanf(buf, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], + &data[6], &data[7], &data[8], &data[9], &data[10], &data[11], + &data[12], &data[13], &data[14], &data[15], &data[16], + &data[17], &data[18], &data[19], &data[20], &data[21], + &data[22], &data[23], &data[24], &data[25], &data[26], + &data[27], &data[28], &data[29], &data[30], &data[31]); + for (i = 0; i < 32; i++) + pap->PortActiveFc4Types.bits[i] = data[i]; + + /* Get FabricName */ + rc = sys_read_wwn(pp->host_dir, "fabric_name", &pap->FabricName); + + /* Get PortSupportedClassofService */ + rc = sa_sys_read_line(pp->host_dir, "supported_classes", + buf, sizeof(buf)); + + cp = strstr(buf, "Class"); + if (cp) + pap->PortSupportedClassofService = *(cp + 6) - '0'; + + /* Get OSDeviceName */ + sa_strncpy_safe(pap->OSDeviceName, sizeof(pap->OSDeviceName), + dp->d_name, sizeof(dp->d_name)); + + /* Get NumberofDiscoveredPorts */ + snprintf(buf, sizeof(buf), "%s/device", pp->host_dir); + sa_dir_read(buf, counting_rports, &pap->NumberofDiscoveredPorts); + + /* + * Add the local port structure into local port table within + * the HBA structure. + */ + if (sa_table_insert(&ap->ad_ports, pp->ap_index, pp) < 0) { + fprintf(stderr, + "%s: insert of HBA %d port %d failed\n", + __func__, ap->ad_kern_index, pp->ap_index); + goto skip; + } + + /* Create adapter name */ + snprintf(buf, sizeof(buf), "fcoe:%s", ifname); + ap->ad_name = strdup(buf); + + /* Get vendor_id */ + rc = sa_sys_read_u32(hba_dir, "vendor", &hba_info.vendor_id); + + /* Get device_id */ + rc = sa_sys_read_u32(hba_dir, "device", &hba_info.device_id); + + /* Get subsystem_vendor_id */ + rc = sa_sys_read_u32(hba_dir, "subsystem_vendor", + &hba_info.subsystem_vendor_id); + + /* Get subsystem_device_id */ + rc = sa_sys_read_u32(hba_dir, "subsystem_device", + &hba_info.subsystem_device_id); + + /* Get device_class */ + rc = sa_sys_read_u32(hba_dir, "class", &hba_info.device_class); + hba_info.device_class = hba_info.device_class>>8; + + /* + * Get Hardware Information via PCI Library + */ + (void) find_pci_device(&hba_info); + + /* Get Number of Ports */ + atp->NumberOfPorts = hba_info.NumberOfPorts; + + /* Get Manufacturer */ + sa_strncpy_safe(atp->Manufacturer, sizeof(atp->Manufacturer), + hba_info.Manufacturer, sizeof(hba_info.Manufacturer)); + + /* Get SerialNumber */ + sa_strncpy_safe(atp->SerialNumber, sizeof(atp->SerialNumber), + hba_info.SerialNumber, sizeof(hba_info.SerialNumber)); + + + /* Get ModelDescription */ + sa_strncpy_safe(atp->ModelDescription, sizeof(atp->ModelDescription), + hba_info.ModelDescription, + sizeof(hba_info.ModelDescription)); + if (!strncmp(hba_info.ModelDescription, "Unknown", + sizeof(hba_info.ModelDescription))) { + snprintf(atp->ModelDescription, sizeof(atp->ModelDescription), + "[%04x:%04x]-[%04x:%04x]-(%04x)", + hba_info.vendor_id, hba_info.device_id, + hba_info.subsystem_vendor_id, + hba_info.subsystem_device_id, + hba_info.device_class); + /* + * Get Model + * + * If the device is a newly developed product, and + * the model description is not in pci.ids yet, use + * the model description constructed above as the + * model string. + */ + sa_strncpy_safe(atp->Model, sizeof(atp->Model), + atp->ModelDescription, + sizeof(atp->ModelDescription)); + } + + /* + * Get Model + * + * If the device name has already been added into + * the pci.ids file, use the first word of the model + * description as the model. If the space after the + * first word is not found (new product), use the + * model description as the model. + */ + sa_strncpy_safe(buf, sizeof(buf), atp->ModelDescription, + sizeof(atp->ModelDescription)); + if (strtok_r(buf, " ", &saveptr)) + sa_strncpy_safe(atp->Model, sizeof(atp->Model), + buf, strnlen(buf, sizeof(buf))); + else + sa_strncpy_safe(atp->Model, sizeof(atp->Model), + atp->ModelDescription, + sizeof(atp->ModelDescription)); + + /* Get HardwareVersion */ + sa_strncpy_safe(atp->HardwareVersion, sizeof(atp->HardwareVersion), + hba_info.HardwareVersion, + sizeof(hba_info.HardwareVersion)); + + /* Get OptionROMVersion (TODO) */ + sa_strncpy_safe(atp->OptionROMVersion, sizeof(atp->OptionROMVersion), + HBA_ROM_VERSION, sizeof(HBA_ROM_VERSION)); + + /* Get FirmwareVersion (TODO) */ + sa_strncpy_safe(atp->FirmwareVersion, sizeof(atp->FirmwareVersion), + HBA_FW_VERSION, sizeof(HBA_FW_VERSION)); + + /* Get VendorSpecificID (TODO) */ + atp->VendorSpecificID = HBA_VENDOR_SPECIFIC_ID; + + /* Get DriverVersion */ + rc = sa_sys_read_line(hba_dir, SYSFS_MODULE_VER, + atp->DriverVersion, sizeof(atp->DriverVersion)); + + /* Get NodeSymbolicName */ + sa_strncpy_safe(atp->NodeSymbolicName, sizeof(atp->NodeSymbolicName), + ap->ad_name, sizeof(atp->NodeSymbolicName)); + + /* Get NodeWWN - The NodeWWN is the same as + * the NodeWWN of the local port. + */ + memcpy((char *)&atp->NodeWWN, (char *)&pap->NodeWWN, + sizeof(pap->NodeWWN)); + + /* Get DriverName */ + snprintf(drv_dir, sizeof(drv_dir), "%s" SYSFS_MODULE , hba_dir); + i = readlink(drv_dir, buf, sizeof(buf)); + if (i < 0) + i = 0; + buf[i] = '\0'; + if (!strstr(buf, "module")) { + /* + * Does not find "module" in the string. + * This should not happen. In this case, set + * the driver name to "Unknown". + */ + driverName = "Unknown"; + } else + driverName = strstr(buf, "module") + 7; + sa_strncpy_safe(atp->DriverName, sizeof(atp->DriverName), + driverName, sizeof(atp->DriverName)); + + /* + * Give HBA to library + */ + rc = adapter_create(ap); + if (rc != HBA_STATUS_OK) { + fprintf(stderr, "%s: adapter_create failed, status=%d\n", + __func__, rc); + adapter_destroy(ap); /* free adapter and ports */ + } + + return 0; + +skip: + free(pp); + free(ap); + return 0; +} + +void +copy_wwn(HBA_WWN *dest, fc_wwn_t src) +{ + dest->wwn[0] = (u_char) (src >> 56); + dest->wwn[1] = (u_char) (src >> 48); + dest->wwn[2] = (u_char) (src >> 40); + dest->wwn[3] = (u_char) (src >> 32); + dest->wwn[4] = (u_char) (src >> 24); + dest->wwn[5] = (u_char) (src >> 16); + dest->wwn[6] = (u_char) (src >> 8); + dest->wwn[7] = (u_char) src; +} + +/* Test for a non-zero WWN */ +int +is_wwn_nonzero(HBA_WWN *wwn) +{ + return (wwn->wwn[0] | wwn->wwn[1] | wwn->wwn[2] | wwn->wwn[3] | + wwn->wwn[4] | wwn->wwn[5] | wwn->wwn[6] | wwn->wwn[7]) != 0; +} + +int +sys_read_wwn(const char *dir, const char *file, HBA_WWN *wwn) +{ + int rc; + u_int64_t val; + + rc = sa_sys_read_u64(dir, file, &val); + if (rc == 0) + copy_wwn(wwn, val); + return rc; +} + +/* Port Statistics */ +HBA_STATUS +sysfs_get_port_stats(char *dir, HBA_PORTSTATISTICS *sp) +{ + int rc; + + rc = sa_sys_read_u64(dir, "seconds_since_last_reset", + (u_int64_t *)&sp->SecondsSinceLastReset); + rc |= sa_sys_read_u64(dir, "tx_frames", (u_int64_t *)&sp->TxFrames); + rc |= sa_sys_read_u64(dir, "tx_words", (u_int64_t *)&sp->TxWords); + rc |= sa_sys_read_u64(dir, "rx_frames", (u_int64_t *)&sp->RxFrames); + rc |= sa_sys_read_u64(dir, "rx_words", (u_int64_t *)&sp->RxWords); + rc |= sa_sys_read_u64(dir, "lip_count", (u_int64_t *)&sp->LIPCount); + rc |= sa_sys_read_u64(dir, "nos_count", (u_int64_t *)&sp->NOSCount); + rc |= sa_sys_read_u64(dir, "error_frames", + (u_int64_t *)&sp->ErrorFrames); + rc |= sa_sys_read_u64(dir, "dumped_frames", + (u_int64_t *)&sp->DumpedFrames); + rc |= sa_sys_read_u64(dir, "link_failure_count", + (u_int64_t *)&sp->LinkFailureCount); + rc |= sa_sys_read_u64(dir, "loss_of_sync_count", + (u_int64_t *)&sp->LossOfSyncCount); + rc |= sa_sys_read_u64(dir, "loss_of_signal_count", + (u_int64_t *)&sp->LossOfSignalCount); + rc |= sa_sys_read_u64(dir, "prim_seq_protocol_err_count", + (u_int64_t *)&sp->PrimitiveSeqProtocolErrCount); + rc |= sa_sys_read_u64(dir, "invalid_tx_word_count", + (u_int64_t *)&sp->InvalidTxWordCount); + rc |= sa_sys_read_u64(dir, "invalid_crc_count", + (u_int64_t *)&sp->InvalidCRCCount); + + return rc; +} + +/* Port FC-4 Statistics */ +HBA_STATUS +sysfs_get_port_fc4stats(char *dir, HBA_FC4STATISTICS *fc4sp) +{ + int rc; + + rc = sa_sys_read_u64(dir, "fcp_input_requests", + (u_int64_t *)&fc4sp->InputRequests); + rc |= sa_sys_read_u64(dir, "fcp_output_requests", + (u_int64_t *)&fc4sp->OutputRequests); + rc |= sa_sys_read_u64(dir, "fcp_control_requests", + (u_int64_t *)&fc4sp->ControlRequests); + rc |= sa_sys_read_u64(dir, "fcp_input_megabytes", + (u_int64_t *)&fc4sp->InputMegabytes); + rc |= sa_sys_read_u64(dir, "fcp_output_megabytes", + (u_int64_t *)&fc4sp->OutputMegabytes); + + return rc; +} +/* + * Open device and read adapter info if available. + */ +void +adapter_init(void) +{ + sa_dir_read(SYSFS_HOST_DIR, sysfs_scan, NULL); +} + +void +adapter_shutdown(void) +{ +} + +HBA_STATUS +get_port_statistics(HBA_HANDLE handle, HBA_UINT32 port, HBA_PORTSTATISTICS *sp) +{ + struct port_info *pp; + char dir[80]; + int rc; + + memset(sp, 0xff, sizeof(*sp)); /* unsupported statistics give -1 */ + pp = adapter_get_port(handle, port); + if (pp == NULL) { + fprintf(stderr, "%s: lookup failed. handle 0x%x port 0x%x\n", + __func__, handle, port); + return HBA_STATUS_ERROR; + } + + snprintf(dir, sizeof(dir), "%s/statistics", pp->host_dir); + rc = sysfs_get_port_stats(dir, sp); + if (rc != 0) { + fprintf(stderr, "%s: sysfs_get_port_stats() failed," + " hba index=%d port index=%d, -rc=0x%x\n", + __func__, pp->ap_adapt->ad_kern_index, + pp->ap_index, -rc); + return HBA_STATUS_ERROR; + } + return HBA_STATUS_OK; +} + +/* + * Get FC4 statistics. + */ +HBA_STATUS +get_port_fc4_statistics(HBA_HANDLE handle, HBA_WWN wwn, + HBA_UINT8 fc4_type, HBA_FC4STATISTICS *sp) +{ + struct port_info *pp; + char dir[80]; + int count; + int rc; + + memset(sp, 0xff, sizeof(*sp)); /* unsupported statistics give -1 */ + + pp = adapter_get_port_by_wwn(handle, wwn, &count); + if (count > 1) + return HBA_STATUS_ERROR_AMBIGUOUS_WWN; + else if (pp == NULL) + return HBA_STATUS_ERROR_ILLEGAL_WWN; + + snprintf(dir, sizeof(dir), "%s/statistics", pp->host_dir); + rc = sysfs_get_port_fc4stats(dir, sp); + if (rc != 0) { + fprintf(stderr, "%s: sysfs_get_port_fc4stats() failed," + " hba index=%d port index=%d, -rc=0x%x\n", + __func__, pp->ap_adapt->ad_kern_index, + pp->ap_index, -rc); + return HBA_STATUS_ERROR; + } + return HBA_STATUS_OK; +} + diff --git a/net_types.h b/net_types.h new file mode 100644 index 0000000..c2ba409 --- /dev/null +++ b/net_types.h @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _LIBSA_NET_TYPES_H_ +#define _LIBSA_NET_TYPES_H_ + +#if !defined(NTOHL) +#include +#endif /* NTOHL */ + +/* + * Type definitions for network order fields in protocol packets. + * The access functions below do gets and puts on these structures. + */ +typedef unsigned char net8_t; /* direct use and assignment allowed */ + +/* + * Aligned network order types. + */ +typedef struct { + u_int16_t net_data; +} net16_t; + +typedef struct { + u_int32_t net_data; +} net32_t; + +/* + * The 64-bit type only requires 32-bit alignment. + */ +typedef struct { + u_int32_t net_data[2]; /* most significant word first */ +} net64_t; + +/* + * 24-bit type. Byte aligned, in spite of the name. + */ +typedef struct { + unsigned char net_data[3]; +} net24_t; + +/* + * 48-bit type. Byte aligned. + */ +typedef struct { + unsigned char net_data[6]; +} net48_t; + +/* + * Unaligned network order types. + * Any of these structures can be byte aligned. No padding is implied. + */ +typedef struct { + unsigned char net_data[2]; +} ua_net16_t; + +typedef struct { + unsigned char net_data[4]; +} ua_net32_t; + +typedef struct { + unsigned char net_data[8]; +} ua_net64_t; + +/* + * Accessor functions. + */ + +/** + * net8_get(net) - fetch from a network-order 8-bit field. + * + * @param net pointer to network-order 8-bit data. + * @return the host-order value. + */ +static inline u_int8_t net8_get(const net8_t * net) +{ + return *net; +} + +/** + * net8_put(net, val) - store to a network-order 8-bit field. + * + * @param net pointer to network-order 8-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net8_put(net8_t * net, u_int8_t val) +{ + *net = val; +} + +/** + * net16_get(net) - fetch from a network-order 16-bit field. + * + * @param net pointer to type net16_t, network-order 16-bit data. + * @return the host-order value. + */ +static inline u_int16_t net16_get(const net16_t * net) +{ + return ntohs(net->net_data); +} + +/** + * net16_put(net, val) - store to a network-order 16-bit field. + * + * @param net pointer to a net16_t, network-order 16-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net16_put(net16_t * net, u_int16_t val) +{ + net->net_data = htons(val); +} + +/** + * ua_net16_get(net) - fetch from an unaligned network-order 16-bit field. + * + * @param net pointer to type ua_net16_t, unaligned, network-order 16-bit data. + * @return the host-order value. + */ +static inline u_int16_t ua_net16_get(const ua_net16_t * net) +{ + return (net->net_data[0] << 8) | net->net_data[1]; +} + +/** + * ua_net16_put(net, val) - store to a network-order 16-bit field. + * + * @param net pointer to a ua_net16_t, network-order 16-bit data. + * @param val host-order value to be stored at net. + */ +static inline void ua_net16_put(ua_net16_t * net, u_int16_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[1] = (u_int8_t)(val & 0xFF); +} + +/** + * net24_get(net) - fetch from a network-order 24-bit field. + * + * @param net pointer to type net24_t, network-order 24-bit data. + * @return the host-order value. + */ +static inline u_int32_t net24_get(const net24_t * net) +{ + return (net->net_data[0] << 16) | + (net->net_data[1] << 8) | net->net_data[2]; +} + +/** + * net24_put(net, val) - store to a network-order 24-bit field. + * + * @param net pointer to a net24_t, network-order 24-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net24_put(net24_t * net, u_int32_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 16) & 0xFF); + net->net_data[1] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[2] = (u_int8_t)(val & 0xFF); +} + +/** + * net32_get(net) - fetch from a network-order 32-bit field. + * + * @param net pointer to type net32_t, network-order 32-bit data. + * @return the host-order value. + */ +static inline u_int32_t net32_get(const net32_t * net) +{ + return ntohl(net->net_data); +} + +/** + * net32_put(net, val) - store to a network-order 32-bit field. + * + * @param net pointer to a net32_t, network-order 32-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net32_put(net32_t * net, u_int32_t val) +{ + net->net_data = htonl(val); +} + +/** + * ua_net32_get(net) - fetch from an unaligned network-order 32-bit field. + * + * @param net pointer to type ua_net32_t, unaligned, network-order 32-bit data. + * @return the host-order value. + */ +static inline u_int32_t ua_net32_get(const ua_net32_t * net) +{ + return (net->net_data[0] << 24) | (net->net_data[1] << 16) | + (net->net_data[2] << 8) | net->net_data[3]; +} + +/** + * ua_net32_put(net, val) - store to a network-order 32-bit field. + * + * @param net pointer to a ua_net32_t, network-order 32-bit data. + * @param val host-order value to be stored at net. + */ +static inline void ua_net32_put(ua_net32_t * net, u_int32_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 24) & 0xFF); + net->net_data[1] = (u_int8_t)((val >> 16) & 0xFF); + net->net_data[2] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[3] = (u_int8_t)(val & 0xFF); +} + +/** + * net48_get(net) - fetch from a network-order 48-bit field. + * + * @param net pointer to type net48_t, network-order 48-bit data. + * @return the host-order value. + */ +static inline u_int64_t net48_get(const net48_t * net) +{ + return ((u_int64_t) net->net_data[0] << 40) | + ((u_int64_t) net->net_data[1] << 32) | + ((u_int64_t) net->net_data[2] << 24) | + ((u_int64_t) net->net_data[3] << 16) | + ((u_int64_t) net->net_data[4] << 8) | + (u_int64_t) net->net_data[5]; +} + +/** + * net48_put(net, val) - store to a network-order 48-bit field. + * + * @param net pointer to a net48_t, network-order 48-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net48_put(net48_t * net, u_int64_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 40) & 0xFF); + net->net_data[1] = (u_int8_t)((val >> 32) & 0xFF); + net->net_data[2] = (u_int8_t)((val >> 24) & 0xFF); + net->net_data[3] = (u_int8_t)((val >> 16) & 0xFF); + net->net_data[4] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[5] = (u_int8_t)(val & 0xFF); +} + +/** + * net64_get(net) - fetch from a network-order 64-bit field. + * + * @param net pointer to type net64_t, network-order 64-bit data. + * @return the host-order value. + */ +static inline u_int64_t net64_get(const net64_t * net) +{ + return ((u_int64_t) ntohl(net->net_data[0]) << 32) | + ntohl(net->net_data[1]); +} + +/** + * net64_put(net, val) - store to a network-order 64-bit field. + * + * @param net pointer to a net64_t, network-order 64-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net64_put(net64_t * net, u_int64_t val) +{ + net->net_data[0] = (u_int32_t)htonl(val >> 32); + net->net_data[1] = (u_int32_t)htonl((u_int32_t) val); +} + +/** + * ua_net64_get(net) - fetch from an unaligned network-order 64-bit field. + * + * @param net pointer to type ua_net64_t, unaligned, network-order 64-bit data. + * @return the host-order value. + */ +static inline u_int64_t ua_net64_get(const ua_net64_t * net) +{ + return ((u_int64_t) net->net_data[0] << 56) | + ((u_int64_t) net->net_data[1] << 48) | + ((u_int64_t) net->net_data[2] << 40) | + ((u_int64_t) net->net_data[3] << 32) | + ((u_int64_t) net->net_data[4] << 24) | + ((u_int64_t) net->net_data[5] << 16) | + ((u_int64_t) net->net_data[6] << 8) | + (u_int64_t) net->net_data[7]; +} + +/** + * ua_net64_put(net, val) - store to a network-order 64-bit field. + * + * @param net pointer to a ua_net64_t, network-order 64-bit data. + * @param val host-order value to be stored at net. + */ +static inline void ua_net64_put(ua_net64_t * net, u_int64_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 56) & 0xFF); + net->net_data[1] = (u_int8_t)((val >> 48) & 0xFF); + net->net_data[2] = (u_int8_t)((val >> 40) & 0xFF); + net->net_data[3] = (u_int8_t)((val >> 32) & 0xFF); + net->net_data[4] = (u_int8_t)((val >> 24) & 0xFF); + net->net_data[5] = (u_int8_t)((val >> 16) & 0xFF); + net->net_data[6] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[7] = (u_int8_t)(val & 0xFF); +} + +/* + * Compile-time initializers for the network-order type structures. + * Note that the upper byte of these values is not masked so the + * compiler will catch initializers that don't fit in the field. + */ + +/** + * NET8_INIT(_val) - initialize a net8_t type. + * + * @param _val 8-bit value. + * @return net8_t network-order value. + */ +#define NET8_INIT(_val) (_val) + +/** + * NET24_INIT(_val) - initialize a net24_t type. + * + * @param _val host-order value. + * @return net24_t network-order value. + */ +#define NET24_INIT(_val) { { \ + ((_val) >> 16), \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +/** + * NET48_INIT(_val) - initialize a net48_t type. + * + * @param _val host-order value. + * @return net48_t network-order value. + */ +#define NET48_INIT(_val) { { \ + ((_val) >> 40), \ + ((_val) >> 32) & 0xff, \ + ((_val) >> 24) & 0xff, \ + ((_val) >> 16) & 0xff, \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +/** + * NET16_INIT(_val) - initialize a net16_t type. + * + * @param _val host-order value. + * @return net16_t network-order value. + */ +#define NET16_INIT(_val) { htons(_val) } + +/** + * UA_NET16_INIT(_val) - initialize an unaligned 16-bit type. + * + * @param _val host-order value. + * @return ua_net24_t network-order value. + */ +#define UA_NET16_INIT(_val) { { \ + ((_val) >> 8), \ + ((_val) >> 0) & 0xff \ + } } + +/** + * NET32_INIT(_val) - initialize a 32-bit type. + * + * @param _val host-order value. + * @return net32_t network-order value. + */ +#define NET32_INIT(_val) { htonl(_val) } + +/** + * UA_NET32_INIT(_val) - initialize an unaligned 32-bit type. + * + * @param _val host-order value. + * @return ua_net32_t network-order value. + */ +#define UA_NET32_INIT(_val) { { \ + ((_val) >> 24), \ + ((_val) >> 16) & 0xff, \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +/** + * UA_NET48_INIT(_val) - initialize an unaligned 48-bit type. + * + * @param _val host-order value. + * @return ua_net48_t network-order value. + */ +#define UA_NET48_INIT(_val) { { \ + ((_val) >> 40), \ + ((_val) >> 32) & 0xff, \ + ((_val) >> 24) & 0xff, \ + ((_val) >> 16) & 0xff, \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +/** + * NET64_INIT(_val) - initialize an unaligned 64-bit type. + * + * @param _val host-order value. + * @return ua_net64_t network-order value. + */ +#define NET64_INIT(_val) { { \ + htonl((_val) >> 32), \ + htonl((_val) & 0xffffffff) \ + } } + +/** + * UA_NET64_INIT(_val) - initialize a 64-bit type. + * + * @param _val host-order value. + * @return net64_t network-order value. + */ +#define UA_NET64_INIT(_val) { { \ + ((_val) >> 56), \ + ((_val) >> 48) & 0xff, \ + ((_val) >> 40) & 0xff, \ + ((_val) >> 32) & 0xff, \ + ((_val) >> 24) & 0xff, \ + ((_val) >> 16) & 0xff, \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +#endif /* _LIBSA_NET_TYPES_H_ */ diff --git a/pci.c b/pci.c new file mode 100644 index 0000000..642e33b --- /dev/null +++ b/pci.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "utils.h" +#include "adapt_impl.h" +#include +#include +#include + +static void +get_device_serial_number(struct pci_device *dev, struct hba_info *hba_info) +{ + pciaddr_t offset; + u_int32_t pcie_cap_header; + u_int16_t pcie_cap_id; + u_int16_t status; + u_int8_t cap_ptr; + u_int32_t dword_low = 0; + u_int32_t dword_high = 0; + int rc; + + /* Default */ + snprintf(hba_info->SerialNumber, + sizeof(hba_info->SerialNumber), + "Unknown"); + /* + * Read the Status Register in the PCIe configuration + * header space to see if the PCI Capability List is + * supported by this device. + */ + rc = pci_device_cfg_read_u16(dev, &status, PCI_STATUS); + if (rc) { + fprintf(stderr, "Failed reading PCI Status Register\n"); + return; + } + if (!(status & PCI_STATUS_CAP_LIST)) { + printf("PCI capabilities are not supported\n"); + return; + } + + /* + * Read the offset (cap_ptr) of first entry in the capability list in + * the PCI configuration space. + */ + rc = pci_device_cfg_read_u8(dev, &cap_ptr, PCI_CAPABILITY_LIST); + if (rc) { + fprintf(stderr, + "Failed reading PCI Capability List Register\n"); + return; + } + offset = cap_ptr; + + /* Search for the PCIe capability */ + while (offset) { + u_int8_t cap_id; + u_int8_t next_cap; + + rc = pci_device_cfg_read_u8(dev, &cap_id, + offset + PCI_CAP_LIST_ID); + if (rc) { +#if defined(__x86_64__) + fprintf(stderr, + "Failed reading capability ID at 0x%lx\n", + offset + PCI_CAP_LIST_ID); +#elif defined(__i386__) + fprintf(stderr, + "Failed reading capability ID at 0x%llx\n", + offset + PCI_CAP_LIST_ID); +#endif + return; + } + + if (cap_id != PCI_CAP_ID_EXP) { + rc = pci_device_cfg_read_u8(dev, &next_cap, + offset + PCI_CAP_LIST_NEXT); + if (rc) { +#if defined(__x86_64__) + fprintf(stderr, "Failed reading next capability " + "offset at 0x%lx\n", + offset + PCI_CAP_LIST_NEXT); +#elif defined(__i386__) + fprintf(stderr, "Failed reading next capability " + "offset at 0x%llx\n", + offset + PCI_CAP_LIST_NEXT); +#endif + return; + } + offset = (pciaddr_t)next_cap; + continue; + } + + /* + * PCIe Capability Structure exists! + */ + + /* + * The first PCIe extended capability is located at + * offset 0x100 in the device configuration space. + */ + offset = 0x100; + + do { + rc = pci_device_cfg_read_u32(dev, &pcie_cap_header, + offset); + if (rc) { + fprintf(stderr, + "Failed reading PCIe config header\n"); + return; + } + + /* Get the PCIe Extended Capability ID */ + pcie_cap_id = pcie_cap_header & 0xffff; + + if (pcie_cap_id != PCI_EXT_CAP_ID_DSN) { + /* Get the offset of the next capability */ + offset = (pciaddr_t)pcie_cap_header >> 20; + continue; + } + + /* + * Found the serial number register! + */ + + rc = pci_device_cfg_read_u32(dev, + &dword_low, offset + 4); + rc = pci_device_cfg_read_u32(dev, + &dword_high, offset + 8); + snprintf(hba_info->SerialNumber, + sizeof(hba_info->SerialNumber), + "%02X%02X%02X%02X%02X%02X\n", + dword_high >> 24, (dword_high >> 16) & 0xff, + (dword_high >> 8) & 0xff, (dword_low >> 16) & 0xff, + (dword_low >> 8) & 0xff, dword_low & 0xff); + break; + } while (offset); + break; + } +} + +static void +get_pci_device_info(struct pci_device *dev, struct hba_info *hba_info) +{ + const char *name; + u_int8_t revision; + char *unknown = "Unknown"; + + name = pci_device_get_vendor_name(dev); + if (!name) + name = unknown; + sa_strncpy_safe(hba_info->Manufacturer, + sizeof(hba_info->Manufacturer), + name, sizeof(hba_info->Manufacturer)); + + name = pci_device_get_device_name(dev); + if (!name) + name = unknown; + sa_strncpy_safe(hba_info->ModelDescription, + sizeof(hba_info->ModelDescription), + name, sizeof(hba_info->ModelDescription)); + + /* + * Reading hardware revision from PCIe + * configuration header space. + */ + pci_device_cfg_read_u8(dev, &revision, PCI_REVISION_ID); + snprintf(hba_info->HardwareVersion, + sizeof(hba_info->HardwareVersion), + "%02x", revision); + + hba_info->NumberOfPorts = 1; + + /* + * Searching for serial number in PCIe extended + * capabilities space + */ + get_device_serial_number(dev, hba_info); +} + +HBA_STATUS +find_pci_device(struct hba_info *hba_info) +{ + struct pci_device_iterator *iterator; + struct pci_device *dev; + struct pci_slot_match match; + + int rc; + + rc = pci_system_init(); + if (rc) { + fprintf(stderr, "pci_system_init failed\n"); + return HBA_STATUS_ERROR; + } + + match.domain = hba_info->domain; + match.bus = hba_info->bus; + match.dev = hba_info->dev; + match.func = hba_info->func; + + iterator = pci_slot_match_iterator_create(&match); + + for (;;) { + dev = pci_device_next(iterator); + if (!dev) + break; + get_pci_device_info(dev, hba_info); + } + + pci_system_cleanup(); + return HBA_STATUS_OK; +} + diff --git a/rport.c b/rport.c new file mode 100644 index 0000000..1460c2e --- /dev/null +++ b/rport.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "utils.h" +#include "api_lib.h" +#include "adapt_impl.h" + +static int sys_read_port_state(const char *, const char *, u_int32_t *); +static int sys_read_classes(const char *, const char *, u_int32_t *); + +static struct sa_table rports_table; /* table of discovered ports */ + +/* + * Handle a single remote port from the /sys directory entry. + * The return value is 0 unless an error is detected which should stop the + * directory read. + */ +static int +sysfs_get_rport(struct dirent *dp, void *arg) +{ + struct port_info *rp; + HBA_PORTATTRIBUTES *rpa; + int rc; + u_int32_t hba; + u_int32_t port; + u_int32_t rp_index; + char *rport_dir; + char buf[256]; + + /* + * Parse name into bus number, channel number, and remote port number. + */ + hba = ~0; + port = ~0; + rp_index = ~0; + rc = sscanf(dp->d_name, SYSFS_RPORT_DIR, &hba, &port, &rp_index); + if (rc != 3) { + fprintf(stderr, + "%s: remote port %s didn't parse." + " rc %d h 0x%x p 0x%x rp 0x%x\n", __func__, + dp->d_name, rc, hba, port, rp_index); + return 0; + } + + /* + * Allocate a remote port. + */ + rp = malloc(sizeof(*rp)); + if (rp == NULL) { + fprintf(stderr, "%s: malloc for remote port %s failed," + " errno=0x%x\n", __func__, dp->d_name, errno); + return ENOMEM; + } + memset(rp, 0, sizeof(*rp)); + rp->ap_kern_hba = hba; + rp->ap_index = port; + rp->ap_disc_index = rp_index; + rpa = &rp->ap_attr; + + snprintf(rpa->OSDeviceName, sizeof(rpa->OSDeviceName), "%s/%s", + SYSFS_RPORT_ROOT, dp->d_name); + rport_dir = rpa->OSDeviceName; + rc = 0; + rc |= sys_read_wwn(rport_dir, "node_name", &rpa->NodeWWN); + rc |= sys_read_wwn(rport_dir, "port_name", &rpa->PortWWN); + rc |= sa_sys_read_u32(rport_dir, "port_id", &rpa->PortFcId); + rc |= sa_sys_read_u32(rport_dir, "scsi_target_id", &rp->ap_scsi_target); + sa_sys_read_line(rport_dir, "maxframe_size", buf, sizeof(buf)); + sscanf(buf, "%d", &rpa->PortMaxFrameSize); + rc |= sys_read_port_state(rport_dir, "port_state", &rpa->PortState); + rc |= sys_read_classes(rport_dir, "supported_classes", + &rpa->PortSupportedClassofService); + if (rc != 0 || sa_table_append(&rports_table, rp) < 0) { + if (rc != 0) + fprintf(stderr, + "%s: errors (%x) from /sys reads in %s\n", + __func__, rc, dp->d_name); + else + fprintf(stderr, + "%s: sa_table_append error on rport %s\n", + __func__, dp->d_name); + free(rp); + } + return 0; +} + +/* + * Get remote port information from /sys. + */ +static void +sysfs_find_rports(void) +{ + sa_dir_read(SYSFS_RPORT_ROOT, sysfs_get_rport, NULL); +} + +/* + * Read port state as formatted by scsi_transport_fc.c in the linux kernel. + */ +static int +sys_read_port_state(const char *dir, const char *file, u_int32_t *statep) +{ + char buf[256]; + int rc; + + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc == 0) { + rc = sa_enum_encode(port_states_table, buf, statep); + if (rc != 0) + fprintf(stderr, + "%s: parse error. file %s/%s line '%s'\n", + __func__, dir, file, buf); + } + return rc; +} + +/* + * Read class list as formatted by scsi_transport_fc.c in the linux kernel. + * Format is expected to be "Class 3[, Class 4]..." + * Actually accepts "[Class ]3[,[ ][Class ]4]..." (i.e., "Class" and spaces + * are optional). + */ +static int +sys_read_classes(const char *dir, const char *file, u_int32_t *classp) +{ + char buf[256]; + int rc; + u_int32_t val; + char *cp; + char *ep; + + *classp = 0; + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc == 0 && strstr(buf, "unspecified") == NULL) { + for (cp = buf; *cp != '\0'; cp = ep) { + if (strncmp(cp, "Class ", 6) == 0) + cp += 6; + val = strtoul(cp, &ep, 10); + *classp |= 1 << val; + if (*ep == '\0') + break; + if (*ep == ',') { + ep++; + if (*ep == ' ') + ep++; + } else { + fprintf(stderr, "%s: parse error. file %s/%s " + "line '%s' ep '%c'\n", __func__, + dir, file, buf, *ep); + rc = -1; + break; + } + } + } + return rc; +} + +/* + * Get all discovered ports for a particular port using /sys. + */ +void +get_rport_info(struct port_info *pp) +{ + struct port_info *rp; + int rp_count = 0; + int ri; + + if (rports_table.st_size == 0) + sysfs_find_rports(); + + for (ri = 0; ri < rports_table.st_size; ri++) { + rp = sa_table_lookup(&rports_table, ri); + if (rp != NULL) { + if (rp->ap_kern_hba == pp->ap_kern_hba && + rp->ap_index == pp->ap_index && + rp->ap_adapt == NULL) { + rp->ap_adapt = pp->ap_adapt; + if (sa_table_lookup(&pp->ap_rports, + rp->ap_disc_index)) { + fprintf(stderr, + "%s: discovered port exists. " + "hba %x port %x rport %x\n", + __func__, pp->ap_kern_hba, + pp->ap_index, rp->ap_disc_index); + } + sa_table_insert(&pp->ap_rports, + rp->ap_disc_index, rp); + } + rp_count++; + } + } +} + diff --git a/scsi.c b/scsi.c new file mode 100644 index 0000000..c04d410 --- /dev/null +++ b/scsi.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "utils.h" +#include "api_lib.h" +#include "adapt_impl.h" +#include "bind_impl.h" +#include "fc_scsi.h" + +/* + * Inquiry V1. + */ +HBA_STATUS +scsi_inquiry_v1(HBA_HANDLE handle, HBA_WWN disc_wwpn, HBA_UINT64 fc_lun, + HBA_UINT8 evpd, HBA_UINT32 page_code, void *resp, + HBA_UINT32 resp_len, void *sense, HBA_UINT32 sense_len) +{ + struct port_info *pp; + char sg_name[50]; + HBA_UINT8 stat; + HBA_STATUS status; + + /* + * Find port. + */ + pp = adapter_get_port(handle, 0); + if (pp == NULL) + return HBA_STATUS_ERROR_INVALID_HANDLE; + + if (get_binding_sg_name( + pp, disc_wwpn, fc_lun, sg_name, sizeof(sg_name)) != 0) + return HBA_STATUS_ERROR_TARGET_LUN; + + status = sg_issue_inquiry(sg_name, evpd ? SCSI_INQF_EVPD : 0, page_code, + resp, &resp_len, &stat, sense, &sense_len); + if (status == HBA_STATUS_OK && stat == SCSI_ST_CHECK) + status = HBA_STATUS_SCSI_CHECK_CONDITION; + + return status; +} + +/* + * Inquiry V2. + */ +HBA_STATUS +scsi_inquiry_v2(HBA_HANDLE handle, HBA_WWN wwpn, HBA_WWN disc_wwpn, + HBA_UINT64 fc_lun, HBA_UINT8 cdb_byte1, + HBA_UINT8 cdb_byte2, void *resp, HBA_UINT32 *resp_lenp, + HBA_UINT8 *statp, void *sense, HBA_UINT32 *sense_lenp) +{ + struct port_info *pp; + char sg_name[50]; + + /* + * Find port. + */ + pp = adapter_get_port_by_wwn(handle, wwpn, NULL); + if (pp == NULL) + return HBA_STATUS_ERROR_ILLEGAL_WWN; + + if (get_binding_sg_name( + pp, disc_wwpn, fc_lun, sg_name, sizeof(sg_name)) != 0) + return HBA_STATUS_ERROR_TARGET_LUN; + + return sg_issue_inquiry(sg_name, cdb_byte1, cdb_byte2, + resp, resp_lenp, statp, sense, sense_lenp); +} + +/* + * Read capacity V1. + */ +HBA_STATUS +scsi_read_capacity_v1(HBA_HANDLE handle, HBA_WWN disc_wwpn, + HBA_UINT64 fc_lun, void *resp, + HBA_UINT32 resp_len, void *sense, + HBA_UINT32 sense_len) +{ + struct port_info *pp; + char sg_name[50]; + HBA_UINT8 stat; + HBA_STATUS status; + + /* + * Find port. + */ + pp = adapter_get_port(handle, 0); + if (pp == NULL) + return HBA_STATUS_ERROR_INVALID_HANDLE; + + if (get_binding_sg_name( + pp, disc_wwpn, fc_lun, sg_name, sizeof(sg_name)) != 0) + return HBA_STATUS_ERROR_TARGET_LUN; + + status = sg_issue_read_capacity(sg_name, resp, &resp_len, + &stat, sense, &sense_len); + if (status == HBA_STATUS_OK && stat == SCSI_ST_CHECK) + status = HBA_STATUS_SCSI_CHECK_CONDITION; + + return status; +} + +/* + * Read capacity V2. + */ +HBA_STATUS +scsi_read_capacity_v2(HBA_HANDLE handle, HBA_WWN wwpn, + HBA_WWN disc_wwpn, HBA_UINT64 fc_lun, + void *resp, HBA_UINT32 *resp_lenp, + HBA_UINT8 *statp, void *sense, + HBA_UINT32 *sense_lenp) +{ + struct port_info *pp; + char sg_name[50]; + + /* + * Find port. + */ + pp = adapter_get_port_by_wwn(handle, wwpn, NULL); + if (pp == NULL) + return HBA_STATUS_ERROR_ILLEGAL_WWN; + + if (get_binding_sg_name( + pp, disc_wwpn, fc_lun, sg_name, sizeof(sg_name)) != 0) + return HBA_STATUS_ERROR_TARGET_LUN; + + return sg_issue_read_capacity(sg_name, resp, resp_lenp, + statp, sense, sense_lenp); +} + +/* + * Report LUNS V1. + */ +HBA_STATUS +scsi_report_luns_v1(HBA_HANDLE handle, HBA_WWN disc_wwpn, + void *resp, HBA_UINT32 resp_len, + void *sense, HBA_UINT32 sense_len) +{ + struct port_info *pp; + char sg_name[50]; + HBA_UINT8 stat; + HBA_STATUS status; + + /* + * Find port. + */ + pp = adapter_get_port(handle, 0); + if (pp == NULL) + return HBA_STATUS_ERROR_INVALID_HANDLE; + + if (get_binding_sg_name( + pp, disc_wwpn, 0, sg_name, sizeof(sg_name)) != 0) + return HBA_STATUS_ERROR_TARGET_PORT_WWN; + + status = sg_issue_report_luns(sg_name, resp, &resp_len, + &stat, sense, &sense_len); + if (status == HBA_STATUS_OK && stat == SCSI_ST_CHECK) + status = HBA_STATUS_SCSI_CHECK_CONDITION; + + return status; +} + +/* + * Report LUNS V2. + */ +HBA_STATUS +scsi_report_luns_v2(HBA_HANDLE handle, HBA_WWN wwpn, + HBA_WWN disc_wwpn, void *resp, + HBA_UINT32 *resp_lenp, HBA_UINT8 *statp, + void *sense, HBA_UINT32 *sense_lenp) +{ + struct port_info *pp; + char sg_name[50]; + + /* + * Find port. + */ + pp = adapter_get_port_by_wwn(handle, wwpn, NULL); + if (pp == NULL) + return HBA_STATUS_ERROR_ILLEGAL_WWN; + + if (get_binding_sg_name( + pp, disc_wwpn, 0, sg_name, sizeof(sg_name)) != 0) + return HBA_STATUS_ERROR_TARGET_PORT_WWN; + + return sg_issue_report_luns(sg_name, resp, resp_lenp, + statp, sense, sense_lenp); +} + diff --git a/sg.c b/sg.c new file mode 100644 index 0000000..229779c --- /dev/null +++ b/sg.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include "utils.h" +#include "api_lib.h" +#include "adapt_impl.h" +#include "fc_scsi.h" + +/* + * Perform INQUIRY of SCSI-generic device. + */ +HBA_STATUS +sg_issue_inquiry(const char *file, HBA_UINT8 cdb_byte1, + HBA_UINT8 cdb_byte2, void *buf, HBA_UINT32 *lenp, + HBA_UINT8 *statp, void *sense, HBA_UINT32 *sense_lenp) +{ + struct sg_io_hdr hdr; + struct scsi_inquiry cmd; + size_t len; + HBA_UINT32 slen; + int fd; + int rc; + + len = *lenp; + slen = *sense_lenp; + if (slen > 255) + slen = 255; /* must fit in an 8-bit field */ + if (len > 255) + len = 255; /* sometimes must fit in 8-byte field */ + *lenp = 0; + *statp = 0; + fd = open(file, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: open of %s failed, errno=0x%x\n", + __func__, file, errno); + return HBA_STATUS_ERROR; + } + memset(&hdr, 0, sizeof(hdr)); + memset(&cmd, 0, sizeof(cmd)); + memset(buf, 0, len); + + cmd.in_op = SCSI_OP_INQUIRY; + cmd.in_flags = cdb_byte1; + cmd.in_page_code = cdb_byte2; + ua_net16_put(&cmd.in_alloc_len, len); /* field may actually be 8 bits */ + + hdr.interface_id = 'S'; + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.cmd_len = sizeof(cmd); + hdr.mx_sb_len = slen; + hdr.dxfer_len = len; + hdr.dxferp = (unsigned char *) buf; + hdr.cmdp = (unsigned char *) &cmd; + hdr.sbp = (unsigned char *) sense; + hdr.timeout = 3000; /* mS to wait for result */ + + rc = ioctl(fd, SG_IO, &hdr); + if (rc < 0) { + rc = errno; + fprintf(stderr, "%s: SG_IO error. file %s, errno=0x%x\n", + __func__, file, errno); + close(fd); + return HBA_STATUS_ERROR; + } + close(fd); + *lenp = len - hdr.resid; + *sense_lenp = hdr.sb_len_wr; + *statp = hdr.status; + return HBA_STATUS_OK; +} + +static inline unsigned int +sg_get_id_type(struct scsi_inquiry_desc *dp) +{ + return dp->id_type_flags & SCSI_INQT_TYPE_MASK; +} + +/* + * Get device ID information for HBA-API. + * See the spec. We get the "best" information and leave the rest. + * The buffer is left empty if nothing is gotten. + */ +void +sg_get_dev_id(const char *name, char *buf, size_t result_len) +{ + struct scsi_inquiry_dev_id *idp; + struct scsi_inquiry_desc *dp; + struct scsi_inquiry_desc *best = NULL; + char sense[252]; + HBA_UINT32 len; + HBA_UINT32 slen; + u_char scsi_stat; + size_t rlen; + size_t dlen; + unsigned int type; + + memset(buf, 0, result_len); + len = result_len; + slen = sizeof(sense); + idp = (struct scsi_inquiry_dev_id *) buf; + sg_issue_inquiry(name, SCSI_INQF_EVPD, SCSI_INQP_DEV_ID, + buf, &len, &scsi_stat, sense, &slen); + if (len < sizeof(*idp)) + return; + if (idp->is_page_code != SCSI_INQP_DEV_ID) + return; + len -= sizeof(*idp); + rlen = net16_get(&idp->is_page_len); + if (rlen > len) + rlen = len; + dp = (struct scsi_inquiry_desc *) (idp + 1); + for (; rlen >= sizeof(*dp); + rlen -= dlen, + dp = (struct scsi_inquiry_desc *) ((char *) dp + dlen)) { + dlen = dp->id_designator_len + sizeof(*dp) - + sizeof(dp->id_designator[0]); + if (dlen > rlen) + break; + type = sg_get_id_type(dp); + if (type > SCSI_DTYPE_NAA) + continue; + if (best == NULL) + best = dp; + else if (type == sg_get_id_type(best) && + (dp->id_designator_len < best->id_designator_len || + (dp->id_designator_len == best->id_designator_len && + memcmp(dp->id_designator, best->id_designator, + best->id_designator_len) < 0))) { + best = dp; + } else if (type > sg_get_id_type(best)) + best = dp; + } + if (best) { + dp = best; + dlen = dp->id_designator_len + sizeof(*dp) - + sizeof(dp->id_designator[0]); + if (dlen > result_len) + dlen = 0; /* can't happen */ + else + memmove(buf, dp, dlen); /* areas may overlap */ + memset(buf + dlen, 0, result_len - dlen); + } +} + +/* + * Read Capacity for HBA-API. + */ +HBA_STATUS +sg_issue_read_capacity(const char *file, void *resp, HBA_UINT32 *resp_lenp, + HBA_UINT8 *statp, void *sense, HBA_UINT32 *sense_lenp) +{ + struct sg_io_hdr hdr; + struct scsi_rcap10 cmd; + struct scsi_rcap16 cmd_16; + size_t len; + int fd; + int rc; + + len = *resp_lenp; + *resp_lenp = 0; + fd = open(file, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: open of %s failed, errno=0x%x\n", + __func__, file, errno); + return errno; + } + memset(&hdr, 0, sizeof(hdr)); + + /* If the response buffer size is enough to + * accomodate READ CAPACITY(16) response issue + * SCSI READ CAPACITY(16) else issue + * SCSI READ CAPACITY(10) + */ + if (len >= sizeof(struct scsi_rcap16_resp)) { + memset(&cmd_16, 0, sizeof(cmd_16)); + cmd_16.rc_op = SCSI_OP_SA_IN_16; + cmd_16.rc_sa = SCSI_SA_READ_CAP16; + ua_net32_put(&cmd_16.rc_alloc_len, len); + hdr.cmd_len = sizeof(cmd_16); + hdr.cmdp = (unsigned char *) &cmd_16; + } else { + memset(&cmd, 0, sizeof(cmd)); + cmd.rc_op = SCSI_OP_READ_CAP10; + hdr.cmd_len = sizeof(cmd); + hdr.cmdp = (unsigned char *) &cmd; + } + + hdr.interface_id = 'S'; + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.mx_sb_len = *sense_lenp; + hdr.dxfer_len = len; + hdr.dxferp = (unsigned char *) resp; + hdr.sbp = (unsigned char *) sense; + hdr.timeout = UINT_MAX; + hdr.timeout = 3000; /* mS to wait for result */ + + rc = ioctl(fd, SG_IO, &hdr); + if (rc < 0) { + rc = errno; + fprintf(stderr, "%s: SG_IO error. file %s, errno=0x%x\n", + __func__, file, errno); + close(fd); + return HBA_STATUS_ERROR; + } + close(fd); + *resp_lenp = len - hdr.resid; + *sense_lenp = hdr.sb_len_wr; + *statp = hdr.status; + return HBA_STATUS_OK; +} + +/* + * Report LUNs for HBA-API. + */ +HBA_STATUS +sg_issue_report_luns(const char *file, void *resp, HBA_UINT32 *resp_lenp, + HBA_UINT8 *statp, void *sense, HBA_UINT32 *sense_lenp) +{ + struct sg_io_hdr hdr; + struct scsi_report_luns cmd; + size_t len; + int fd; + int rc; + + len = *resp_lenp; + *resp_lenp = 0; + fd = open(file, O_RDWR); + if (fd < 0) { + fprintf(stderr, "%s: open of %s failed, errno=0x%x\n", + __func__, file, errno); + return errno; + } + memset(&hdr, 0, sizeof(hdr)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.rl_op = SCSI_OP_REPORT_LUNS; + ua_net32_put(&cmd.rl_alloc_len, len); + + hdr.interface_id = 'S'; + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.cmd_len = sizeof(cmd); + hdr.mx_sb_len = *sense_lenp; + hdr.dxfer_len = len; + hdr.dxferp = (unsigned char *) resp; + hdr.cmdp = (unsigned char *) &cmd; + hdr.sbp = (unsigned char *) sense; + hdr.timeout = UINT_MAX; + hdr.timeout = 3000; /* mS to wait for result */ + + rc = ioctl(fd, SG_IO, &hdr); + if (rc < 0) { + rc = errno; + fprintf(stderr, "%s: SG_IO error. file %s, errno=0x%x\n", + __func__, file, errno); + close(fd); + return HBA_STATUS_ERROR; + } + close(fd); + *resp_lenp = len - hdr.resid; + *sense_lenp = hdr.sb_len_wr; + *statp = hdr.status; + return HBA_STATUS_OK; +} + diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..3a5666b --- /dev/null +++ b/utils.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "utils.h" + +/* + * Read a line from the specified file in the specified directory + * into the buffer. The file is opened and closed. + * Any trailing white space is trimmed off. + * This is useful for accessing /sys files. + * Returns 0 or an error number. + */ +int +sa_sys_read_line(const char *dir, const char *file, char *buf, size_t len) +{ + FILE *fp; + char file_name[256]; + char *cp; + int rc = 0; + + snprintf(file_name, sizeof(file_name), "%s/%s", dir, file); + fp = fopen(file_name, "r"); + if (fp == NULL) + rc = -1; + else { + cp = fgets(buf, len, fp); + if (cp == NULL) { + fprintf(stderr, + "%s: read error or empty file %s," + " errno=0x%x\n", __func__, + file_name, errno); + rc = -1; + } else { + + /* + * Trim off trailing newline or other white space. + */ + cp = buf + strlen(buf); + while (--cp >= buf && isspace(*cp)) + *cp = '\0'; + } + fclose(fp); + } + return rc; +} + +/* + * Write a string to the specified file in the specified directory. + * The file is opened and closed. + * The string has a new-line appended to it. + * This is useful for accessing /sys files. + * Returns 0 or an error number. + */ +int +sa_sys_write_line(const char *dir, const char *file, const char *string) +{ + FILE *fp; + char file_name[256]; + int rc = 0; + + snprintf(file_name, sizeof(file_name), "%s/%s", dir, file); + fp = fopen(file_name, "w"); + if (fp == NULL) { + fprintf(stderr, "%s: fopen of %s failed, errno=0x%x\n", + __func__, file_name, errno); + rc = -1; + } else { + rc = fprintf(fp, "%s\n", string); + if (rc < 0) + fprintf(stderr, + "%s: write to %s of %s failed, errno=0x%x\n", + __func__, file_name, string, errno); + fclose(fp); + } + return rc; +} + +int +sa_sys_read_u32(const char *dir, const char *file, u_int32_t *vp) +{ + char buf[256]; + int rc; + u_int32_t val; + char *endptr; + + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc == 0) { + val = strtoul(buf, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, + "%s: parse error. file %s/%s line '%s'\n", + __func__, dir, file, buf); + rc = -1; + } else + *vp = val; + } + return rc; +} + +int +sa_sys_read_u64(const char *dir, const char *file, u_int64_t *vp) +{ + char buf[256]; + int rc; + u_int64_t val; + char *endptr; + + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc == 0) { + val = strtoull(buf, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, + "%s: parse error. file %s/%s line '%s'\n", + __func__, dir, file, buf); + rc = -1; + } else + *vp = val; + } + return rc; +} + +/* + * Make a printable NUL-terminated copy of the string. + * The source buffer might not be NUL-terminated. + */ +char * +sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len) +{ + char *dp = dest; + const char *sp = src; + + while (len-- > 1 && src_len-- > 0 && *sp != '\0') { + *dp++ = isprint(*sp) ? *sp : (isspace(*sp) ? ' ' : '.'); + sp++; + } + *dp = '\0'; + + /* + * Take off trailing blanks. + */ + while (--dp >= dest && isspace(*dp)) + *dp = '\0'; + + return dest; +} + +/** sa_enum_encode(tp, name, valp) + * + * @param tp pointer to table of names and values, struct sa_nameval. + * @param name string to be encoded into a value + * @returns zero on success, non-zero if no matching string found. + */ +int +sa_enum_encode(const struct sa_nameval *tp, const char *name, u_int32_t *valp) +{ + for (; tp->nv_name != NULL; tp++) { + if (strcasecmp(tp->nv_name, name) == 0) { + *valp = tp->nv_val; + return 0; + } + } + return -1; +} + +/** sa_enum_decode(buf, len, tp, val) + * + * @param buf buffer for result (may be used or not). + * @param len size of buffer (at least 32 bytes recommended). + * @param tp pointer to table of names and values, struct sa_nameval. + * @param val value to be decoded into a name. + * @returns pointer to name string. Unknown values are put into buffer in hex. + */ +const char * +sa_enum_decode(char *buf, size_t len, + const struct sa_nameval *tp, u_int32_t val) +{ + for (; tp->nv_name != NULL; tp++) { + if (tp->nv_val == val) + return tp->nv_name; + } + snprintf(buf, len, "Unknown (code 0x%X)", val); + return buf; +} + +/* + * Read through a directory and call a function for each entry. + */ +int +sa_dir_read(char *dir_name, int (*func)(struct dirent *dp, void *), void *arg) +{ + DIR *dir; + struct dirent *dp; + int error = 0; + + dir = opendir(dir_name); + if (dir == NULL) + error = errno; + else { + while ((dp = readdir(dir)) != NULL && error == 0) { + if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + error = (*func)(dp, arg); + } + closedir(dir); + } + return error; +} + +/* + * Size of on-stack line buffers. + * These shouldn't be to large for a kernel stack frame. + */ +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +static const u_int32_t sa_table_growth = 16; /* entries to grow by */ + +/** sa_table_grow(tp, index) - add space to a table for index. + * + * @param tp pointer to sa_table structure. + * @param index - new index past the end of the current table. + * @returns new index, or -1 if table couldn't be grown. + * + * Note: if the table has never been used, and is still all zero, this works. + * + * Note: perhaps not safe for multithreading. Caller can lock the table + * externally, but reallocation can take a while, during which time the + * caller may not wish to hold the lock. + */ +int +sa_table_grow(struct sa_table *tp, u_int32_t index) +{ + u_int32_t new_size; + void **ap; + + if (index >= tp->st_size) { + new_size = index + sa_table_growth; + ap = realloc(tp->st_table, new_size * sizeof(*ap)); + if (ap == NULL) + return -1; + memset(ap + tp->st_size, 0, + (new_size - tp->st_size) * sizeof(*ap)); + tp->st_table = ap; + tp->st_size = new_size; + } + tp->st_limit = index + 1; + return index; +} + +/** sa_table_destroy(tp) - free memory used by table. + * + * @param tp pointer to sa_table structure. + */ +void +sa_table_destroy(struct sa_table *tp) +{ + if (tp->st_table) { + free(tp->st_table); + tp->st_table = NULL; + } + tp->st_limit = 0; + tp->st_size = 0; +} + +/** sa_table_destroy_all(tp) - free memory used by table, including entries. + * + * @param tp pointer to sa_table structure. + */ +void +sa_table_destroy_all(struct sa_table *tp) +{ + int i; + + if (tp->st_table) { + for (i = 0; i < tp->st_limit; i++) { + if (tp->st_table[i]) { + free(tp->st_table[i]); + tp->st_table[i] = NULL; + } + } + } + sa_table_destroy(tp); +} + +/** sa_table_iterate(tp, handler, arg) + * + * @param tp pointer to sa_table structure. + * @param handler function to be called for each non-NULL entry. + * @param arg argument for function. + */ +void +sa_table_iterate(struct sa_table *tp, + void (*handler)(void *ep, void *arg), + void *arg) +{ + int i; + void *ep; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL) + (*handler)(ep, arg); + } +} + +/** sa_table_search(tp, match, arg) + * + * @param tp pointer to sa_table structure. + * @param match function to compare entries with arg and + * return non-NULL if match. + * @param arg argument for match function. + * + * Note that the value found could actually be something not in the table + * if the match function is doing something clever, like returning a + * sub-structure of the table entry. + */ +void * +sa_table_search(struct sa_table *tp, void *(*match)(void *ep, void *arg), + void *arg) +{ + int i; + void *found = NULL; + void *ep; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL) { + found = (*match)(ep, arg); + if (found != NULL) + break; + } + } + return found; +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..2475a04 --- /dev/null +++ b/utils.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fc_types.h" + +/* + * Structure for tables encoding and decoding name-value pairs such as enums. + */ +struct sa_nameval { + char *nv_name; + u_int32_t nv_val; +}; + +/* + * Structure for integer-indexed tables that can grow. + */ +struct sa_table { + u_int32_t st_size; /* number of entries in table */ + u_int32_t st_limit; /* end of valid entries in table (public) */ + void **st_table; /* re-allocatable array of pointers */ +}; + +/* + * Function prototypes + */ +extern int sa_sys_read_line(const char *, const char *, char *, size_t); +extern int sa_sys_write_line(const char *, const char *, const char *); +extern int sa_sys_read_u32(const char *, const char *, u_int32_t *); +extern int sa_sys_read_u64(const char *, const char *, u_int64_t *); +extern int sa_dir_read(char *, int (*)(struct dirent *, void *), void *); +extern char *sa_strncpy_safe(char *dest, size_t len, + const char *src, size_t src_len); +extern const char *sa_enum_decode(char *, size_t, + const struct sa_nameval *, u_int32_t); +extern int sa_enum_encode(const struct sa_nameval *tp, + const char *, u_int32_t *); +extern const char *sa_flags_decode(char *, size_t, + const struct sa_nameval *, u_int32_t); +extern int sa_table_grow(struct sa_table *, u_int32_t index); +extern void sa_table_destroy_all(struct sa_table *); +extern void sa_table_destroy(struct sa_table *); +extern void sa_table_iterate(struct sa_table *tp, + void (*handler)(void *ep, void *arg), void *arg); +extern void *sa_table_search(struct sa_table *tp, + void *(*match)(void *ep, void *arg), void *arg); + +/** sa_table_init(tp) - initialize a table. + * @param tp table pointer. + * + * This just clears a table structure that was allocated by the caller. + */ +static inline void sa_table_init(struct sa_table *tp) +{ + memset(tp, 0, sizeof(*tp)); +} + +/** sa_table_lookup(tp, index) - lookup an entry in the table. + * @param tp table pointer. + * @param index the index in the table to access + * @returns the entry, or NULL if the index wasn't valid. + */ +static inline void *sa_table_lookup(const struct sa_table *tp, u_int32_t index) +{ + void *ep = NULL; + + if (index < tp->st_limit) + ep = tp->st_table[index]; + return ep; +} + +/** sa_table_lookup_n(tp, n) - find Nth non-empty entry in a table. + * @param tp table pointer. + * @param n is the entry number, the first non-empty entry is 0. + * @returns the entry, or NULL if the end of the table reached first. + */ +static inline void *sa_table_lookup_n(const struct sa_table *tp, u_int32_t n) +{ + void *ep = NULL; + u_int32_t i; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL && n-- == 0) + return ep; + } + return NULL; +} + +/** sa_table_insert(tp, index, ep) - Replace or insert an entry in the table. + * @param tp table pointer. + * @param index the index for the new entry. + * @param ep entry pointer. + * @returns index on success, or -1 if the insert failed. + * + * Note: if the table has never been used, and is still all zero, this works. + * + * Note: perhaps not safe for multithreading. Caller can lock the table + * externally, but reallocation can take a while, during which time the + * caller may not wish to hold the lock. + */ +static inline int sa_table_insert(struct sa_table *tp, + u_int32_t index, void *ep) +{ + if (index >= tp->st_limit && sa_table_grow(tp, index) < 0) + return -1; + tp->st_table[index] = ep; + return index; +} + +/** sa_table_append(tp, ep) - add entry to table and return index. + * + * @param tp pointer to sa_table structure. + * @param ep pointer to new entry, to be added at the end of the table. + * @returns new index, or -1 if table couldn't be grown. + * + * See notes on sa_table_insert(). + */ +static inline int +sa_table_append(struct sa_table *tp, void *ep) +{ + return sa_table_insert(tp, tp->st_limit, ep); +} + +/** sa_table_sort(tp, compare) - sort table in place + * + * @param tp pointer to sa_table structure. + * @param compare function to compare two entries. It is called with pointers + * to the pointers to the entries to be compared. See qsort(3). + */ +static inline void +sa_table_sort(struct sa_table *tp, int (*compare)(const void **, const void **)) +{ + qsort(tp->st_table, tp->st_limit, sizeof(void *), + (int (*)(const void *, const void *)) compare); +} + +#endif /* _UTILS_H_ */