From: greg@kroah.com Date: Thu, 13 Nov 2003 14:30:57 +0000 (-0800) Subject: [PATCH] Add multipath "extra" program from Christophe Varoqui, --- diff --git a/extras/multipath/AUTHOR b/extras/multipath/AUTHOR new file mode 100644 index 000000000..4e8eeef5c --- /dev/null +++ b/extras/multipath/AUTHOR @@ -0,0 +1 @@ +Christophe Varoqui, diff --git a/extras/multipath/COPYING b/extras/multipath/COPYING new file mode 100644 index 000000000..9e31bbf0b --- /dev/null +++ b/extras/multipath/COPYING @@ -0,0 +1,483 @@ + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + 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 Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the 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 a program 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. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + 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, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +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 compile 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) 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. + + c) 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. + + d) 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 source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + 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 to +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 Library 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 + + Appendix: 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 Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, 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/extras/multipath/ChangeLog b/extras/multipath/ChangeLog new file mode 100644 index 000000000..f3014cf01 --- /dev/null +++ b/extras/multipath/ChangeLog @@ -0,0 +1,3 @@ +2003-09-18 Christophe Varoqui + * multipath 0.0.1 released. + * Initial release. diff --git a/extras/multipath/Makefile b/extras/multipath/Makefile new file mode 100644 index 000000000..0835d7a2c --- /dev/null +++ b/extras/multipath/Makefile @@ -0,0 +1,34 @@ +# Makefile +# +# Copyright (C) 2003 Christophe Varoqui, + +EXEC = multipath + +prefix = /usr/local +exec_prefix = ${prefix} +bindir = ${exec_prefix}/bin + +CC = gcc +CFLAGS = -g -O2 -Wall -Wunused -Wstrict-prototypes +LDFLAGS = -lsysfs -ldevmapper + +OBJS = main.o sg_err.o + +all: $(EXEC) + strip $(EXEC) + @echo "" + @echo "Make complete" + +$(EXEC): $(OBJS) + $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) + +clean: + rm -f core *.o $(EXEC) + +install: + install -d $(bindir) + install -m 755 $(EXEC) $(bindir)/ + +# Code dependencies +main.o: main.c main.h sg_err.h sg_include.h +sg_err.o: sg_err.c sg_err.h sg_include.h diff --git a/extras/multipath/README b/extras/multipath/README new file mode 100644 index 000000000..6a5637de4 --- /dev/null +++ b/extras/multipath/README @@ -0,0 +1,82 @@ +Dependancies : +============== + +o libdevmapper : comes with device-mapper-XXXX.tar.gz + See www.sistina.com +o libsysfs : comes with sysutils + See ftp.kernel.org/pub/linux/utils/kernel/hotplug/ + +How it works : +============== + +Fill the all_paths array. Each path store this info : + +struct path { + char dev[FILE_NAME_SIZE]; + char sg_dev[FILE_NAME_SIZE]; + struct scsi_idlun scsi_id; + struct sg_id sg_id; + int state; + char wwid[WWID_SIZE]; +}; + +scsi_id, sg_dev and sg_id are only really useful for 2.4 +kernels, for which SG cmnds must go through sg devs. +In 2.5+ we have the nice opportunity to send SG cmnds +through SCSI bdevs. + +For 2.4 compat, we pivot on idlun tupple to map sg devs +to SCSI bdevs. + +2.4 does not do device enumeration, so we must scan a +defined number of sg devs and scsi bdevs. Good enough. +In 2.5+, we rely on libsysfs (sysutils) to access to +sysfs device enums. + +the wwid is retrieved by a switch fonction. Only White +Listed HW can filled this field. For now only +StorageWorks HW is White Listed. (See notes) + +When all_paths is filled, we coalesce the paths and store +the result in mp array. Each mp is a struct like this : + +struct multipath { + char wwid[WWID_SIZE]; + int npaths; + int pindex[MAX_MP_PATHS]; +}; + +When mp is filled, the device maps are fed to the kernel +through libdevmapper. Stale paths (failed TUR) are +discarded. + +Notes : +======= + +o make sure you have enough /dev/sg* nodes + (/dev/MAKEDEV if necesary) + +o path coalescing relies on a path unique id being found. + This unique id, lacking a standard method, is vendor + specific. A switch function (get_unique_id) is present + and an example function is provided for storageworks + arrays (get_storageworks_wwid). Feel free to enrich + with hardware you have at hand :) + +o Something goes wrong with sd.o, qla2200 & dm-mod + refcounting : I can't unload these modules after exec. + +o The kernel does NOT manage properly ghosts paths + with StorageWorks HW. Seems nobody cares after a load + of posts to linux-scsi. + +o 2.4.21 version of DM does not like even segment size. + if you enconter pbs with this, upgrade DM. + +Credits : +========= + +o Heavy cut'n paste from sg_utils. Thanks goes to D. + Gilbert. +o Light cut'n paste from dmsetup. Thanks Joe Thornber. +o Greg KH for the nice sysfs API. diff --git a/extras/multipath/VERSION b/extras/multipath/VERSION new file mode 100644 index 000000000..bbdeab622 --- /dev/null +++ b/extras/multipath/VERSION @@ -0,0 +1 @@ +0.0.5 diff --git a/extras/multipath/main.c b/extras/multipath/main.c new file mode 100644 index 000000000..0ed74c52a --- /dev/null +++ b/extras/multipath/main.c @@ -0,0 +1,741 @@ +/* + * Soft: Description here... + * + * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ + * + * Author: Copyright (C) 2003 Christophe Varoqui + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "main.h" + +static int +do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, + void *resp, int mx_resp_len, int noisy) +{ + int res; + unsigned char inqCmdBlk[INQUIRY_CMDLEN] = + { INQUIRY_CMD, 0, 0, 0, 0, 0 }; + unsigned char sense_b[SENSE_BUFF_LEN]; + struct sg_io_hdr io_hdr; + + if (cmddt) + inqCmdBlk[1] |= 2; + if (evpd) + inqCmdBlk[1] |= 1; + inqCmdBlk[2] = (unsigned char) pg_op; + inqCmdBlk[4] = (unsigned char) mx_resp_len; + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (inqCmdBlk); + io_hdr.mx_sb_len = sizeof (sense_b); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = mx_resp_len; + io_hdr.dxferp = resp; + io_hdr.cmdp = inqCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = DEF_TIMEOUT; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + perror("SG_IO (inquiry) error"); + return -1; + } + res = sg_err_category3(&io_hdr); + switch (res) { + case SG_ERR_CAT_CLEAN: + case SG_ERR_CAT_RECOVERED: + return 0; + default: + return -1; + } +} + +static int +do_tur(int fd) +{ + unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; + struct sg_io_hdr io_hdr; + unsigned char sense_buffer[32]; + + memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof (turCmdBlk); + io_hdr.mx_sb_len = sizeof (sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = 20000; + io_hdr.pack_id = 0; + if (ioctl(fd, SG_IO, &io_hdr) < 0) { + close(fd); + return 0; + } + if (io_hdr.info & SG_INFO_OK_MASK) { + return 0; + } + return 1; +} + +static void +sprint_wwid(char * buff, const char * str) +{ + int i; + const char *p; + char *cursor; + unsigned char c; + + p = str; + cursor = buff; + for (i = 0; i <= WWID_SIZE / 2 - 1; i++) { + c = *p++; + sprintf(cursor, "%.2x", (int) (unsigned char) c); + cursor += 2; + } + buff[WWID_SIZE - 1] = '\0'; +} + +static int +get_lun_strings(int fd, struct path * mypath) +{ + char buff[36]; + + if (0 == do_inq(fd, 0, 0, 0, buff, 36, 1)) { + memcpy(mypath->vendor_id, &buff[8], 8); + memcpy(mypath->product_id, &buff[16], 16); + memcpy(mypath->rev, &buff[32], 4); + return 1; + } + return 0; +} + +/* +static int +get_serial (int fd, char * str) +{ + char buff[MX_ALLOC_LEN + 1]; + int len; + + if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) { + len = buff[3]; + if (len > 0) { + memcpy(str, buff + 4, len); + buff[len] = '\0'; + } + return 1; + } + return 0; +} +*/ + +/* hardware vendor specifics : add support for new models below */ +static int +get_storageworks_wwid(int fd, char *str) +{ + char buff[64]; + + if (0 == do_inq(fd, 0, 1, 0x83, buff, sizeof (buff), 1)) { + sprint_wwid(str, &buff[8]); + return 1; + } + return 0; +} + +/* White list switch */ +static int +get_unique_id(int fd, struct path * mypath) +{ + if (strncmp(mypath->product_id, "HSV110 (C)COMPAQ", 16) == 0 || + strncmp(mypath->product_id, "HSG80 ", 16) == 0) { + get_storageworks_wwid(fd, mypath->wwid); + return 0; + } + + return 1; +} + +static void +basename(char * str1, char * str2) +{ + char *p = str1 + (strlen(str1) - 1); + + while (*--p != '/') + continue; + strcpy(str2, ++p); +} + +static int +get_all_paths_sysfs(struct env * conf, struct path * all_paths) +{ + int k=0; + int sg_fd; + struct sysfs_directory * sdir; + struct sysfs_directory * devp; + struct sysfs_dlink * linkp; + char buff[FILE_NAME_SIZE]; + + char block_path[FILE_NAME_SIZE]; + + sprintf(block_path, "%s/block", conf->sysfs_path); + sdir = sysfs_open_directory(block_path); + sysfs_read_directory(sdir); + devp = sdir->subdirs; + while (devp != NULL) { + sysfs_read_directory(devp); + linkp = devp->links; + while (linkp != NULL) { + if (!strncmp(linkp->name, "device", 6)) + break; + linkp = linkp->next; + } + if (linkp == NULL) { + devp = devp->next; + continue; + } + + basename(devp->path, buff); + sprintf(all_paths[k].sg_dev, "/dev/%s", buff); + strcpy(all_paths[k].dev, all_paths[k].sg_dev); + if ((sg_fd = open(all_paths[k].sg_dev, O_RDONLY)) < 0) { + devp = devp->next; + continue; + } + get_lun_strings(sg_fd, &all_paths[k]); + get_unique_id(sg_fd, &all_paths[k]); + all_paths[k].state = do_tur(sg_fd); + close(sg_fd); + basename(linkp->target->path, buff); + sscanf(buff, "%i:%i:%i:%i", + &all_paths[k].sg_id.host_no, + &all_paths[k].sg_id.channel, + &all_paths[k].sg_id.scsi_id, + &all_paths[k].sg_id.lun); + k++; + devp = devp->next; + } + sysfs_close_directory(sdir); + + return 0; +} + +static int +get_all_paths_nosysfs(struct env * conf, struct path * all_paths, + struct scsi_dev * all_scsi_ids) +{ + int k, i, sg_fd; + char buff[FILE_NAME_SIZE]; + char file_name[FILE_NAME_SIZE]; + + for (k = 0; k < conf->max_devs; k++) { + strcpy(file_name, "/dev/sg"); + sprintf(buff, "%d", k); + strncat(file_name, buff, FILE_NAME_SIZE); + strcpy(all_paths[k].sg_dev, file_name); + if ((sg_fd = open(file_name, O_RDONLY)) < 0) + continue; + get_lun_strings(sg_fd, &all_paths[k]); + get_unique_id(sg_fd, &all_paths[k]); + all_paths[k].state = do_tur(sg_fd); + if (0 > ioctl(sg_fd, SG_GET_SCSI_ID, &(all_paths[k].sg_id))) + printf("device %s failed on sg ioctl, skip\n", + file_name); + + close(sg_fd); + + for (i = 0; i < conf->max_devs; i++) { + if ((all_paths[k].sg_id.host_no == + all_scsi_ids[i].host_no) + && (all_paths[k].sg_id.scsi_id == + (all_scsi_ids[i].scsi_id.dev_id & 0xff)) + && (all_paths[k].sg_id.lun == + ((all_scsi_ids[i].scsi_id.dev_id >> 8) & 0xff)) + && (all_paths[k].sg_id.channel == + ((all_scsi_ids[i].scsi_id. + dev_id >> 16) & 0xff))) { + strcpy(all_paths[k].dev, all_scsi_ids[i].dev); + break; + } + } + } + return 0; +} + +static int +get_all_scsi_ids(struct env * conf, struct scsi_dev * all_scsi_ids) +{ + int k, big, little, res, host_no, fd; + char buff[64]; + char fname[FILE_NAME_SIZE]; + struct scsi_idlun my_scsi_id; + + for (k = 0; k < conf->max_devs; k++) { + strcpy(fname, "/dev/sd"); + if (k < 26) { + buff[0] = 'a' + (char) k; + buff[1] = '\0'; + strcat(fname, buff); + } else if (k <= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */ + big = k / 26; + little = k - (26 * big); + big = big - 1; + + buff[0] = 'a' + (char) big; + buff[1] = 'a' + (char) little; + buff[2] = '\0'; + strcat(fname, buff); + } else + strcat(fname, "xxxx"); + + if ((fd = open(fname, O_RDONLY)) < 0) + continue; + + res = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &my_scsi_id); + if (res < 0) { + close(fd); + printf("Could not get scsi idlun\n"); + continue; + } + + res = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no); + if (res < 0) { + close(fd); + printf("Could not get host_no\n"); + continue; + } + + close(fd); + + strcpy(all_scsi_ids[k].dev, fname); + all_scsi_ids[k].scsi_id = my_scsi_id; + all_scsi_ids[k].host_no = host_no; + } + return 0; +} + +/* print_path style */ +#define ALL 0 +#define NOWWID 1 + +static void +print_path(struct path * all_paths, int k, int style) +{ + if (style != NOWWID) + printf("%s ", all_paths[k].wwid); + else + printf(" \\_"); + printf("(%i %i %i %i) ", + all_paths[k].sg_id.host_no, + all_paths[k].sg_id.channel, + all_paths[k].sg_id.scsi_id, all_paths[k].sg_id.lun); + printf("%s ", all_paths[k].sg_dev); + printf("op:%i ", all_paths[k].state); + printf("%s ", all_paths[k].dev); + printf("[%.16s]\n", all_paths[k].product_id); +} + +static void +print_all_path(struct env * conf, struct path * all_paths) +{ + int k; + char empty_buff[WWID_SIZE]; + + memset(empty_buff, 0, WWID_SIZE); + for (k = 0; k < conf->max_devs; k++) { + if (memcmp(empty_buff, all_paths[k].wwid, WWID_SIZE) == 0) + continue; + print_path(all_paths, k, ALL); + } +} + +static void +print_all_mp(struct path * all_paths, struct multipath * mp, int nmp) +{ + int k, i; + + for (k = 0; k <= nmp; k++) { + printf("%s\n", mp[k].wwid); + for (i = 0; i <= mp[k].npaths; i++) + print_path(all_paths, PINDEX(k,i), NOWWID); + } +} + +static int +coalesce_paths(struct env * conf, struct multipath * mp, + struct path * all_paths) +{ + int k, i, nmp, np, already_done; + char empty_buff[WWID_SIZE]; + + nmp = -1; + already_done = 0; + memset(empty_buff, 0, WWID_SIZE); + + for (k = 0; k < conf->max_devs - 1; k++) { + /* skip this path if no unique id has been found */ + if (memcmp(empty_buff, all_paths[k].wwid, WWID_SIZE) == 0) + continue; + np = 0; + + for (i = 0; i <= nmp; i++) { + if (0 == strcmp(mp[i].wwid, all_paths[k].wwid)) + already_done = 1; + } + + if (already_done) { + already_done = 0; + continue; + } + + nmp++; + strcpy(mp[nmp].wwid, all_paths[k].wwid); + PINDEX(nmp,np) = k; + + for (i = k + 1; i < conf->max_devs; i++) { + if (0 == strcmp(all_paths[k].wwid, all_paths[i].wwid)) { + np++; + PINDEX(nmp,np) = i; + mp[nmp].npaths = np; + } + } + } + return nmp; +} + +static long +get_disk_size (struct env * conf, char * dev) { + long size; + int fd; + char attr_path[FILE_NAME_SIZE]; + char buff[FILE_NAME_SIZE]; + char basedev[FILE_NAME_SIZE]; + + if(conf->with_sysfs) { + basename(dev, basedev); + sprintf(attr_path, "%s/block/%s/size", + conf->sysfs_path, basedev); + if (0 > sysfs_read_attribute_value(attr_path, buff, + FILE_NAME_SIZE * sizeof(char))) + return -1; + size = atoi(buff); + return size; + } else { + if ((fd = open(dev, O_RDONLY)) < 0) + return -1; + if(!ioctl(fd, BLKGETSIZE, &size)) + return size; + } + return -1; +} + +static int +make_dm_node(char * str) +{ + int r = 0; + dev_t dev; + char buff[FILE_NAME_SIZE]; + int major, minor; + struct dm_names * names; + unsigned next = 0; + struct dm_task *dmt; + + if (!(dmt = dm_task_create(DM_DEVICE_LIST))) + return 0; + + if (!dm_task_run(dmt)) + goto out; + + if (!(names = dm_task_get_names(dmt))) + goto out; + + if (!names->dev) { + r = 1; + goto out; + } + + do { + if (0 == strcmp(names->name, str)) + break; + next = names->next; + names = (void *) names + next; + } while (next); + + major = (int) MAJOR(names->dev); + minor = (int) MINOR(names->dev); + + dev = major << sizeof(dev_t); + dev = dev | minor; + sprintf(buff, "/dev/mapper/%s", str); + unlink(buff); + mknod(buff, 0600 | S_IFBLK, dev); + + out: + dm_task_destroy(dmt); + return r; + +} + +/* future use ? +static int +del_map(char * str) { + struct dm_task *dmt; + + if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) + return 0; + if (!dm_task_set_name(dmt, str)) + goto delout; + if (!dm_task_run(dmt)) + goto delout; + + printf("Deleted device map : %s\n", str); + + delout: + dm_task_destroy(dmt); + return 1; +} +*/ + +static int +add_map(struct env * conf, struct path * all_paths, + struct multipath * mp, int index, int op) +{ + char params[255]; + char * params_p; + struct dm_task *dmt; + int i, np; + long size = -1; + + if (!(dmt = dm_task_create(op))) + return 0; + + if (!dm_task_set_name(dmt, mp[index].wwid)) + goto addout; + params_p = ¶ms[0]; + + np = 0; + for (i=0; i<=mp[index].npaths; i++) { + if ((1 == all_paths[PINDEX(index,i)].state) && + (0 == all_paths[PINDEX(index,i)].sg_id.scsi_type)) + np++; + } + if (np == 0) + goto addout; + params_p += sprintf(params_p, "%i 32", np); + + for (i=0; i<=mp[index].npaths; i++) { + if (( 0 == all_paths[PINDEX(index,i)].state) || + (0 != all_paths[PINDEX(index,i)].sg_id.scsi_type)) + continue; + if (size < 0) + size = get_disk_size(conf, all_paths[PINDEX(index,0)].dev); + params_p += sprintf(params_p, " %s %i", + all_paths[PINDEX(index,i)].dev, 0); + } + + if (size < 0) + goto addout; + + if (!conf->quiet) { + if (op == DM_DEVICE_RELOAD) + printf("U|"); + if (op == DM_DEVICE_CREATE) + printf("N|"); + printf("%s : 0 %li %s %s\n", + mp[index].wwid, size, DM_TARGET, params); + } + + if (!dm_task_add_target(dmt, 0, size, DM_TARGET, params)) + goto addout; + + if (!dm_task_run(dmt)) + goto addout; + + + make_dm_node(mp[index].wwid); + + addout: + dm_task_destroy(dmt); + return 1; +} + +/* +static int +get_table(const char * str) +{ + int r = 0; + struct dm_task *dmt; + void *next = NULL; + uint64_t start, length; + char *target_type = NULL; + char *params; + + if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) + return 0; + + if (!dm_task_set_name(dmt, str)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + do { + next = dm_get_next_target(dmt, next, &start, &length, + &target_type, ¶ms); + if (target_type) { + printf("%" PRIu64 " %" PRIu64 " %s %s\n", + start, length, target_type, params); + } + } while (next); + + r = 1; + + out: + dm_task_destroy(dmt); + return r; + +} +*/ + +static int +map_present(char * str) +{ + int r = 0; + struct dm_task *dmt; + struct dm_names *names; + unsigned next = 0; + + if (!(dmt = dm_task_create(DM_DEVICE_LIST))) + return 0; + + if (!dm_task_run(dmt)) + goto out; + + if (!(names = dm_task_get_names(dmt))) + goto out; + + if (!names->dev) { + goto out; + } + + do { + if (0 == strcmp(names->name, str)) + r = 1; + next = names->next; + names = (void *) names + next; + } while (next); + + out: + dm_task_destroy(dmt); + return r; +} + +static void +usage(char * progname) +{ + fprintf(stderr, VERSION_STRING); + fprintf(stderr, "Usage: %s [-v|-q] [-d] [-m max_devs]\n", progname); + fprintf(stderr, "\t-v\t\tverbose, print all paths and multipaths\n"); + fprintf(stderr, "\t-q\t\tquiet, no output at all\n"); + fprintf(stderr, "\t-d\t\tdry run, do not create or update devmaps\n"); + fprintf(stderr, "\t-m max_devs\tscan {max_devs} devices at most\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct multipath * mp; + struct path * all_paths; + struct scsi_dev * all_scsi_ids; + struct env conf; + int i, k, nmp; + + /* Default behaviour */ + conf.max_devs = MAX_DEVS; + conf.dry_run = 0; /* 1 == Do not Create/Update devmaps */ + conf.verbose = 0; /* 1 == Print all_paths and mp */ + conf.quiet = 0; /* 1 == Do not even print devmaps */ + conf.with_sysfs = 0; /* Default to compat / suboptimal behaviour */ + + /* kindly provided by libsysfs */ + if (0 == sysfs_get_mnt_path(conf.sysfs_path, FILE_NAME_SIZE)) + conf.with_sysfs = 1; + + for (i = 1; i < argc; ++i) { + if (0 == strcmp("-v", argv[i])) { + if (conf.quiet == 1) + usage(argv[0]); + conf.verbose = 1; + } else if (0 == strcmp("-m", argv[i])) { + conf.max_devs = atoi(argv[++i]); + if (conf.max_devs < 2) + usage(argv[0]); + } else if (0 == strcmp("-q", argv[i])) { + if (conf.verbose == 1) + usage(argv[0]); + conf.quiet = 1; + } else if (0 == strcmp("-d", argv[i])) + conf.dry_run = 1; + else if (*argv[i] == '-') { + fprintf(stderr, "Unknown switch: %s\n", argv[i]); + usage(argv[0]); + } else if (*argv[i] != '-') { + fprintf(stderr, "Unknown argument\n"); + usage(argv[0]); + } + + } + + /* dynamic allocations */ + mp = malloc(conf.max_devs * sizeof(struct multipath)); + all_paths = malloc(conf.max_devs * sizeof(struct path)); + all_scsi_ids = malloc(conf.max_devs * sizeof(struct scsi_dev)); + if (mp == NULL || all_paths == NULL || all_scsi_ids == NULL) + exit(1); + + if (!conf.with_sysfs) { + get_all_scsi_ids(&conf, all_scsi_ids); + get_all_paths_nosysfs(&conf, all_paths, all_scsi_ids); + } else { + get_all_paths_sysfs(&conf, all_paths); + } + nmp = coalesce_paths(&conf, mp, all_paths); + + if (conf.verbose) { + print_all_path(&conf, all_paths); + printf("\n"); + print_all_mp(all_paths, mp, nmp); + printf("\n"); + } + + if (conf.dry_run) + exit(0); + + for (k=0; k<=nmp; k++) { + if (map_present(mp[k].wwid)) { + add_map(&conf, all_paths, mp, k, DM_DEVICE_RELOAD); + } else { + add_map(&conf, all_paths, mp, k, DM_DEVICE_CREATE); + } + } + exit(0); +} diff --git a/extras/multipath/main.h b/extras/multipath/main.h new file mode 100644 index 000000000..43a24ac7b --- /dev/null +++ b/extras/multipath/main.h @@ -0,0 +1,111 @@ +/* + * Soft: Description here... + * + * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ + * + * Author: Copyright (C) 2003 Christophe Varoqui + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _MAIN_H +#define _MAIN_H + +/* local includes */ +#include "sg_include.h" +#include "sg_err.h" + +/* global defs */ +#define WWID_SIZE 33 +#define MAX_DEVS 128 +#define MAX_MP MAX_DEVS / 2 +#define MAX_MP_PATHS MAX_DEVS / 4 +#define FILE_NAME_SIZE 256 +#define INQUIRY_CMDLEN 6 +#define INQUIRY_CMD 0x12 +#define SENSE_BUFF_LEN 32 +#define DEF_TIMEOUT 60000 +#define EBUFF_SZ 256 +#define TUR_CMD_LEN 6 +#define MX_ALLOC_LEN 255 +#define BLKGETSIZE _IO(0x12,96) +#define DM_TARGET "striped" + +#define PINDEX(x,y) mp[(x)].pindex[(y)] + +/* global types */ +struct scsi_idlun { + int dev_id; + int host_unique_id; + int host_no; +}; + +struct sg_id { + int host_no; + int channel; + int scsi_id; + int lun; + int scsi_type; + short h_cmd_per_lun; + short d_queue_depth; + int unused1; + int unused2; +}; + +struct scsi_dev { + char dev[FILE_NAME_SIZE]; + struct scsi_idlun scsi_id; + int host_no; +}; + +struct path { + char dev[FILE_NAME_SIZE]; + char sg_dev[FILE_NAME_SIZE]; + struct scsi_idlun scsi_id; + struct sg_id sg_id; + int state; + char wwid[WWID_SIZE]; + char vendor_id[8]; + char product_id[16]; + char rev[4]; +}; + +struct multipath { + char wwid[WWID_SIZE]; + int npaths; + int pindex[MAX_MP_PATHS]; +}; + +struct env { + int max_devs; + int verbose; + int quiet; + int dry_run; + int with_sysfs; + char sysfs_path[FILE_NAME_SIZE]; +}; + +/* Build version */ +#define PROG "multipath" + +#define VERSION_CODE 0x000005 +#define DATE_CODE 0x120903 + +#define MULTIPATH_VERSION(version) \ + (version >> 16) & 0xFF, \ + (version >> 8) & 0xFF, \ + version & 0xFF + +#define VERSION_STRING PROG" v%d.%d.%d (%.2d/%.2d, 20%.2d)\n", \ + MULTIPATH_VERSION(VERSION_CODE), \ + MULTIPATH_VERSION(DATE_CODE) + +#endif diff --git a/extras/multipath/sg_err.c b/extras/multipath/sg_err.c new file mode 100644 index 000000000..8a332cd8c --- /dev/null +++ b/extras/multipath/sg_err.c @@ -0,0 +1,1197 @@ +#include +#include +#include +#include "sg_include.h" +#include "sg_err.h" + + +/* This file is a huge cut, paste and hack from linux/drivers/scsi/constant.c +* which I guess was written by: +* Copyright (C) 1993, 1994, 1995 Eric Youngdale + +* The rest of this is: +* Copyright (C) 1999 - 2003 D. Gilbert +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* ASCII values for a number of symbolic constants, printing functions, etc. +* +* Some of the tables have been updated for SCSI 2. +* Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 12 18 March 2003) +* +* Version 0.91 (20030529) +* sense key specific field (bytes 15-17) decoding [Trent Piepho] +*/ + +#define OUTP stderr + +static const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, + 16, 12, 10, 10 }; + +#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7] + +static const char unknown[] = "UNKNOWN"; + +static const char * group_0_commands[] = { +/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense", +/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks", +/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown, +/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry", +/* 13-16 */ "Verify", "Recover Buffered Data", "Mode Select", "Reserve", +/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit", +/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic", +/* 1e-1f */ "Prevent/Allow Medium Removal", unknown, +}; + + +static const char *group_1_commands[] = { +/* 20-23 */ unknown, unknown, unknown, "Read Format capacities", +/* 24-28 */ "Set window", "Read Capacity", + unknown, unknown, "Read (10)", +/* 29-2d */ "Read Generation", "Write (10)", "Seek (10)", "Erase", + "Read updated block", +/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal", +/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", +/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data", +/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer", + "Read Buffer", +/* 3d-3f */ "Update Block", "Read Long", "Write Long", +}; + +static const char *group_2_commands[] = { +/* 40-41 */ "Change Definition", "Write Same", +/* 42-48 */ "Read sub-channel", "Read TOC", "Read header", + "Play audio (10)", "Get configuration", "Play audio msf", + "Play audio track/index", +/* 49-4f */ "Play track relative (10)", "Get event status notification", + "Pause/resume", "Log Select", "Log Sense", "Stop play/scan", + unknown, +/* 50-55 */ "Xdwrite", "Xpwrite, Read disk info", "Xdread, Read track info", + "Reserve track", "Send OPC onfo", "Mode Select (10)", +/* 56-5b */ "Reserve (10)", "Release (10)", "Repair track", "Read master cue", + "Mode Sense (10)", "Close track/session", +/* 5c-5f */ "Read buffer capacity", "Send cue sheet", "Persistent reserve in", + "Persistent reserve out", +}; + +/* The following are 16 byte commands in group 4 */ +static const char *group_4_commands[] = { +/* 80-84 */ "Xdwrite (16)", "Rebuild (16)", "Regenerate (16)", "Extended copy", + "Receive copy results", +/* 85-89 */ "Memory Export In (16)", "Access control in", "Access control out", + "Read (16)", "Memory Export Out (16)", +/* 8a-8f */ "Write (16)", unknown, "Read attributes", "Write attributes", + "Write and verify (16)", "Verify (16)", +/* 90-94 */ "Pre-fetch (16)", "Synchronize cache (16)", + "Lock/unlock cache (16)", "Write same (16)", unknown, +/* 95-99 */ unknown, unknown, unknown, unknown, unknown, +/* 9a-9f */ unknown, unknown, unknown, unknown, "Service action in", + "Service action out", +}; + +/* The following are 12 byte commands in group 5 */ +static const char *group_5_commands[] = { +/* a0-a5 */ "Report luns", "Blank", "Send event", "Maintenance (in)", + "Maintenance (out)", "Move medium/play audio(12)", +/* a6-a9 */ "Exchange medium", "Move medium attached", "Read(12)", + "Play track relative(12)", +/* aa-ae */ "Write(12)", unknown, "Erase(12), Get Performance", + "Read DVD structure", "Write and verify(12)", +/* af-b1 */ "Verify(12)", "Search data high(12)", "Search data equal(12)", +/* b2-b4 */ "Search data low(12)", "Set limits(12)", + "Read element status attached", +/* b5-b6 */ "Request volume element address", "Send volume tag, set streaming", +/* b7-b9 */ "Read defect data(12)", "Read element status", "Read CD msf", +/* ba-bc */ "Redundancy group (in), Scan", + "Redundancy group (out), Set cd-rom speed", "Spare (in), Play cd", +/* bd-bf */ "Spare (out), Mechanism status", "Volume set (in), Read cd", + "Volume set (out), Send DVD structure", +}; + + +#define group(opcode) (((opcode) >> 5) & 7) + +#define RESERVED_GROUP 0 +#define VENDOR_GROUP 1 + +static const char **commands[] = { + group_0_commands, group_1_commands, group_2_commands, + (const char **) RESERVED_GROUP, group_4_commands, + group_5_commands, (const char **) VENDOR_GROUP, + (const char **) VENDOR_GROUP +}; + +static const char reserved[] = "RESERVED"; +static const char vendor[] = "VENDOR SPECIFIC"; + +static void print_opcode(int opcode) { + const char **table = commands[ group(opcode) ]; + + switch ((unsigned long) table) { + case RESERVED_GROUP: + fprintf(OUTP, "%s(0x%02x)", reserved, opcode); + break; + case VENDOR_GROUP: + fprintf(OUTP, "%s(0x%02x)", vendor, opcode); + break; + default: + fprintf(OUTP, "%s",table[opcode & 0x1f]); + break; + } +} + +void sg_print_command (const unsigned char * command) { + int k, s; + print_opcode(command[0]); + fprintf(OUTP, " ["); + for (k = 0, s = COMMAND_SIZE(command[0]); k < s; ++k) + fprintf(OUTP, "%02x ", command[k]); + fprintf(OUTP, "]\n"); +} + +void sg_print_status(int masked_status) +{ + int scsi_status = (masked_status << 1) & 0x7e; + + sg_print_scsi_status(scsi_status); +} + +void sg_print_scsi_status(int scsi_status) +{ + const char * ccp; + + scsi_status &= 0x7e; /* sanitize as much as possible */ + switch (scsi_status) { + case 0: ccp = "Good"; break; + case 0x2: ccp = "Check Condition"; break; + case 0x4: ccp = "Condition Met"; break; + case 0x8: ccp = "Busy"; break; + case 0x10: ccp = "Intermediate"; break; + case 0x14: ccp = "Intermediate-Condition Met"; break; + case 0x18: ccp = "Reservation Conflict"; break; + case 0x22: ccp = "Command Terminated (obsolete)"; break; + case 0x28: ccp = "Task set Full"; break; + case 0x30: ccp = "ACA Active"; break; + case 0x40: ccp = "Task Aborted"; break; + default: ccp = "Unknown status"; break; + } + fprintf(OUTP, "%s ", ccp); +} + +/* In brackets is the related SCSI document (see www.t10.org) with the */ +/* peripheral device type after the colon */ +/* No programmatic use is made of these flags currently */ +#define D 0x0001 /* DIRECT ACCESS DEVICE (disk) [SBC-2: 0] */ +#define T 0x0002 /* SEQUENTIAL ACCESS DEVICE (tape) [SSC: 1] */ +#define L 0x0004 /* PRINTER DEVICE [SSC: 2] */ +#define P 0x0008 /* PROCESSOR DEVICE [SPC-2: 3] */ +#define W 0x0010 /* WRITE ONCE READ MULTIPLE DEVICE [SBC-2: 4] */ +#define R 0x0020 /* CD/DVD DEVICE [MMC-2: 5] */ +#define S 0x0040 /* SCANNER DEVICE [SCSI-2 (obsolete): 6] */ +#define O 0x0080 /* OPTICAL MEMORY DEVICE [SBC-2: 7] */ +#define M 0x0100 /* MEDIA CHANGER DEVICE [SMC-2: 8] */ +#define C 0x0200 /* COMMUNICATION DEVICE [SCSI-2 (obsolete): 9] */ +#define A 0x0400 /* ARRAY STORAGE [SCC-2: 12] */ +#define E 0x0800 /* ENCLOSURE SERVICES DEVICE [SES: 13] */ +#define B 0x1000 /* SIMPLIFIED DIRECT ACCESS DEVICE [RBC: 14] */ +#define K 0x2000 /* OPTICAL CARD READER/WRITER DEVICE [OCRW: 15] */ + +#define SC_ALL_DEVS ( D|T|L|P|W|R|S|O|M|C|A|E|B|K ) + +/* oft used strings are encoded using ASCII codes 0x1 to 0x1f . */ +/* This is to save space. This encoding should be UTF-8 and */ +/* UTF-16 friendly. */ +#define SC_AUDIO_PLAY_OPERATION "\x1" +#define SC_LOGICAL_UNIT "\x2" +#define SC_NOT_READY "\x3" +#define SC_OPERATION "\x4" +#define SC_IN_PROGRESS "\x5" +#define SC_HARDWARE_IF "\x6" +#define SC_CONTROLLER_IF "\x7" +#define SC_DATA_CHANNEL_IF "\x8" +#define SC_SERVO_IF "\x9" +#define SC_SPINDLE_IF "\xa" +#define SC_FIRMWARE_IF "\xb" +#define SC_RECOVERED_DATA "\xc" +#define SC_ERROR_RATE_TOO_HIGH "\xd" +#define SC_TIMES_TOO_HIGH "\xe" + + +struct error_info{ + unsigned char code1, code2; + unsigned short int devices; + const char * text; +}; + +struct error_info2{ + unsigned char code1, code2_min, code2_max; + unsigned short int devices; + const char * text; +}; + +static struct error_info2 additional2[] = +{ + {0x40,0x00,0x7f,D,"Ram failure (%x)"}, + {0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"}, + {0x41,0x00,0xff,D,"Data path failure (%x)"}, + {0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"}, + {0, 0, 0, 0, NULL} +}; + +static struct error_info additional[] = +{ + {0x00,0x00,SC_ALL_DEVS,"No additional sense information"}, + {0x00,0x01,T,"Filemark detected"}, + {0x00,0x02,T|S,"End-of-partition/medium detected"}, + {0x00,0x03,T,"Setmark detected"}, + {0x00,0x04,T|S,"Beginning-of-partition/medium detected"}, + {0x00,0x05,T|L|S,"End-of-data detected"}, + {0x00,0x06,SC_ALL_DEVS,"I/O process terminated"}, + {0x00,0x11,R,SC_AUDIO_PLAY_OPERATION SC_IN_PROGRESS}, + {0x00,0x12,R,SC_AUDIO_PLAY_OPERATION "paused"}, + {0x00,0x13,R,SC_AUDIO_PLAY_OPERATION "successfully completed"}, + {0x00,0x14,R,SC_AUDIO_PLAY_OPERATION "stopped due to error"}, + {0x00,0x15,R,"No current audio status to return"}, + {0x00,0x16,SC_ALL_DEVS,SC_OPERATION SC_IN_PROGRESS}, + {0x00,0x17,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning requested"}, + {0x00,0x18,T,"Erase" SC_OPERATION SC_IN_PROGRESS}, + {0x00,0x19,T,"Locate" SC_OPERATION SC_IN_PROGRESS}, + {0x00,0x1a,T,"Rewind" SC_OPERATION SC_IN_PROGRESS}, + {0x00,0x1b,T,"Set capacity" SC_OPERATION SC_IN_PROGRESS}, + {0x00,0x1c,T,"Verify" SC_OPERATION SC_IN_PROGRESS}, + {0x01,0x00,D|W|O|B|K,"No index/sector signal"}, + {0x02,0x00,D|W|R|O|M|B|K,"No seek complete"}, + {0x03,0x00,D|T|L|W|S|O|B|K,"Peripheral device write fault"}, + {0x03,0x01,T,"No write current"}, + {0x03,0x02,T,"Excessive write errors"}, + {0x04,0x00,SC_ALL_DEVS,SC_LOGICAL_UNIT SC_NOT_READY "cause not reportable"}, + {0x04,0x01,SC_ALL_DEVS,SC_LOGICAL_UNIT "is" SC_IN_PROGRESS + "of becoming ready"}, + {0x04,0x02,SC_ALL_DEVS,SC_LOGICAL_UNIT SC_NOT_READY + "initializing cmd. required"}, + {0x04,0x03,SC_ALL_DEVS,SC_LOGICAL_UNIT SC_NOT_READY + "manual intervention required"}, + {0x04,0x04,D|T|L|R|O|B,SC_LOGICAL_UNIT SC_NOT_READY "format" SC_IN_PROGRESS}, + {0x04,0x05,D|T|W|O|M|C|A|B|K,SC_LOGICAL_UNIT SC_NOT_READY + "rebuild" SC_IN_PROGRESS}, + {0x04,0x06,D|T|W|O|M|C|A|B|K,SC_LOGICAL_UNIT SC_NOT_READY + "recalculation" SC_IN_PROGRESS}, + {0x04,0x07,SC_ALL_DEVS,SC_LOGICAL_UNIT SC_NOT_READY + SC_OPERATION SC_IN_PROGRESS}, + {0x04,0x08,R,SC_LOGICAL_UNIT SC_NOT_READY "long write" SC_IN_PROGRESS}, + {0x04,0x09,SC_ALL_DEVS,SC_LOGICAL_UNIT SC_NOT_READY "self-test" + SC_IN_PROGRESS}, + {0x04,0x0a,SC_ALL_DEVS,SC_LOGICAL_UNIT + "not accessible, asymmetric access state transition"}, + {0x04,0x0b,SC_ALL_DEVS,SC_LOGICAL_UNIT + "not accessible, target port in standby state"}, + {0x04,0x0c,SC_ALL_DEVS,SC_LOGICAL_UNIT + "not accessible, target port in unavailable state"}, + {0x04,0x10,SC_ALL_DEVS,SC_LOGICAL_UNIT SC_NOT_READY + "auxiliary memory not accessible"}, + {0x05,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,SC_LOGICAL_UNIT + "does not respond to selection"}, + {0x06,0x00,D|W|R|O|M|B|K,"No reference position found"}, + {0x07,0x00,D|T|L|W|R|S|O|M|B|K,"Multiple peripheral devices selected"}, + {0x08,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,SC_LOGICAL_UNIT "communication failure"}, + {0x08,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,SC_LOGICAL_UNIT + "communication time-out"}, + {0x08,0x02,D|T|L|W|R|S|O|M|C|A|E|B|K,SC_LOGICAL_UNIT + "communication parity error"}, + {0x08,0x03,D|T|R|O|M|B|K,SC_LOGICAL_UNIT + "communication CRC error (Ultra-DMA/32)"}, + {0x08,0x04,D|T|L|P|W|R|S|O|C|K,"Unreachable copy target"}, + {0x09,0x00,D|T|W|R|O|B,"Track following error"}, + {0x09,0x01,W|R|O|K,"Tracking servo failure"}, + {0x09,0x02,W|R|O|K,"Focus servo failure"}, + {0x09,0x03,W|R|O,"Spindle servo failure"}, + {0x09,0x04,D|T|W|R|O|B,"Head select fault"}, + {0x0A,0x00,SC_ALL_DEVS,"Error log overflow"}, + {0x0B,0x00,SC_ALL_DEVS,"Warning"}, + {0x0B,0x01,SC_ALL_DEVS,"Warning - specified temperature exceeded"}, + {0x0B,0x02,SC_ALL_DEVS,"Warning - enclosure degraded"}, + {0x0C,0x00,T|R|S,"Write error"}, + {0x0C,0x01,K,"Write error - recovered with auto reallocation"}, + {0x0C,0x02,D|W|O|B|K,"Write error - auto reallocation failed"}, + {0x0C,0x03,D|W|O|B|K,"Write error - recommend reassignment"}, + {0x0C,0x04,D|T|W|O|B,"Compression check miscompare error"}, + {0x0C,0x05,D|T|W|O|B,"Data expansion occurred during compression"}, + {0x0C,0x06,D|T|W|O|B,"Block not compressible"}, + {0x0C,0x07,R,"Write error - recovery needed"}, + {0x0C,0x08,R,"Write error - recovery failed"}, + {0x0C,0x09,R,"Write error - loss of streaming"}, + {0x0C,0x0A,R,"Write error - padding blocks added"}, + {0x0C,0x0B,D|T|W|R|O|M|B,"Auxiliary memory write error"}, + {0x0C,0x0C,SC_ALL_DEVS,"Write error - unexpected unsolicited data"}, + {0x0C,0x0D,SC_ALL_DEVS,"Write error - not enough unsolicited data"}, + {0x0D,0x00,D|T|L|P|W|R|S|O|C|A|K, + "Error detected by third party temporary initiator"}, + {0x0D,0x01,D|T|L|P|W|R|S|O|C|A|K, "Third party device failure"}, + {0x0D,0x02,D|T|L|P|W|R|S|O|C|A|K, "Copy target device not reachable"}, + {0x0D,0x03,D|T|L|P|W|R|S|O|C|A|K, "Incorrect copy target device"}, + {0x0D,0x04,D|T|L|P|W|R|S|O|C|A|K, "Copy target device underrun"}, + {0x0D,0x05,D|T|L|P|W|R|S|O|C|A|K, "Copy target device overrun"}, + {0x10,0x00,D|W|O|B|K,"Id CRC or ECC error"}, + {0x11,0x00,D|T|W|R|S|O|B|K,"Unrecovered read error"}, + {0x11,0x01,D|T|W|R|S|O|B|K,"Read retries exhausted"}, + {0x11,0x02,D|T|W|R|S|O|B|K,"Error too long to correct"}, + {0x11,0x03,D|T|W|S|O|B|K,"Multiple read errors"}, + {0x11,0x04,D|W|O|B|K,"Unrecovered read error - auto reallocate failed"}, + {0x11,0x05,W|R|O|B,"L-EC uncorrectable error"}, + {0x11,0x06,W|R|O|B,"CIRC unrecovered error"}, + {0x11,0x07,W|O|B,"Data re-synchronization error"}, + {0x11,0x08,T,"Incomplete block read"}, + {0x11,0x09,T,"No gap found"}, + {0x11,0x0A,D|T|O|B|K,"Miscorrected error"}, + {0x11,0x0B,D|W|O|B|K,"Unrecovered read error - recommend reassignment"}, + {0x11,0x0C,D|W|O|B|K,"Unrecovered read error - recommend rewrite the data"}, + {0x11,0x0D,D|T|W|R|O|B,"De-compression CRC error"}, + {0x11,0x0E,D|T|W|R|O|B,"Cannot decompress using declared algorithm"}, + {0x11,0x0F,R,"Error reading UPC/EAN number"}, + {0x11,0x10,R,"Error reading ISRC number"}, + {0x11,0x11,R,"Read error - loss of streaming"}, + {0x11,0x12,D|T|W|R|O|M|B,"Auxiliary memory read error"}, + {0x11,0x13,SC_ALL_DEVS,"Read error - failed retransmission request"}, + {0x12,0x00,D|W|O|B|K,"Address mark not found for id field"}, + {0x13,0x00,D|W|O|B|K,"Address mark not found for data field"}, + {0x14,0x00,D|T|L|W|R|S|O|B|K,"Recorded entity not found"}, + {0x14,0x01,D|T|W|R|O|B|K,"Record not found"}, + {0x14,0x02,T,"Filemark or setmark not found"}, + {0x14,0x03,T,"End-of-data not found"}, + {0x14,0x04,T,"Block sequence error"}, + {0x14,0x05,D|T|W|O|B|K,"Record not found - recommend reassignment"}, + {0x14,0x06,D|T|W|O|B|K,"Record not found - data auto-reallocated"}, + {0x14,0x07,T,"Locate" SC_OPERATION " failure"}, + {0x15,0x00,D|T|L|W|R|S|O|M|B|K,"Random positioning error"}, + {0x15,0x01,D|T|L|W|R|S|O|M|B|K,"Mechanical positioning error"}, + {0x15,0x02,D|T|W|R|O|B|K,"Positioning error detected by read of medium"}, + {0x16,0x00,D|W|O|B|K,"Data synchronization mark error"}, + {0x16,0x01,D|W|O|B|K,"Data sync error - data rewritten"}, + {0x16,0x02,D|W|O|B|K,"Data sync error - recommend rewrite"}, + {0x16,0x03,D|W|O|B|K,"Data sync error - data auto-reallocated"}, + {0x16,0x04,D|W|O|B|K,"Data sync error - recommend reassignment"}, + {0x17,0x00,D|T|W|R|S|O|B|K,SC_RECOVERED_DATA + "with no error correction applied"}, + {0x17,0x01,D|T|W|R|S|O|B|K,SC_RECOVERED_DATA "with retries"}, + {0x17,0x02,D|T|W|R|O|B|K,SC_RECOVERED_DATA "with positive head offset"}, + {0x17,0x03,D|T|W|R|O|B|K,SC_RECOVERED_DATA "with negative head offset"}, + {0x17,0x04,W|R|O|B,SC_RECOVERED_DATA "with retries and/or circ applied"}, + {0x17,0x05,D|W|R|O|B|K,SC_RECOVERED_DATA "using previous sector id"}, + {0x17,0x06,D|W|O|B|K,SC_RECOVERED_DATA "without ecc - data auto-reallocated"}, + {0x17,0x07,D|W|R|O|B|K,SC_RECOVERED_DATA + "without ecc - recommend reassignment"}, + {0x17,0x08,D|W|R|O|B|K,SC_RECOVERED_DATA "without ecc - recommend rewrite"}, + {0x17,0x09,D|W|R|O|B|K,SC_RECOVERED_DATA "without ecc - data rewritten"}, + {0x18,0x00,D|T|W|R|O|B|K,SC_RECOVERED_DATA "with error correction applied"}, + {0x18,0x01,D|W|R|O|B|K,SC_RECOVERED_DATA + "with error corr. & retries applied"}, + {0x18,0x02,D|W|R|O|B|K,SC_RECOVERED_DATA "- data auto-reallocated"}, + {0x18,0x03,R,SC_RECOVERED_DATA "with CIRC"}, + {0x18,0x04,R,SC_RECOVERED_DATA "with L-EC"}, + {0x18,0x05,D|W|R|O|B|K,SC_RECOVERED_DATA "- recommend reassignment"}, + {0x18,0x06,D|W|R|O|B|K,SC_RECOVERED_DATA "- recommend rewrite"}, + {0x18,0x07,D|W|O|B|K,SC_RECOVERED_DATA "with ecc - data rewritten"}, + {0x18,0x08,R,SC_RECOVERED_DATA "with linking"}, + {0x19,0x00,D|O|K,"Defect list error"}, + {0x19,0x01,D|O|K,"Defect list not available"}, + {0x19,0x02,D|O|K,"Defect list error in primary list"}, + {0x19,0x03,D|O|K,"Defect list error in grown list"}, + {0x1A,0x00,SC_ALL_DEVS,"Parameter list length error"}, + {0x1B,0x00,SC_ALL_DEVS,"Synchronous data transfer error"}, + {0x1C,0x00,D|O|B|K,"Defect list not found"}, + {0x1C,0x01,D|O|B|K,"Primary defect list not found"}, + {0x1C,0x02,D|O|B|K,"Grown defect list not found"}, + {0x1D,0x00,D|T|W|R|O|B|K,"Miscompare during verify" SC_OPERATION}, + {0x1E,0x00,D|W|O|B|K,"Recovered id with ecc correction"}, + {0x1F,0x00,D|O|K,"Partial defect list transfer"}, + {0x20,0x00,SC_ALL_DEVS,"Invalid command" SC_OPERATION " code"}, + {0x20,0x01,D|T|P|W|R|O|M|A|E|B|K, + "Access denied - initiator pending-enrolled"}, + {0x20,0x02,D|T|P|W|R|O|M|A|E|B|K,"Access denied - no access rights"}, + {0x20,0x03,D|T|P|W|R|O|M|A|E|B|K,"Access denied - no mgmt id key"}, + {0x20,0x04,T,"Illegal command while in write capable state"}, + {0x20,0x05,T,"Obsolete"}, + {0x20,0x06,T,"Illegal command while in explicit address mode"}, + {0x20,0x07,T,"Illegal command while in implicit address mode"}, + {0x20,0x08,D|T|P|W|R|O|M|A|E|B|K,"Access denied - enrollment conflict"}, + {0x20,0x09,D|T|P|W|R|O|M|A|E|B|K,"Access denied - invalid LU identifier"}, + {0x20,0x0A,D|T|P|W|R|O|M|A|E|B|K,"Access denied - invalid proxy token"}, + {0x20,0x0B,D|T|P|W|R|O|M|A|E|B|K,"Access denied - ACL LUN conflict"}, + {0x21,0x00,D|T|W|R|O|M|B|K,"Logical block address out of range"}, + {0x21,0x01,D|T|W|R|O|M|B|K,"Invalid element address"}, + {0x21,0x02,R,"Invalid address for write"}, + {0x22,0x00,D,"Illegal function (use 20 00,24 00,or 26 00)"}, + {0x24,0x00,SC_ALL_DEVS,"Invalid field in cdb"}, + {0x24,0x01,SC_ALL_DEVS,"CDB decryption error"}, + {0x25,0x00,SC_ALL_DEVS,SC_LOGICAL_UNIT "not supported"}, + {0x26,0x00,SC_ALL_DEVS,"Invalid field in parameter list"}, + {0x26,0x01,SC_ALL_DEVS,"Parameter not supported"}, + {0x26,0x02,SC_ALL_DEVS,"Parameter value invalid"}, + {0x26,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Threshold parameters not supported"}, + {0x26,0x04,SC_ALL_DEVS,"Invalid release of persistent reservation"}, + {0x26,0x05,D|T|L|P|W|R|S|O|M|C|A|B|K,"Data decryption error"}, + {0x26,0x06,D|T|L|P|W|R|S|O|C|K,"Too many target descriptors"}, + {0x26,0x07,D|T|L|P|W|R|S|O|C|K,"Unsupported target descriptor type code"}, + {0x26,0x08,D|T|L|P|W|R|S|O|C|K,"Too many segment descriptors"}, + {0x26,0x09,D|T|L|P|W|R|S|O|C|K,"Unsupported segment descriptor type code"}, + {0x26,0x0A,D|T|L|P|W|R|S|O|C|K,"Unexpected inexact segment"}, + {0x26,0x0B,D|T|L|P|W|R|S|O|C|K,"Inline data length exceeded"}, + {0x26,0x0C,D|T|L|P|W|R|S|O|C|K, + "Invalid" SC_OPERATION " for copy source or destination"}, + {0x26,0x0D,D|T|L|P|W|R|S|O|C|K,"Copy segment granularity violation"}, + {0x27,0x00,D|T|W|R|O|B|K,"Write protected"}, + {0x27,0x01,D|T|W|R|O|B|K,"Hardware write protected"}, + {0x27,0x02,D|T|W|R|O|B|K,SC_LOGICAL_UNIT "software write protected"}, + {0x27,0x03,T|R,"Associated write protect"}, + {0x27,0x04,T|R,"Persistent write protect"}, + {0x27,0x05,T|R,"Permanent write protect"}, + {0x27,0x06,R,"Conditional write protect"}, + {0x28,0x00,SC_ALL_DEVS,"Not ready to ready change, medium may have changed"}, + {0x28,0x01,D|T|W|R|O|M|B,"Import or export element accessed"}, + {0x29,0x00,SC_ALL_DEVS,"Power on, reset, or bus device reset occurred"}, + {0x29,0x01,SC_ALL_DEVS,"Power on occurred"}, + {0x29,0x02,SC_ALL_DEVS,"Scsi bus reset occurred"}, + {0x29,0x03,SC_ALL_DEVS,"Bus device reset function occurred"}, + {0x29,0x04,SC_ALL_DEVS,"Device internal reset"}, + {0x29,0x05,SC_ALL_DEVS,"Transceiver mode changed to single-ended"}, + {0x29,0x06,SC_ALL_DEVS,"Transceiver mode changed to lvd"}, + {0x29,0x07,SC_ALL_DEVS,"I_T nexus loss occurred"}, + {0x2A,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Parameters changed"}, + {0x2A,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Mode parameters changed"}, + {0x2A,0x02,D|T|L|W|R|S|O|M|C|A|E|K,"Log parameters changed"}, + {0x2A,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Reservations preempted"}, + {0x2A,0x04,D|T|L|P|W|R|S|O|M|C|A|E,"Reservations released"}, + {0x2A,0x05,D|T|L|P|W|R|S|O|M|C|A|E,"Registrations preempted"}, + {0x2A,0x06,SC_ALL_DEVS,"Asymmetric access state changed"}, + {0x2A,0x07,SC_ALL_DEVS,"Implicit asymmetric access state transition failed"}, + {0x2B,0x00,D|T|L|P|W|R|S|O|C|K, + "Copy cannot execute since host cannot disconnect"}, + {0x2C,0x00,SC_ALL_DEVS,"Command sequence error"}, + {0x2C,0x01,S,"Too many windows specified"}, + {0x2C,0x02,S,"Invalid combination of windows specified"}, + {0x2C,0x03,R,"Current program area is not empty"}, + {0x2C,0x04,R,"Current program area is empty"}, + {0x2C,0x05,B,"Illegal power condition request"}, + {0x2C,0x06,R,"Persistent prevent conflict"}, + {0x2C,0x07,SC_ALL_DEVS,"Previous busy status"}, + {0x2C,0x08,SC_ALL_DEVS,"Previous task set full status"}, + {0x2C,0x09,D|T|L|P|W|R|S|O|M|E|B|K,"Previous reservation conflict status"}, + {0x2D,0x00,T,"Overwrite error on update in place"}, + {0x2F,0x00,SC_ALL_DEVS,"Commands cleared by another initiator"}, + {0x30,0x00,D|T|W|R|O|M|B|K,"Incompatible medium installed"}, + {0x30,0x01,D|T|W|R|O|B|K,"Cannot read medium - unknown format"}, + {0x30,0x02,D|T|W|R|O|B|K,"Cannot read medium - incompatible format"}, + {0x30,0x03,D|T|R|K,"Cleaning cartridge installed"}, + {0x30,0x04,D|T|W|R|O|B|K,"Cannot write medium - unknown format"}, + {0x30,0x05,D|T|W|R|O|B|K,"Cannot write medium - incompatible format"}, + {0x30,0x06,D|T|W|R|O|B,"Cannot format medium - incompatible medium"}, + {0x30,0x07,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning failure"}, + {0x30,0x08,R,"Cannot write - application code mismatch"}, + {0x30,0x09,R,"Current session not fixated for append"}, + {0x30,0x10,R,"Medium not formatted"}, /* should ascq be 0xa ?? */ + {0x31,0x00,D|T|W|R|O|B|K,"Medium format corrupted"}, + {0x31,0x01,D|L|R|O|B,"Format command failed"}, + {0x31,0x02,R,"Zoned formatting failed due to spare linking"}, + {0x32,0x00,D|W|O|B|K,"No defect spare location available"}, + {0x32,0x01,D|W|O|B|K,"Defect list update failure"}, + {0x33,0x00,T,"Tape length error"}, + {0x34,0x00,SC_ALL_DEVS,"Enclosure failure"}, + {0x35,0x00,SC_ALL_DEVS,"Enclosure services failure"}, + {0x35,0x01,SC_ALL_DEVS,"Unsupported enclosure function"}, + {0x35,0x02,SC_ALL_DEVS,"Enclosure services unavailable"}, + {0x35,0x03,SC_ALL_DEVS,"Enclosure services transfer failure"}, + {0x35,0x04,SC_ALL_DEVS,"Enclosure services transfer refused"}, + {0x36,0x00,L,"Ribbon,ink,or toner failure"}, + {0x37,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Rounded parameter"}, + {0x38,0x00,B,"Event status notification"}, + {0x38,0x02,B,"Esn - power management class event"}, + {0x38,0x04,B,"Esn - media class event"}, + {0x38,0x06,B,"Esn - device busy class event"}, + {0x39,0x00,D|T|L|W|R|S|O|M|C|A|E|K,"Saving parameters not supported"}, + {0x3A,0x00,D|T|L|W|R|S|O|M|B|K,"Medium not present"}, + {0x3A,0x01,D|T|W|R|O|M|B|K,"Medium not present - tray closed"}, + {0x3A,0x02,D|T|W|R|O|M|B|K,"Medium not present - tray open"}, + {0x3A,0x03,D|T|W|R|O|M|B,"Medium not present - loadable"}, + {0x3A,0x04,D|T|W|R|O|M|B, + "Medium not present - medium auxiliary memory accessible"}, + {0x3B,0x00,T|L,"Sequential positioning error"}, + {0x3B,0x01,T,"Tape position error at beginning-of-medium"}, + {0x3B,0x02,T,"Tape position error at end-of-medium"}, + {0x3B,0x03,L,"Tape or electronic vertical forms unit " SC_NOT_READY}, + {0x3B,0x04,L,"Slew failure"}, + {0x3B,0x05,L,"Paper jam"}, + {0x3B,0x06,L,"Failed to sense top-of-form"}, + {0x3B,0x07,L,"Failed to sense bottom-of-form"}, + {0x3B,0x08,T,"Reposition error"}, + {0x3B,0x09,S,"Read past end of medium"}, + {0x3B,0x0A,S,"Read past beginning of medium"}, + {0x3B,0x0B,S,"Position past end of medium"}, + {0x3B,0x0C,T|S,"Position past beginning of medium"}, + {0x3B,0x0D,D|T|W|R|O|M|B|K,"Medium destination element full"}, + {0x3B,0x0E,D|T|W|R|O|M|B|K,"Medium source element empty"}, + {0x3B,0x0F,R,"End of medium reached"}, + {0x3B,0x11,D|T|W|R|O|M|B|K,"Medium magazine not accessible"}, + {0x3B,0x12,D|T|W|R|O|M|B|K,"Medium magazine removed"}, + {0x3B,0x13,D|T|W|R|O|M|B|K,"Medium magazine inserted"}, + {0x3B,0x14,D|T|W|R|O|M|B|K,"Medium magazine locked"}, + {0x3B,0x15,D|T|W|R|O|M|B|K,"Medium magazine unlocked"}, + {0x3B,0x16,R,"Mechanical positioning or changer error"}, + {0x3D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|K,"Invalid bits in identify message"}, + {0x3E,0x00,SC_ALL_DEVS,SC_LOGICAL_UNIT "has not self-configured yet"}, + {0x3E,0x01,SC_ALL_DEVS,SC_LOGICAL_UNIT "failure"}, + {0x3E,0x02,SC_ALL_DEVS,"Timeout on logical unit"}, + {0x3E,0x03,SC_ALL_DEVS,SC_LOGICAL_UNIT "failed self-test"}, + {0x3E,0x04,SC_ALL_DEVS,SC_LOGICAL_UNIT "unable to update self-test log"}, + {0x3F,0x00,SC_ALL_DEVS,"Target operating conditions have changed"}, + {0x3F,0x01,SC_ALL_DEVS,"Microcode has been changed"}, + {0x3F,0x02,D|T|L|P|W|R|S|O|M|C|B|K,"Changed operating definition"}, + {0x3F,0x03,SC_ALL_DEVS,"Inquiry data has changed"}, + {0x3F,0x04,D|T|W|R|O|M|C|A|E|B|K,"Component device attached"}, + {0x3F,0x05,D|T|W|R|O|M|C|A|E|B|K,"Device identifier changed"}, + {0x3F,0x06,D|T|W|R|O|M|C|A|E|B,"Redundancy group created or modified"}, + {0x3F,0x07,D|T|W|R|O|M|C|A|E|B,"Redundancy group deleted"}, + {0x3F,0x08,D|T|W|R|O|M|C|A|E|B,"Spare created or modified"}, + {0x3F,0x09,D|T|W|R|O|M|C|A|E|B,"Spare deleted"}, + {0x3F,0x0A,D|T|W|R|O|M|C|A|E|B|K,"Volume set created or modified"}, + {0x3F,0x0B,D|T|W|R|O|M|C|A|E|B|K,"Volume set deleted"}, + {0x3F,0x0C,D|T|W|R|O|M|C|A|E|B|K,"Volume set deassigned"}, + {0x3F,0x0D,D|T|W|R|O|M|C|A|E|B|K,"Volume set reassigned"}, + {0x3F,0x0E,D|T|L|P|W|R|S|O|M|C|A|E,"Reported luns data has changed"}, + {0x3F,0x10,D|T|W|R|O|M|B,"Medium loadable"}, + {0x3F,0x11,D|T|W|R|O|M|B,"Medium auxiliary memory accessible"}, + {0x40,0x00,D,"Ram failure (should use 40 nn)"}, + /* + * FIXME(eric) - need a way to represent wildcards here. + */ + {0x40,0x00,SC_ALL_DEVS,"Diagnostic failure on component nn (80h-ffh)"}, + {0x41,0x00,D,"Data path failure (should use 40 nn)"}, + {0x42,0x00,D,"Power-on or self-test failure (should use 40 nn)"}, + {0x43,0x00,SC_ALL_DEVS,"Message error"}, + {0x44,0x00,SC_ALL_DEVS,"Internal target failure"}, + {0x45,0x00,SC_ALL_DEVS,"Select or reselect failure"}, + {0x46,0x00,D|T|L|P|W|R|S|O|M|C|B|K,"Unsuccessful soft reset"}, + {0x47,0x00,SC_ALL_DEVS,"Scsi parity error"}, + {0x47,0x01,SC_ALL_DEVS,"Data phase CRC error detected"}, + {0x47,0x02,SC_ALL_DEVS,"Scsi parity error detected during st data phase"}, + {0x47,0x03,SC_ALL_DEVS,"Information unit CRC error detected"}, + {0x47,0x04,SC_ALL_DEVS,"Asynchronous information protection error detected"}, + {0x47,0x05,SC_ALL_DEVS,"Protocol service CRC error"}, + {0x48,0x00,SC_ALL_DEVS,"Initiator detected error message received"}, + {0x49,0x00,SC_ALL_DEVS,"Invalid message error"}, + {0x4A,0x00,SC_ALL_DEVS,"Command phase error"}, + {0x4B,0x00,SC_ALL_DEVS,"Data phase error"}, + {0x4C,0x00,SC_ALL_DEVS,SC_LOGICAL_UNIT "failed self-configuration"}, + /* + * FIXME(eric) - need a way to represent wildcards here. + */ + {0x4D,0x00,SC_ALL_DEVS,"Tagged overlapped commands (nn = queue tag)"}, + {0x4E,0x00,SC_ALL_DEVS,"Overlapped commands attempted"}, + {0x50,0x00,T,"Write append error"}, + {0x50,0x01,T,"Write append position error"}, + {0x50,0x02,T,"Position error related to timing"}, + {0x51,0x00,T|R|O,"Erase failure"}, + {0x52,0x00,T,"Cartridge fault"}, + {0x53,0x00,D|T|L|W|R|S|O|M|B|K,"Media load or eject failed"}, + {0x53,0x01,T,"Unload tape failure"}, + {0x53,0x02,D|T|W|R|O|M|B|K,"Medium removal prevented"}, + {0x54,0x00,P,"Scsi to host system interface failure"}, + {0x55,0x00,P,"System resource failure"}, + {0x55,0x01,D|O|B|K,"System buffer full"}, + {0x55,0x02,D|T|L|P|W|R|S|O|M|A|E|K,"Insufficient reservation resources"}, + {0x55,0x03,D|T|L|P|W|R|S|O|M|C|A|E,"Insufficient resources"}, + {0x55,0x04,D|T|L|P|W|R|S|O|M|A|E,"Insufficient registration resources"}, + {0x55,0x05,D|T|P|W|R|O|M|A|E|B|K,"Insufficient access control resources"}, + {0x55,0x06,D|T|W|R|O|M|B,"Auxiliary memory out of space"}, + {0x57,0x00,R,"Unable to recover table-of-contents"}, + {0x58,0x00,O,"Generation does not exist"}, + {0x59,0x00,O,"Updated block read"}, + {0x5A,0x00,D|T|L|P|W|R|S|O|M|B|K,"Operator request or state change input"}, + {0x5A,0x01,D|T|W|R|O|M|B|K,"Operator medium removal request"}, + {0x5A,0x02,D|T|W|R|O|A|B|K,"Operator selected write protect"}, + {0x5A,0x03,D|T|W|R|O|A|B|K,"Operator selected write permit"}, + {0x5B,0x00,D|T|L|P|W|R|S|O|M|K,"Log exception"}, + {0x5B,0x01,D|T|L|P|W|R|S|O|M|K,"Threshold condition met"}, + {0x5B,0x02,D|T|L|P|W|R|S|O|M|K,"Log counter at maximum"}, + {0x5B,0x03,D|T|L|P|W|R|S|O|M|K,"Log list codes exhausted"}, + {0x5C,0x00,D|O,"Rpl status change"}, + {0x5C,0x01,D|O,"Spindles synchronized"}, + {0x5C,0x02,D|O,"Spindles not synchronized"}, + {0x5D,0x00,SC_ALL_DEVS,"Failure prediction threshold exceeded"}, + {0x5D,0x01,R|B,"Media failure prediction threshold exceeded"}, + {0x5D,0x02,R,SC_LOGICAL_UNIT "failure prediction threshold exceeded"}, + {0x5D,0x03,R,"spare area exhaustion prediction threshold exceeded"}, + /* large series of "impending failure" messages */ + {0x5D,0x10,D|B,SC_HARDWARE_IF "general hard drive failure"}, + {0x5D,0x11,D|B,SC_HARDWARE_IF "drive" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x12,D|B,SC_HARDWARE_IF "data" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x13,D|B,SC_HARDWARE_IF "seek" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x14,D|B,SC_HARDWARE_IF "too many block reassigns"}, + {0x5D,0x15,D|B,SC_HARDWARE_IF "access" SC_TIMES_TOO_HIGH }, + {0x5D,0x16,D|B,SC_HARDWARE_IF "start unit" SC_TIMES_TOO_HIGH }, + {0x5D,0x17,D|B,SC_HARDWARE_IF "channel parametrics"}, + {0x5D,0x18,D|B,SC_HARDWARE_IF "controller detected"}, + {0x5D,0x19,D|B,SC_HARDWARE_IF "throughput performance"}, + {0x5D,0x1A,D|B,SC_HARDWARE_IF "seek time performance"}, + {0x5D,0x1B,D|B,SC_HARDWARE_IF "spin-up retry count"}, + {0x5D,0x1C,D|B,SC_HARDWARE_IF "drive calibration retry count"}, + {0x5D,0x20,D|B,SC_CONTROLLER_IF "general hard drive failure"}, + {0x5D,0x21,D|B,SC_CONTROLLER_IF "drive" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x22,D|B,SC_CONTROLLER_IF "data" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x23,D|B,SC_CONTROLLER_IF "seek" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x24,D|B,SC_CONTROLLER_IF "too many block reassigns"}, + {0x5D,0x25,D|B,SC_CONTROLLER_IF "access" SC_TIMES_TOO_HIGH }, + {0x5D,0x26,D|B,SC_CONTROLLER_IF "start unit" SC_TIMES_TOO_HIGH }, + {0x5D,0x27,D|B,SC_CONTROLLER_IF "channel parametrics"}, + {0x5D,0x28,D|B,SC_CONTROLLER_IF "controller detected"}, + {0x5D,0x29,D|B,SC_CONTROLLER_IF "throughput performance"}, + {0x5D,0x2A,D|B,SC_CONTROLLER_IF "seek time performance"}, + {0x5D,0x2B,D|B,SC_CONTROLLER_IF "spin-up retry count"}, + {0x5D,0x2C,D|B,SC_CONTROLLER_IF "drive calibration retry count"}, + {0x5D,0x30,D|B,SC_DATA_CHANNEL_IF "general hard drive failure"}, + {0x5D,0x31,D|B,SC_DATA_CHANNEL_IF "drive" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x32,D|B,SC_DATA_CHANNEL_IF "data" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x33,D|B,SC_DATA_CHANNEL_IF "seek" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x34,D|B,SC_DATA_CHANNEL_IF "too many block reassigns"}, + {0x5D,0x35,D|B,SC_DATA_CHANNEL_IF "access" SC_TIMES_TOO_HIGH }, + {0x5D,0x36,D|B,SC_DATA_CHANNEL_IF "start unit" SC_TIMES_TOO_HIGH }, + {0x5D,0x37,D|B,SC_DATA_CHANNEL_IF "channel parametrics"}, + {0x5D,0x38,D|B,SC_DATA_CHANNEL_IF "controller detected"}, + {0x5D,0x39,D|B,SC_DATA_CHANNEL_IF "throughput performance"}, + {0x5D,0x3A,D|B,SC_DATA_CHANNEL_IF "seek time performance"}, + {0x5D,0x3B,D|B,SC_DATA_CHANNEL_IF "spin-up retry count"}, + {0x5D,0x3C,D|B,SC_DATA_CHANNEL_IF "drive calibration retry count"}, + {0x5D,0x40,D|B,SC_SERVO_IF "general hard drive failure"}, + {0x5D,0x41,D|B,SC_SERVO_IF "drive" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x42,D|B,SC_SERVO_IF "data" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x43,D|B,SC_SERVO_IF "seek" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x44,D|B,SC_SERVO_IF "too many block reassigns"}, + {0x5D,0x45,D|B,SC_SERVO_IF "access" SC_TIMES_TOO_HIGH }, + {0x5D,0x46,D|B,SC_SERVO_IF "start unit" SC_TIMES_TOO_HIGH }, + {0x5D,0x47,D|B,SC_SERVO_IF "channel parametrics"}, + {0x5D,0x48,D|B,SC_SERVO_IF "controller detected"}, + {0x5D,0x49,D|B,SC_SERVO_IF "throughput performance"}, + {0x5D,0x4A,D|B,SC_SERVO_IF "seek time performance"}, + {0x5D,0x4B,D|B,SC_SERVO_IF "spin-up retry count"}, + {0x5D,0x4C,D|B,SC_SERVO_IF "drive calibration retry count"}, + {0x5D,0x50,D|B,SC_SPINDLE_IF "general hard drive failure"}, + {0x5D,0x51,D|B,SC_SPINDLE_IF "drive" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x52,D|B,SC_SPINDLE_IF "data" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x53,D|B,SC_SPINDLE_IF "seek" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x54,D|B,SC_SPINDLE_IF "too many block reassigns"}, + {0x5D,0x55,D|B,SC_SPINDLE_IF "access" SC_TIMES_TOO_HIGH }, + {0x5D,0x56,D|B,SC_SPINDLE_IF "start unit" SC_TIMES_TOO_HIGH }, + {0x5D,0x57,D|B,SC_SPINDLE_IF "channel parametrics"}, + {0x5D,0x58,D|B,SC_SPINDLE_IF "controller detected"}, + {0x5D,0x59,D|B,SC_SPINDLE_IF "throughput performance"}, + {0x5D,0x5A,D|B,SC_SPINDLE_IF "seek time performance"}, + {0x5D,0x5B,D|B,SC_SPINDLE_IF "spin-up retry count"}, + {0x5D,0x5C,D|B,SC_SPINDLE_IF "drive calibration retry count"}, + {0x5D,0x60,D|B,SC_FIRMWARE_IF "general hard drive failure"}, + {0x5D,0x61,D|B,SC_FIRMWARE_IF "drive" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x62,D|B,SC_FIRMWARE_IF "data" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x63,D|B,SC_FIRMWARE_IF "seek" SC_ERROR_RATE_TOO_HIGH }, + {0x5D,0x64,D|B,SC_FIRMWARE_IF "too many block reassigns"}, + {0x5D,0x65,D|B,SC_FIRMWARE_IF "access" SC_TIMES_TOO_HIGH }, + {0x5D,0x66,D|B,SC_FIRMWARE_IF "start unit" SC_TIMES_TOO_HIGH }, + {0x5D,0x67,D|B,SC_FIRMWARE_IF "channel parametrics"}, + {0x5D,0x68,D|B,SC_FIRMWARE_IF "controller detected"}, + {0x5D,0x69,D|B,SC_FIRMWARE_IF "throughput performance"}, + {0x5D,0x6A,D|B,SC_FIRMWARE_IF "seek time performance"}, + {0x5D,0x6B,D|B,SC_FIRMWARE_IF "spin-up retry count"}, + {0x5D,0x6C,D|B,SC_FIRMWARE_IF "drive calibration retry count"}, + {0x5D,0xFF,SC_ALL_DEVS,"Failure prediction threshold exceeded (false)"}, + {0x5E,0x00,D|T|L|P|W|R|S|O|C|A|K,"Low power condition on"}, + {0x5E,0x01,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by timer"}, + {0x5E,0x02,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by timer"}, + {0x5E,0x03,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by command"}, + {0x5E,0x04,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by command"}, + {0x5E,0x41,B,"Power state change to active"}, + {0x5E,0x42,B,"Power state change to idle"}, + {0x5E,0x43,B,"Power state change to standby"}, + {0x5E,0x45,B,"Power state change to sleep"}, + {0x5E,0x47,B|K,"Power state change to device control"}, + {0x60,0x00,S,"Lamp failure"}, + {0x61,0x00,S,"Video acquisition error"}, + {0x61,0x01,S,"Unable to acquire video"}, + {0x61,0x02,S,"Out of focus"}, + {0x62,0x00,S,"Scan head positioning error"}, + {0x63,0x00,R,"End of user area encountered on this track"}, + {0x63,0x01,R,"Packet does not fit in available space"}, + {0x64,0x00,R,"Illegal mode for this track"}, + {0x64,0x01,R,"Invalid packet size"}, + {0x65,0x00,SC_ALL_DEVS,"Voltage fault"}, + {0x66,0x00,S,"Automatic document feeder cover up"}, + {0x66,0x01,S,"Automatic document feeder lift up"}, + {0x66,0x02,S,"Document jam in automatic document feeder"}, + {0x66,0x03,S,"Document miss feed automatic in document feeder"}, + {0x67,0x00,A,"Configuration failure"}, + {0x67,0x01,A,"Configuration of incapable logical units failed"}, + {0x67,0x02,A,"Add logical unit failed"}, + {0x67,0x03,A,"Modification of logical unit failed"}, + {0x67,0x04,A,"Exchange of logical unit failed"}, + {0x67,0x05,A,"Remove of logical unit failed"}, + {0x67,0x06,A,"Attachment of logical unit failed"}, + {0x67,0x07,A,"Creation of logical unit failed"}, + {0x67,0x08,A,"Assign failure occurred"}, + {0x67,0x09,A,"Multiply assigned logical unit"}, + {0x67,0x0A,SC_ALL_DEVS,"Set target port groups command failed"}, + {0x68,0x00,A,SC_LOGICAL_UNIT "not configured"}, + {0x69,0x00,A,"Data loss on logical unit"}, + {0x69,0x01,A,"Multiple logical unit failures"}, + {0x69,0x02,A,"Parity/data mismatch"}, + {0x6A,0x00,A,"Informational,refer to log"}, + {0x6B,0x00,A,"State change has occurred"}, + {0x6B,0x01,A,"Redundancy level got better"}, + {0x6B,0x02,A,"Redundancy level got worse"}, + {0x6C,0x00,A,"Rebuild failure occurred"}, + {0x6D,0x00,A,"Recalculate failure occurred"}, + {0x6E,0x00,A,"Command to logical unit failed"}, + {0x6F,0x00,R,"Copy protection key exchange failure - authentication failure"}, + {0x6F,0x01,R,"Copy protection key exchange failure - key not present"}, + {0x6F,0x02,R,"Copy protection key exchange failure - key not established"}, + {0x6F,0x03,R,"Read of scrambled sector without authentication"}, + {0x6F,0x04,R,"Media region code is mismatched to logical unit region"}, + {0x6F,0x05,R,"Drive region must be permanent/region reset count error"}, + /* + * FIXME(eric) - need a way to represent wildcards here. + */ + {0x70,0x00,T,"Decompression exception short algorithm id of nn"}, + {0x71,0x00,T,"Decompression exception long algorithm id"}, + {0x72,0x00,R,"Session fixation error"}, + {0x72,0x01,R,"Session fixation error writing lead-in"}, + {0x72,0x02,R,"Session fixation error writing lead-out"}, + {0x72,0x03,R,"Session fixation error - incomplete track in session"}, + {0x72,0x04,R,"Empty or partially written reserved track"}, + {0x72,0x05,R,"No more track reservations allowed"}, + {0x73,0x00,R,"Cd control error"}, + {0x73,0x01,R,"Power calibration area almost full"}, + {0x73,0x02,R,"Power calibration area is full"}, + {0x73,0x03,R,"Power calibration area error"}, + {0x73,0x04,R,"Program memory area update failure"}, + {0x73,0x05,R,"Program memory area is full"}, + {0x73,0x06,R,"RMA/PMA is full"}, + {0, 0, 0, NULL} +}; + +static const char * sc_oft_used[0x1f] = { + "umulig", /* index 0x0 should be impossible */ + "Audio play operation ", + "Logical unit ", + "not ready, ", + " operation", + " in progress ", + "Hardware impending failure ", + "Controller impending failure ", + "Data channel impending failure ", /* index 0x8 */ + "Servo impending failure ", + "Spindle impending failure ", + "Firmware impending failure ", + "Recovered data ", + " error rate too high", + " times too high", +}; + +static const char *snstext[] = { + "No Sense", /* There is no sense information */ + "Recovered Error", /* The last command completed successfully + but used error correction */ + "Not Ready", /* The addressed target is not ready */ + "Medium Error", /* Data error detected on the medium */ + "Hardware Error", /* Controller or device failure */ + "Illegal Request", + "Unit Attention", /* Removable medium was changed, or + the target has been reset */ + "Data Protect", /* Access to the data is blocked */ + "Blank Check", /* Reached unexpected written or unwritten + region of the medium */ + "Key=9", /* Vendor specific */ + "Copy Aborted", /* COPY or COMPARE was aborted */ + "Aborted Command", /* The target aborted the command */ + "Equal", /* SEARCH DATA found data equal (obsolete) */ + "Volume Overflow", /* Medium full with still data to be written */ + "Miscompare", /* Source data and data on the medium + do not agree */ + "Key=15" /* Reserved */ +}; + +static +void sg_print_asc_ascq(unsigned char asc, unsigned char ascq) +{ + int k, j; + char obuff[256]; + const char * ccp; + const char * oup; + char c; + int found = 0; + + for (k=0; additional[k].text; k++) { + if (additional[k].code1 == asc && + additional[k].code2 == ascq) { + found = 1; + ccp = additional[k].text; + for (j = 0; *ccp && (j < sizeof(obuff)); ++ccp) { + c = *ccp; + if ((c < 0x20) && (c > 0)) { + oup = sc_oft_used[(int)c]; + if (oup) { + strcpy(obuff + j, oup); + j += strlen(oup); + } + else { + strcpy(obuff + j, "???"); + j += 3; + } + } + else + obuff[j++] = c; + } + if (j < sizeof(obuff)) + obuff[j] = '\0'; + else + obuff[sizeof(obuff) - 1] = '\0'; + fprintf(OUTP, "Additional sense: %s\n", obuff); + } + } + if (found) + return; + + for(k=0; additional2[k].text; k++) { + if ((additional2[k].code1 == asc) && + (ascq >= additional2[k].code2_min) && + (ascq <= additional2[k].code2_max)) { + found = 1; + fprintf(OUTP, "Additional sense: "); + fprintf(OUTP, additional2[k].text, ascq); + fprintf(OUTP, "\n"); + } + } + if (! found) + fprintf(OUTP, "ASC=%2x ASCQ=%2x\n", asc, ascq); +} + +/* Print sense information */ +void sg_print_sense(const char * leadin, const unsigned char * sense_buffer, + int sb_len) +{ + int k, s; + int sense_key, sense_class, valid, code; + int descriptor_format = 0; + const char * error = NULL; + + if (sb_len < 1) { + fprintf(OUTP, "sense buffer empty\n"); + return; + } + sense_class = (sense_buffer[0] >> 4) & 0x07; + code = sense_buffer[0] & 0xf; + valid = sense_buffer[0] & 0x80; + if (leadin) + fprintf(OUTP, "%s: ", leadin); + + if (sense_class == 7) { /* extended sense data */ + s = sense_buffer[7] + 8; + if(s > sb_len) /* device has more available which we ignore */ + s = sb_len; + + switch (code) { + case 0x0: + error = "Current"; /* error concerns current command */ + break; + case 0x1: + error = "Deferred"; /* error concerns some earlier command */ + /* e.g., an earlier write to disk cache succeeded, but + now the disk discovers that it cannot write the data */ + break; + case 0x2: + descriptor_format = 1; + error = "Descriptor current"; + /* new descriptor sense format */ + break; + case 0x3: + descriptor_format = 1; + error = "Descriptor deferred"; + /* new descriptor sense format (deferred report) */ + break; + default: + error = "Invalid"; + } + sense_key = sense_buffer[ descriptor_format ? 1 : 2 ] & 0xf; + fprintf(OUTP, "%s, Sense key: %s\n", error, snstext[sense_key]); + + if (descriptor_format) + sg_print_asc_ascq(sense_buffer[2], sense_buffer[3]); + else { + if (!valid) + fprintf(OUTP, "[valid=0] "); + fprintf(OUTP, "Info fld=0x%x, ", (int)((sense_buffer[3] << 24) | + (sense_buffer[4] << 16) | (sense_buffer[5] << 8) | + sense_buffer[6])); + + if (sense_buffer[2] & 0x80) + fprintf(OUTP, "FMK "); /* current command has read a filemark */ + if (sense_buffer[2] & 0x40) + fprintf(OUTP, "EOM "); /* end-of-medium condition exists */ + if (sense_buffer[2] & 0x20) + fprintf(OUTP, "ILI "); /* incorrect block length requested */ + + if (s > 13) { + if (sense_buffer[12] || sense_buffer[13]) + sg_print_asc_ascq(sense_buffer[12], sense_buffer[13]); + } + if (sense_key == 5 && s >= 18 && (sense_buffer[15]&0x80)) { + fprintf(OUTP, "Sense Key Specific: Error in %s byte %d", + (sense_buffer[15]&0x40)?"Command":"Data", + (sense_buffer[16]<<8)|sense_buffer[17]); + if(sense_buffer[15]&0x08) { + fprintf(OUTP, " bit %d\n", sense_buffer[15]&0x07); + } else { + fprintf(OUTP, "\n"); + } + } + } + + } else { /* non-extended sense data */ + + /* + * Standard says: + * sense_buffer[0] & 0200 : address valid + * sense_buffer[0] & 0177 : vendor-specific error code + * sense_buffer[1] & 0340 : vendor-specific + * sense_buffer[1..3] : 21-bit logical block address + */ + + if (sb_len < 4) { + fprintf(OUTP, "sense buffer too short (4 byte minimum)\n"); + return; + } + if (leadin) + fprintf(OUTP, "%s: ", leadin); + if (sense_buffer[0] < 15) + fprintf(OUTP, + "old sense: key %s\n", snstext[sense_buffer[0] & 0x0f]); + else + fprintf(OUTP, "sns = %2x %2x\n", sense_buffer[0], sense_buffer[2]); + + fprintf(OUTP, "Non-extended sense class %d code 0x%0x ", + sense_class, code); + s = 4; + } + + fprintf(OUTP, "Raw sense data (in hex):\n "); + for (k = 0; k < s; ++k) { + if ((k > 0) && (0 == (k % 24))) + fprintf(OUTP, "\n "); + fprintf(OUTP, "%02x ", sense_buffer[k]); + } + fprintf(OUTP, "\n"); +} + +static const char * hostbyte_table[]={ +"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET", +"DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR", +"DID_PASSTHROUGH", "DID_SOFT_ERROR", NULL}; + +void sg_print_host_status(int host_status) +{ static int maxcode=0; + int i; + + if(! maxcode) { + for(i = 0; hostbyte_table[i]; i++) ; + maxcode = i-1; + } + fprintf(OUTP, "Host_status=0x%02x", host_status); + if(host_status > maxcode) { + fprintf(OUTP, "is invalid "); + return; + } + fprintf(OUTP, "(%s) ",hostbyte_table[host_status]); +} + +static const char * driverbyte_table[]={ +"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR", +"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE", NULL}; + +static const char * driversuggest_table[]={"SUGGEST_OK", +"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE", +unknown,unknown,unknown, "SUGGEST_SENSE",NULL}; + + +void sg_print_driver_status(int driver_status) +{ + static int driver_max =0 , suggest_max=0; + int i; + int dr = driver_status & SG_ERR_DRIVER_MASK; + int su = (driver_status & SG_ERR_SUGGEST_MASK) >> 4; + + if(! driver_max) { + for(i = 0; driverbyte_table[i]; i++) ; + driver_max = i; + for(i = 0; driversuggest_table[i]; i++) ; + suggest_max = i; + } + fprintf(OUTP, "Driver_status=0x%02x",driver_status); + fprintf(OUTP, " (%s,%s) ", + dr < driver_max ? driverbyte_table[dr]:"invalid", + su < suggest_max ? driversuggest_table[su]:"invalid"); +} + +static int sg_sense_print(const char * leadin, int scsi_status, + int host_status, int driver_status, + const unsigned char * sense_buffer, int sb_len) +{ + int done_leadin = 0; + int done_sense = 0; + + scsi_status &= 0x7e; /*sanity */ + if ((0 == scsi_status) && (0 == host_status) && + (0 == driver_status)) + return 1; /* No problems */ + if (0 != scsi_status) { + if (leadin) + fprintf(OUTP, "%s: ", leadin); + done_leadin = 1; + fprintf(OUTP, "scsi status: "); + sg_print_scsi_status(scsi_status); + fprintf(OUTP, "\n"); + if (sense_buffer && ((scsi_status == SCSI_CHECK_CONDITION) || + (scsi_status == SCSI_COMMAND_TERMINATED))) { + sg_print_sense(0, sense_buffer, sb_len); + done_sense = 1; + } + } + if (0 != host_status) { + if (leadin && (! done_leadin)) + fprintf(OUTP, "%s: ", leadin); + if (done_leadin) + fprintf(OUTP, "plus...: "); + else + done_leadin = 1; + sg_print_host_status(host_status); + fprintf(OUTP, "\n"); + } + if (0 != driver_status) { + if (leadin && (! done_leadin)) + fprintf(OUTP, "%s: ", leadin); + if (done_leadin) + fprintf(OUTP, "plus...: "); + else + done_leadin = 1; + sg_print_driver_status(driver_status); + fprintf(OUTP, "\n"); + if (sense_buffer && (! done_sense) && + (SG_ERR_DRIVER_SENSE == (0xf & driver_status))) + sg_print_sense(0, sense_buffer, sb_len); + } + return 0; +} + +#ifdef SG_IO +int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp) +{ + return sg_sense_print(leadin, hp->status, hp->host_status, + hp->driver_status, hp->sbp, hp->sb_len_wr); +} +#endif + +int sg_chk_n_print(const char * leadin, int masked_status, + int host_status, int driver_status, + const unsigned char * sense_buffer, int sb_len) +{ + int scsi_status = (masked_status << 1) & 0x7e; + + return sg_sense_print(leadin, scsi_status, host_status, driver_status, + sense_buffer, sb_len); +} + +#ifdef SG_IO +int sg_err_category3(struct sg_io_hdr * hp) +{ + return sg_err_category_new(hp->status, hp->host_status, + hp->driver_status, hp->sbp, hp->sb_len_wr); +} +#endif + +int sg_err_category(int masked_status, int host_status, + int driver_status, const unsigned char * sense_buffer, + int sb_len) +{ + int scsi_status = (masked_status << 1) & 0x7e; + + return sg_err_category_new(scsi_status, host_status, driver_status, + sense_buffer, sb_len); +} + +int sg_err_category_new(int scsi_status, int host_status, int driver_status, + const unsigned char * sense_buffer, int sb_len) +{ + scsi_status &= 0x7e; + if ((0 == scsi_status) && (0 == host_status) && + (0 == driver_status)) + return SG_ERR_CAT_CLEAN; + if ((SCSI_CHECK_CONDITION == scsi_status) || + (SCSI_COMMAND_TERMINATED == scsi_status) || + (SG_ERR_DRIVER_SENSE == (0xf & driver_status))) { + if (sense_buffer && (sb_len > 2)) { + int sense_key; + unsigned char asc; + + if (sense_buffer[0] & 0x2) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + } + else { + sense_key = sense_buffer[2] & 0xf; + asc = (sb_len > 12) ? sense_buffer[12] : 0; + } + + if(RECOVERED_ERROR == sense_key) + return SG_ERR_CAT_RECOVERED; + else if (UNIT_ATTENTION == sense_key) { + if (0x28 == asc) + return SG_ERR_CAT_MEDIA_CHANGED; + if (0x29 == asc) + return SG_ERR_CAT_RESET; + } + } + return SG_ERR_CAT_SENSE; + } + if (0 != host_status) { + if ((SG_ERR_DID_NO_CONNECT == host_status) || + (SG_ERR_DID_BUS_BUSY == host_status) || + (SG_ERR_DID_TIME_OUT == host_status)) + return SG_ERR_CAT_TIMEOUT; + } + if (0 != driver_status) { + if (SG_ERR_DRIVER_TIMEOUT == driver_status) + return SG_ERR_CAT_TIMEOUT; + } + return SG_ERR_CAT_OTHER; +} + +int sg_get_command_size(unsigned char opcode) +{ + return COMMAND_SIZE(opcode); +} + +void sg_get_command_name(unsigned char opcode, int buff_len, char * buff) +{ + const char **table = commands[ group(opcode) ]; + + if ((NULL == buff) || (buff_len < 1)) + return; + + switch ((unsigned long) table) { + case RESERVED_GROUP: + strncpy(buff, reserved, buff_len); + break; + case VENDOR_GROUP: + strncpy(buff, vendor, buff_len); + break; + default: + strncpy(buff, table[opcode & 0x1f], buff_len); + break; + } +} diff --git a/extras/multipath/sg_err.h b/extras/multipath/sg_err.h new file mode 100644 index 000000000..ef57b5ce3 --- /dev/null +++ b/extras/multipath/sg_err.h @@ -0,0 +1,162 @@ +#ifndef SG_ERR_H +#define SG_ERR_H + +/* Feel free to copy and modify this GPL-ed code into your applications. */ + +/* Version 0.90 (20030519) +*/ + + +/* Some of the following error/status codes are exchanged between the + various layers of the SCSI sub-system in Linux and should never + reach the user. They are placed here for completeness. What appears + here is copied from drivers/scsi/scsi.h which is not visible in + the user space. */ + +#ifndef SCSI_CHECK_CONDITION +/* Following are the "true" SCSI status codes. Linux has traditionally + used a 1 bit right and masked version of these. So now CHECK_CONDITION + and friends (in ) are deprecated. */ +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_CONDITION_MET 0x4 +#define SCSI_BUSY 0x8 +#define SCSI_IMMEDIATE 0x10 +#define SCSI_IMMEDIATE_CONDITION_MET 0x14 +#define SCSI_RESERVATION_CONFLICT 0x18 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SCSI_TASK_SET_FULL 0x28 +#define SCSI_ACA_ACTIVE 0x30 +#define SCSI_TASK_ABORTED 0x40 +#endif + +/* The following are 'host_status' codes */ +#ifndef DID_OK +#define DID_OK 0x00 +#endif +#ifndef DID_NO_CONNECT +#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ +#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ +#define DID_TIME_OUT 0x03 /* Timed out for some other reason */ +#define DID_BAD_TARGET 0x04 /* Bad target (id?) */ +#define DID_ABORT 0x05 /* Told to abort for some other reason */ +#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */ +#define DID_ERROR 0x07 /* Internal error */ +#define DID_RESET 0x08 /* Reset by somebody */ +#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */ +#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */ +#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */ +#endif + +/* These defines are to isolate applictaions from kernel define changes */ +#define SG_ERR_DID_OK DID_OK +#define SG_ERR_DID_NO_CONNECT DID_NO_CONNECT +#define SG_ERR_DID_BUS_BUSY DID_BUS_BUSY +#define SG_ERR_DID_TIME_OUT DID_TIME_OUT +#define SG_ERR_DID_BAD_TARGET DID_BAD_TARGET +#define SG_ERR_DID_ABORT DID_ABORT +#define SG_ERR_DID_PARITY DID_PARITY +#define SG_ERR_DID_ERROR DID_ERROR +#define SG_ERR_DID_RESET DID_RESET +#define SG_ERR_DID_BAD_INTR DID_BAD_INTR +#define SG_ERR_DID_PASSTHROUGH DID_PASSTHROUGH +#define SG_ERR_DID_SOFT_ERROR DID_SOFT_ERROR + +/* The following are 'driver_status' codes */ +#ifndef DRIVER_OK +#define DRIVER_OK 0x00 +#endif +#ifndef DRIVER_BUSY +#define DRIVER_BUSY 0x01 +#define DRIVER_SOFT 0x02 +#define DRIVER_MEDIA 0x03 +#define DRIVER_ERROR 0x04 +#define DRIVER_INVALID 0x05 +#define DRIVER_TIMEOUT 0x06 +#define DRIVER_HARD 0x07 +#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ + +/* Following "suggests" are "or-ed" with one of previous 8 entries */ +#define SUGGEST_RETRY 0x10 +#define SUGGEST_ABORT 0x20 +#define SUGGEST_REMAP 0x30 +#define SUGGEST_DIE 0x40 +#define SUGGEST_SENSE 0x80 +#define SUGGEST_IS_OK 0xff +#endif +#ifndef DRIVER_MASK +#define DRIVER_MASK 0x0f +#endif +#ifndef SUGGEST_MASK +#define SUGGEST_MASK 0xf0 +#endif + +/* These defines are to isolate applictaions from kernel define changes */ +#define SG_ERR_DRIVER_OK DRIVER_OK +#define SG_ERR_DRIVER_BUSY DRIVER_BUSY +#define SG_ERR_DRIVER_SOFT DRIVER_SOFT +#define SG_ERR_DRIVER_MEDIA DRIVER_MEDIA +#define SG_ERR_DRIVER_ERROR DRIVER_ERROR +#define SG_ERR_DRIVER_INVALID DRIVER_INVALID +#define SG_ERR_DRIVER_TIMEOUT DRIVER_TIMEOUT +#define SG_ERR_DRIVER_HARD DRIVER_HARD +#define SG_ERR_DRIVER_SENSE DRIVER_SENSE +#define SG_ERR_SUGGEST_RETRY SUGGEST_RETRY +#define SG_ERR_SUGGEST_ABORT SUGGEST_ABORT +#define SG_ERR_SUGGEST_REMAP SUGGEST_REMAP +#define SG_ERR_SUGGEST_DIE SUGGEST_DIE +#define SG_ERR_SUGGEST_SENSE SUGGEST_SENSE +#define SG_ERR_SUGGEST_IS_OK SUGGEST_IS_OK +#define SG_ERR_DRIVER_MASK DRIVER_MASK +#define SG_ERR_SUGGEST_MASK SUGGEST_MASK + + + +/* The following "print" functions send ACSII to stdout */ +extern void sg_print_command(const unsigned char * command); +extern void sg_print_sense(const char * leadin, + const unsigned char * sense_buffer, int sb_len); +extern void sg_print_status(int masked_status); +extern void sg_print_scsi_status(int scsi_status); +extern void sg_print_host_status(int host_status); +extern void sg_print_driver_status(int driver_status); + +/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings + else it prints to standard output and returns 0. */ +extern int sg_chk_n_print(const char * leadin, int masked_status, + int host_status, int driver_status, + const unsigned char * sense_buffer, int sb_len); + +/* The following function declaration is for the sg version 3 driver. + Only version 3 sg_err.c defines it. */ +struct sg_io_hdr; +extern int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp); + + +/* The following "category" function returns one of the following */ +#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */ +#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */ +#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */ +#define SG_ERR_CAT_TIMEOUT 3 +#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */ +#define SG_ERR_CAT_SENSE 98 /* Something else is in the sense buffer */ +#define SG_ERR_CAT_OTHER 99 /* Some other error/warning has occurred */ + +extern int sg_err_category(int masked_status, int host_status, + int driver_status, const unsigned char * sense_buffer, + int sb_len); + +extern int sg_err_category_new(int scsi_status, int host_status, + int driver_status, const unsigned char * sense_buffer, + int sb_len); + +/* The following function declaration is for the sg version 3 driver. + Only version 3 sg_err.c defines it. */ +extern int sg_err_category3(struct sg_io_hdr * hp); + +/* Returns length of SCSI command given the opcode (first byte) */ +extern int sg_get_command_size(unsigned char opcode); + +extern void sg_get_command_name(unsigned char opcode, int buff_len, + char * buff); + +#endif diff --git a/extras/multipath/sg_include.h b/extras/multipath/sg_include.h new file mode 100644 index 000000000..6b6dd6f37 --- /dev/null +++ b/extras/multipath/sg_include.h @@ -0,0 +1,44 @@ +#ifdef SG_KERNEL_INCLUDES + #define __user + typedef unsigned char u8; + #include "/usr/src/linux/include/scsi/sg.h" + #include "/usr/src/linux/include/scsi/scsi.h" +#else + #ifdef SG_TRICK_GNU_INCLUDES + #include + #include + #else + #include + #include + #endif +#endif + +/* + Getting the correct include files for the sg interface can be an ordeal. + In a perfect world, one would just write: + #include + #include + This would include the files found in the /usr/include/scsi directory. + Those files are maintained with the GNU library which may or may not + agree with the kernel and version of sg driver that is running. Any + many cases this will not matter. However in some it might, for example + glibc 2.1's include files match the sg driver found in the lk 2.2 + series. Hence if glibc 2.1 is used with lk 2.4 then the additional + sg v3 interface will not be visible. + If this is a problem then defining SG_KERNEL_INCLUDES will access the + kernel supplied header files (assuming they are in the normal place). + The GNU library maintainers and various kernel people don't like + this approach (but it does work). + The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and + was used) prior to glibc 2.2 . Prior to that version /usr/include/linux + was a symbolic link to /usr/src/linux/include/linux . + + There are other approaches if this include "mixup" causes pain. These + would involve include files being copied or symbolic links being + introduced. + + Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES + nor SG_TRICK_GNU_INCLUDES is defined. + + dpg 20010415, 20030522 +*/