From 6e64b86f968c387672eac691acad20f91b4bd526 Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Wed, 27 Jun 2007 02:51:54 +0000 Subject: Load sg3_utils-1.03 into trunk/. git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@29 6180dd3e-e324-4e3e-922d-17de1ae2f315 --- archive/sg_err.h | 4 +- sg3_utils.spec | 128 ++++++++++++++++ sg_inq.8 | 85 +++++++++++ sg_logs.8 | 70 +++++++++ sg_modes.8 | 76 ++++++++++ sg_senddiag.8 | 90 +++++++++++ sg_senddiag.c | 452 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 902 insertions(+), 3 deletions(-) create mode 100644 sg3_utils.spec create mode 100644 sg_inq.8 create mode 100644 sg_logs.8 create mode 100644 sg_modes.8 create mode 100644 sg_senddiag.8 create mode 100644 sg_senddiag.c diff --git a/archive/sg_err.h b/archive/sg_err.h index 3609963c..317767b6 100644 --- a/archive/sg_err.h +++ b/archive/sg_err.h @@ -3,9 +3,7 @@ /* Feel free to copy and modify this GPL-ed code into your applications. */ -/* Version 0.87 (20020616) - - all output now sent to stderr rather thatn stdout - - remove header files included in this file +/* Version 0.89 (20030313) */ diff --git a/sg3_utils.spec b/sg3_utils.spec new file mode 100644 index 00000000..9d3d2647 --- /dev/null +++ b/sg3_utils.spec @@ -0,0 +1,128 @@ +# +# spec file for sg3_utils +# +# please send bugfixes or comments to dgilbert@interlog.com +# + +Summary: Utils for Linux's SCSI generic driver devices + raw devices +Name: sg3_utils +Version: 1.03 +Release: 1 +Packager: dgilbert@interlog.com +License: GPL +Group: Utilities/System +Source: ftp://www.torque.net/sg/p/sg3_utils-1.03.tgz +Url: http://www.torque.net/sg/u_index.html +Provides: sg_utils + +%description +Collection of small useful tools, that are based on the Linux SCSI generic +(sg) interface and give info on the SCSI bus, copy data,... The utilities +to copy data are based on the Unix dd command and are called sg_dd, sgp_dd +and sgm_dd. These 3 commands can also act on raw devices (e.g. /dev/raw/raw1). + +Warning: Some of these tools access the internals of your system and the +incorrect usage of them may render your system inoperable. + +Authors: +-------- + Doug Gilbert + Kurt Garloff [sg_test_rwbuff] + Peter Allworth [contribution to sg_dd and sgp_dd] + Martin Schwenke [contribution to sg_inq] + +%prep +%setup + +%build +make + +%install +make install INSTDIR=%{_bindir} MANDIR=%{_mandir} + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%attr(-,root,root) %doc README README.sg_start CHANGELOG INSTALL +%attr(755,root,root) %{_bindir}/sg_dd +%attr(755,root,root) %{_bindir}/sg_debug +%attr(755,root,root) %{_bindir}/sg_inq +%attr(755,root,root) %{_bindir}/sg_scan +%attr(755,root,root) %{_bindir}/sg_rbuf +%attr(755,root,root) %{_bindir}/sginfo +%attr(755,root,root) %{_bindir}/sg_simple1 +%attr(755,root,root) %{_bindir}/sg_simple2 +%attr(755,root,root) %{_bindir}/sg_simple3 +%attr(755,root,root) %{_bindir}/sg_simple4 +%attr(755,root,root) %{_bindir}/sg_simple16 +%attr(755,root,root) %{_bindir}/sg_readcap +%attr(755,root,root) %{_bindir}/scsi_inquiry +%attr(755,root,root) %{_bindir}/sgp_dd +%attr(755,root,root) %{_bindir}/sg_map +%attr(755,root,root) %{_bindir}/sg_turs +%attr(755,root,root) %{_bindir}/sg_test_rwbuf +%attr(755,root,root) %{_bindir}/scsi_devfs_scan +%attr(755,root,root) %{_bindir}/sg_start +%attr(755,root,root) %{_bindir}/sgm_dd +%attr(755,root,root) %{_bindir}/sg_read +%attr(755,root,root) %{_bindir}/sg_reset +%attr(755,root,root) %{_bindir}/sg_modes +%attr(755,root,root) %{_bindir}/sg_logs +%attr(755,root,root) %{_bindir}/sg_senddiag +%attr(-,root,root) %doc %{_mandir}/man8/sg_dd.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sgp_dd.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sgm_dd.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sg_read.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sg_map.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sg_rbuf.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sg_inq.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sg_modes.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sg_logs.8.gz +%attr(-,root,root) %doc %{_mandir}/man8/sg_senddiag.8.gz + + +%changelog +* Wed Apr 02 2003 - dgilbert@interlog.com +- 6 byte CDBs for sg_modes, sg_start on block devs, sg_senddiag, man pages + * sg3_utils-1.03 +* Wed Jan 01 2003 - dgilbert@interlog.com +- interwork with block SG_IO, fix in sginfo, '-t' for sg_turs + * sg3_utils-1.02 +* Wed Aug 14 2002 - dgilbert@interlog.com +- raw switch in sg_inq + * sg3_utils-1.01 +* Sun Jul 28 2002 - dgilbert@interlog.com +- decode sg_logs pages, add dio to sgm_dd, drop "gen=1" arg, "of=/dev/null" + * sg3_utils-1.00 +* Sun Mar 17 2002 - dgilbert@interlog.com +- add sg_modes+sg_logs for sense pages, expand sg_inq, add fua+sync to sg_dd++ + * sg3_utils-0.99 +* Sat Feb 16 2002 - dgilbert@interlog.com +- resurrect sg_reset; snprintf cleanup, time,gen+cdbsz args to sg_dd++ + * sg3_utils-0.98 +* Sun Dec 23 2001 - dgilbert@interlog.com +- move isosize to archive directory; now found in util-linux-2.10s and later + * sg3_utils-0.97 +* Fri Dec 21 2001 - dgilbert@interlog.com +- add sgm_dd, sg_read, sg_simple4 and sg_simple16 [add mmap-ed IO support] + * sg3_utils-0.96 +* Sun Sep 15 2001 - dgilbert@interlog.com +- sg_map can do inquiry; sg_dd, sgp_dd + sgq_dd dio help + * sg3_utils-0.95 +* Sun Apr 19 2001 - dgilbert@interlog.com +- add sg_start, improve sginfo and sg_map [Kurt Garloff] + * sg3_utils-0.94 +* Sun Mar 5 2001 - dgilbert@interlog.com +- add scsi_devfs_scan, add sg_include.h, 'coe' more general in sgp_dd + * sg3_utils-0.93 +* Tue Jan 16 2001 - dgilbert@interlog.com +- clean sg_err.h include dependencies, bug fixes, Makefile in archive directory + * sg3_utils-0.92 +* Mon Dec 21 2000 - dgilbert@interlog.com +- signals for sg_dd, man pages and additions for sg_rbuf and isosize + * sg3_utils-0.91 +* Mon Dec 11 2000 - dgilbert@interlog.com +- Initial creation of package, containing + * sg3_utils-0.90 diff --git a/sg_inq.8 b/sg_inq.8 new file mode 100644 index 00000000..9995daf3 --- /dev/null +++ b/sg_inq.8 @@ -0,0 +1,85 @@ +.TH SG_INQ "8" "March 2003" "sg3_utils-1.03" SG3_UTILS +.SH NAME +sg_inq \- outputs data retrieved from the SCSI INQUIRY command +.SH SYNOPSIS +.B sg_inq +[\fI-c\fR] [\fI-cl>\fR] [\fI-e\fR] [\fI-h\fR] [\fI-o=\fR] +[\fI-p\fR] [\fI-r\fR] [\fI-V\fR] [\fI-36\fR] [\fI-?\fR] \fI\fR +.SH DESCRIPTION +.\" Add any additional description here +.PP +This command sends an INQUIRY SCSI command to the given device and then +outputs the response. All SCSI devices are meant to respond to +a "standard" INQUIRY command with at least a 36 byte response (in SCSI 2 +and higher). An INQUIRY is termed as "standard" when both the EVPD and +CmdDt bits are clear. +.TP +-c +set the Command Support Data (CmdDt) bit (defaults to clear(0)). Used +in conjunction with the '-o=' option to specify the SCSI command +opcode to supply the support data for. The command support data is a mask of +the same length as the command with bits set in positions that are +modifiable. For example, '12 03 ff 00 ff 01' shows the device +supports the EVPD and CmdDt bits ]byte 1 bits 0 and 1] in an INQUIRY command. +.TP +-cl +lists the command data for all supported commands (followed by the command +name) by looping through all 256 opcodes. +.TP +-e +enable (i.e. sets) the Vital Product Data (EVPD) bit (defaults to clear(0)). +Used in conjunction with the '-o=' option to specify the VPD page +to fetch. According to the SCSI 2 standard VPD page 0 lists the supported +VPD page numbers. +.TP +-h +outputs INQUIRY response in hex rather than trying to decode it. +.TP +-o= +used in conjunction with the '-c' option in which case the argument is +a command opcode. Alternatively it can be used in conjunction with +the '-e' option in which case the argument is a VPD page number. Argument +is hexadecimal and expected to be in the range 0 to ff inclusive. +.TP +-p +outputs PCI slot information of the given device (if there is any) after +the INQUIRY response. +.TP +-r +outputs the INQUIRY response in binary. Standard output should be redirected +to a file or some other program that can process binary data. +.TP +-V +print out version string +.TP +-36 +only requires 36 bytes of response data for an INQUIRY. Furthermore even +if the device indicates in its response it can supply more data, a +second (longer) INQUIRY is not performed. +.TP +-? +output usage message. Ignore all other parameters. +.PP +Some device with weak SCSI command set implementations lock up when +they receive commands they don't understand (or even response lengths +that they don't expect). Such devices need to be treated carefully +hence the '-36' option. Without this option this utility will issue +a standard INQUIRY requesting 36 bytes of response data. If the device +indicates it could have supplied more data then a second INQUIRY is +issued to fetch the longer response. +.PP +In the 2.4 series of Linux kernels the given device must be +a SCSI generic (sg) device. In the 2.5 series block devices (e.g. disks +and SCSI DVDs) can also be specified. For example "sg_inq /dev/sda" +will work in the 2.5 series kernels. +.SH AUTHOR +Written by Doug Gilbert +.SH "REPORTING BUGS" +Report bugs to . +.SH COPYRIGHT +Copyright \(co 2001-2003 Douglas Gilbert +.br +This software is distributed under the GPL version 2. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.SH "SEE ALSO" +.B sgdiag(scsirastools), sg_modes(sg3_utils), sg_logs(sg3_utils) diff --git a/sg_logs.8 b/sg_logs.8 new file mode 100644 index 00000000..08414d2b --- /dev/null +++ b/sg_logs.8 @@ -0,0 +1,70 @@ +.TH SG_LOGS "8" "March 2003" "sg3_utils-1.03" SG3_UTILS +.SH NAME +sg_logs \- reads SCSI LOG SENSE pages +.SH SYNOPSIS +.B sg_logs +[\fI-a\fR] [\fI-c=\fR] [\fI-h\fR] [\fI-l\fR] +[\fI-p=\fR] [\fI-paramp=\fR] [\fI-ppc\fR] +[\fI-sp\fR] [\fI-V\fR] [\fI-?\fR] \fI\fR +.SH DESCRIPTION +.\" Add any additional description here +.PP +This command sends a LOG SENSE SCSI command to the given device and then +outputs the response. There is no facility to change log page data with +this command. +.TP +-a +outputs all the log pages supported by the device. +.TP +-c=page_control +accepts 0, 1, 2 or 3 as an argument. 0 for current threshhold values, +1 for current cumulative values, 2 for default threshhold values and 3 +for default cumulative values. The default is 1 (i.e. current threshhold +values). +.TP +-h +suppresses decoding of known log sense pages and prints out the +response in hex instead. +.TP +-l +lists the names of all logs sense pages supported by this device. +.TP +-p= +log page code to fetch. Should be a hexadecimal number between 0 and 3f +inclusive. +.TP +-paramp= +parameter pointer value (in hex) to plave in command. Should be a number +between 0 and ffff inclusive. Requires PPC bit to be set to be active. +.TP +-ppc +sets the Parameter Pointer Control (PPC) bit. Default is 0 (i.e. cleared). +.TP +-sp +sets the Saving Parameters (SP) bit. Default is 0 (i.e. cleared). +.TP +-V +print out version string +.TP +-? +output usage message. Ignore all other parameters. +.PP +Various log pages hold information error rates, device temperature, +start stop cycles since device produced and the results of the last +20 self tests. Self tests can be initiated by the sg_senddiag command. +.PP +In the 2.4 series of Linux kernels the given device must be +a SCSI generic (sg) device. In the 2.5 series block devices (e.g. disks +and SCSI DVDs) can also be specified. For example "sg_logs -a /dev/sda" +will work in the 2.5 series kernels. +.SH AUTHOR +Written by Doug Gilbert +.SH "REPORTING BUGS" +Report bugs to . +.SH COPYRIGHT +Copyright \(co 2002-2003 Douglas Gilbert +.br +This software is distributed under the GPL version 2. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.SH "SEE ALSO" +.B smartmontools(see net), sg_senddiag(sg3_utils) diff --git a/sg_modes.8 b/sg_modes.8 new file mode 100644 index 00000000..dd53c888 --- /dev/null +++ b/sg_modes.8 @@ -0,0 +1,76 @@ +.TH SG_MODES "8" "March 2003" "sg3_utils-1.03" SG3_UTILS +.SH NAME +sg_modes \- reads SCSI MODE SENSE pages +.SH SYNOPSIS +.B sg_modes +[\fI-a\fR] [\fI-c=\fR] [\fI-d\fR] [\fI-h\fR] [\fI-l\fR] +[\fI-p=\fR] [\fI-V\fR] [\fI-6\fR] [\fI-?\fR] [\fI\fR] +.SH DESCRIPTION +.\" Add any additional description here +.PP +This command sends a MODE SENSE SCSI command (the 10 byte variant) +to the given device and then outputs the response. There is no facility +to change the page or descriptor data with this command. +.TP +-a +lists all the mode pages supported by the device. +.TP +-c=page_control +up to four different versions os each page are held by the device: +the current values [0] are the default (i.e. when this parameter is +not given), the changeable values [1] (bit mask showing which parts +could be changed with a MODE SELECT), default value [2] (from the +manufacturer?) and saved values [4] (when this device was last power +cycled?). +.TP +-d +disable block descriptors. By default MODE SENSE returns in its response +block descriptors and this parameter can be used to suppress them. +.TP +-h +currently only suppresses the printing out of known page code descriptions +.TP +-l +lists all known page codes. These are mainly for direct access +devices (disks) and those that apply to all devices. Ignores the given +device and most other parameters. +.TP +-p= +page code to fetch. Should be a hexadecimal number between 0 and 0x3f +inclusive. +.TP +-V +print out version string +.TP +-6 +by default this command sends out a 10 byte MODE SENSE command. However +some SCSI devices only support 6 byte MODE SENSE commands (e.g. SCSI-2 +tape drives). This parameter forces the use of 6 byte MODE SENSE commands. +.TP +-? +output usage message. Ignore all other parameters. +.PP +If the normal sg_modes command fails with "illegal command +operation code" then try the "-6" parameter. To alter page settings +see the program listed in the "See Also" section below. +.PP +This command performs a SCSI INQUIRY command to determine the peripheral +type of the device (e.g. 0 -> Direct Access Device (disk)) prior to +sending a MODE SENSE command. This helps in decoding the block +descriptor and mode pages. +.PP +In the 2.4 series of Linux kernels the given device must be +a SCSI generic (sg) device. In the 2.5 series block devices (e.g. disks +and SCSI DVDs) can also be specified. For example "sg_modes -a /dev/sda" +will work in the 2.5 series kernels. +.SH AUTHOR +Written by Doug Gilbert +.SH "REPORTING BUGS" +Report bugs to . +.SH COPYRIGHT +Copyright \(co 2000-2003 Douglas Gilbert +.br +This software is distributed under the GPL version 2. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.SH "SEE ALSO" +.B sginfo(this package), sgmode(scsirastools), scsiinfo(see net), scu(see net) diff --git a/sg_senddiag.8 b/sg_senddiag.8 new file mode 100644 index 00000000..2ad6c7fd --- /dev/null +++ b/sg_senddiag.8 @@ -0,0 +1,90 @@ +.TH SG_SENDDIAG "8" "March 2003" "sg3_utils-1.03" SG3_UTILS +.SH NAME +sg_senddiag \- performs a SCSI SEND DIAGNOSTIC command +.SH SYNOPSIS +.B sg_senddiag +[\fI-cpf\fR] [\fI-doff\fR] [\fI-h\fR] [\fI-l\fR] [\fI-s=\fR] +[\fI-t\fR] [\fI-uoff\fR] [\fI-V\fR] [\fI-?\fR] \fI\fR +.SH DESCRIPTION +.\" Add any additional description here +.PP +This command sends a SEND DIAGNOSTIC SCSI command to the given device. +In the case of '-l' option it then sends a RECEIVE DIAGNOSTIC SCSI +command to fetch the response (i.e. the page numbers of supported +diagnostic pages). This command is mainly used to make the device do +self tests. +.TP +-cpf +clear Page Format (PF) bit. By default it is set (i.e. 1). +.TP +-doff +set the Device Offline (DevOfl) bit (default is clear). Only significant +when '-t' option is set for the default self test. In this sense the 'Device' +is a target (with all contained logical units potentially impacted). +.TP +-e +outputs the time taken for the previous extended self test. The duration +is given in seconds (and minutes in parentheses). This figure is obtained +from mode page 0xa (control page). +.TP +-h +outputs response from RECEIVE DIAGNOSTIC is hex rather than decode it. +.TP +-l +lists the names of all diagnostic pages supported by this device. +The request is sent via a SEND DIAGNOSTIC command and the response +is fetched by a RECEIVE DIAGNOSTIC command. +.TP +-s= +the default value is 0 which is inactive. A value of 1 selects a background +short self test; 2 selects a background extended self test; 5 selects a +foreground short self test; 6 selects a foreground extended test. A value +of 4 will abort a (background) self test that is in progress. This +option is mutually exclusive with default self test (i.e. '-t'). +.TP +-t +sets the _default_ Self Test (SelfTest) bit. By default this is clear (0). +The '-s=' option should not be active together with this option. +Both the '-doff' and/or '-uoff' options can be used with this option. +.TP +-uoff +set the Unit Offline (DevOfl) bit (default is clear). Only significant +when '-t' option is set for the default self test. In this sense the 'Unit' +is a logical units on a target. +.TP +-V +print out version string +.TP +-? +output usage message. Ignore all other parameters. +.PP +All devices should support the default self test. The 'short' self test +codes should complete in 2 minutes or less. The 'extended' self test +codes' maximum duration is vendor specific (e.g. a little over 10 +minutes with my disks). The foreground self test codes wait until they +are completed while the background self test codes return immediately. +The results of both foreground and background self test codes are +placed in the 'self test results' log page (see sg_logs). The SCSI command +timeout for this utility is set to 40 minutes to allow for slow foreground +extended self tests. +.PP +If the given device is a disk then no file systems residing on that disk +should be mounted during a foreground self test. The reason is that +other SCSI commands may become queued behind the foreground self test and +timeout. +.PP +In the 2.4 series of Linux kernels the given device must be +a SCSI generic (sg) device. In the 2.5 series block devices (e.g. disks +and SCSI DVDs) can also be specified. For example 'sg_senddiag -t /dev/sda' +will work in the 2.5 series kernels. +.SH AUTHOR +Written by Doug Gilbert +.SH "REPORTING BUGS" +Report bugs to . +.SH COPYRIGHT +Copyright \(co 2003 Douglas Gilbert +.br +This software is distributed under the GPL version 2. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.SH "SEE ALSO" +.B smartmontools(see net), sg_logs(sg3_utils) diff --git a/sg_senddiag.c b/sg_senddiag.c new file mode 100644 index 00000000..913b2c14 --- /dev/null +++ b/sg_senddiag.c @@ -0,0 +1,452 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sg_include.h" +#include "sg_err.h" + +/* A utility program for the Linux OS SCSI generic ("sg") device driver. +* Copyright (C) 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. + + This program outputs information provided by a SCSI RECEIVE DIAGNOSTIC + command. +*/ + +static char * version_str = "0.13 20030331"; + +#define ME "sg_senddiag: " + +/* #define SG_DEBUG */ + +#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ +#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */ +#define LONG_TIMEOUT 2400000 /* 2,400,000 millisecs == 40 minutes */ + +#define SEND_DIAGNOSTIC_CMD 0x1d +#define SEND_DIAGNOSTIC_CMDLEN 6 +#define RECEIVE_DIAGNOSTIC_CMD 0x1c +#define RECEIVE_DIAGNOSTIC_CMDLEN 6 +#define MODE_SENSE6_CMD 0x1a +#define MODE_SENSE6_CMDLEN 6 +#define MODE_SENSE10_CMD 0x5a +#define MODE_SENSE10_CMDLEN 10 +#define MX_ALLOC_LEN (1024 * 4) + +#define PG_CODE_ALL 0x0 + +#define EBUFF_SZ 256 + + +static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit, + int devofl_bit, int unitofl_bit, void * outgoing_pg, + int outgoing_len, int noisy) +{ + int res; + unsigned char senddiagCmdBlk[SEND_DIAGNOSTIC_CMDLEN] = + {SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0}; + unsigned char sense_b[SENSE_BUFF_LEN]; + sg_io_hdr_t io_hdr; + + senddiagCmdBlk[1] = (unsigned char)((sf_code << 5) | (pf_bit << 4) | + (sf_bit << 2) | (devofl_bit << 1) | unitofl_bit); + senddiagCmdBlk[3] = (unsigned char)((outgoing_len >> 8) & 0xff); + senddiagCmdBlk[4] = (unsigned char)(outgoing_len & 0xff); + + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = SEND_DIAGNOSTIC_CMDLEN; + io_hdr.mx_sb_len = sizeof(sense_b); + io_hdr.dxfer_direction = outgoing_len ? SG_DXFER_TO_DEV : SG_DXFER_NONE; + io_hdr.dxfer_len = outgoing_len; + io_hdr.dxferp = outgoing_pg; + io_hdr.cmdp = senddiagCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = LONG_TIMEOUT; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + perror("SG_IO (send diagnostic) error"); + return -1; + } + res = sg_err_category3(&io_hdr); + switch (res) { + case SG_ERR_CAT_CLEAN: + case SG_ERR_CAT_RECOVERED: + return 0; + default: + if (noisy) { + char ebuff[EBUFF_SZ]; + snprintf(ebuff, EBUFF_SZ, "Send diagnostic error, sf_code=0x%x, " + "pf_bit=%d, sf_bit=%d ", sf_code, pf_bit, sf_bit); + sg_chk_n_print3(ebuff, &io_hdr); + } + return -1; + } +} + +static int do_rcvdiag(int sg_fd, int pcv, int pg_code, void * resp, + int mx_resp_len, int noisy) +{ + int res; + unsigned char rcvdiagCmdBlk[RECEIVE_DIAGNOSTIC_CMDLEN] = + {RECEIVE_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0}; + unsigned char sense_b[SENSE_BUFF_LEN]; + sg_io_hdr_t io_hdr; + + rcvdiagCmdBlk[1] = (unsigned char)(pcv ? 0x1 : 0); + rcvdiagCmdBlk[2] = (unsigned char)(pg_code); + rcvdiagCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); + rcvdiagCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); + + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = RECEIVE_DIAGNOSTIC_CMDLEN; + 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 = rcvdiagCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = DEF_TIMEOUT; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + perror("SG_IO (receive diagnostic) error"); + return -1; + } + res = sg_err_category3(&io_hdr); + switch (res) { + case SG_ERR_CAT_CLEAN: + case SG_ERR_CAT_RECOVERED: + return 0; + default: + if (noisy) { + char ebuff[EBUFF_SZ]; + snprintf(ebuff, EBUFF_SZ, "Receive diagnostic error, pcv=%d, " + "page_code=%x ", pcv, pg_code); + sg_chk_n_print3(ebuff, &io_hdr); + } + return -1; + } +} + +/* Get last extended self-test time from mode page 0xa (for '-e' option) */ +static int do_modes_0a(int sg_fd, void * resp, int mx_resp_len, int noisy, + int mode6) +{ + int res; + unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = + {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char sense_b[SENSE_BUFF_LEN]; + sg_io_hdr_t io_hdr; + int dbd = 1; + int pc = 0; + int pg_code = 0xa; + + modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0); + modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); + if (mx_resp_len > (mode6 ? 0xff : 0xffff)) { + printf( ME "mx_resp_len too big\n"); + return -1; + } + if(mode6) { + modesCmdBlk[0] = MODE_SENSE6_CMD; + modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); + } else { + modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); + modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); + } + + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN; + 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 = modesCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = DEF_TIMEOUT; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + perror("SG_IO (mode sense) error"); + return -1; + } + res = sg_err_category3(&io_hdr); + switch (res) { + case SG_ERR_CAT_CLEAN: + case SG_ERR_CAT_RECOVERED: + return 0; + default: + if (noisy) { + char ebuff[EBUFF_SZ]; + snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d, " + "pc=%d, page_code=%x ", dbd, pc, pg_code); + sg_chk_n_print3(ebuff, &io_hdr); + } + return -1; + } +} + +struct page_code_desc { + int page_code; + const char * desc; +}; +static struct page_code_desc pc_desc_arr[] = { + {0x0, "Supported diagnostic pages"}, + {0x1, "Configuration (SES)"}, + {0x2, "Enclosure status/control (SES)"}, + {0x3, "Help text (SES)"}, + {0x4, "String In/Out (SES)"}, + {0x5, "Threshold In/Out (SES)"}, + {0x6, "Array Status/Control (SES)"}, + {0x7, "Element descriptor (SES)"}, + {0x8, "Short enclosure status (SES)"}, + {0x9, "Enclosure busy (SES-2)"}, + {0xa, "Device element status (SES-2)"}, + {0x40, "Translate address (direct access)"}, + {0x41, "Device status (direct access)"}, +}; + +const char * find_page_code_desc(int page_num) +{ + int k; + int num = sizeof(pc_desc_arr) / sizeof(pc_desc_arr[0]); + const struct page_code_desc * pcdp = &pc_desc_arr[0]; + + for (k = 0; k < num; ++k, ++pcdp) { + if (page_num == pcdp->page_code) + return pcdp->desc; + else if (page_num < pcdp->page_code) + return NULL; + } + return NULL; +} + +static void list_page_codes() +{ + int k; + int num = sizeof(pc_desc_arr) / sizeof(pc_desc_arr[0]); + const struct page_code_desc * pcdp = &pc_desc_arr[0]; + + printf("Page_Code Description\n"); + for (k = 0; k < num; ++k, ++pcdp) + printf(" 0x%02x %s\n", pcdp->page_code, pcdp->desc); +} + +static void usage() +{ + printf("Usage: 'sg_senddiag [-cpf] [-doff] [-h] [-l]" + " [-s=]\n\t\t [-t] [-uoff] [-V] []'\n" + " where -cpf clear PF bit (def: 1)\n" + " -doff device online (def: 0, only with '-t')\n" + " -e duration of last extended test (from mode page 0xa)\n" + " -h output in hex\n" + " -l list supported page codes\n" + " -s= (def: 0)\n" + " 1->background short, 2->background extended," + " 4->abort test\n" + " 5->foreground short, 6->foreground extended\n" + " -t default self test\n" + " -uoff unit online (def: 0, only with '-t')\n" + " -V output version string\n" + " -? output this usage message\n"); +} + + +static void dStrHex(const char* str, int len, int no_ascii) +{ + const char* p = str; + unsigned char c; + char buff[82]; + int a = 0; + const int bpstart = 5; + const int cpstart = 60; + int cpos = cpstart; + int bpos = bpstart; + int i, k; + + if (len <= 0) return; + memset(buff,' ',80); + buff[80]='\0'; + k = sprintf(buff + 1, "%.2x", a); + buff[k + 1] = ' '; + if (bpos >= ((bpstart + (9 * 3)))) + bpos++; + + for(i = 0; i < len; i++) + { + c = *p++; + bpos += 3; + if (bpos == (bpstart + (9 * 3))) + bpos++; + sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); + buff[bpos + 2] = ' '; + if (no_ascii) + buff[cpos++] = ' '; + else { + if ((c < ' ') || (c >= 0x7f)) + c='.'; + buff[cpos++] = c; + } + if (cpos > (cpstart+15)) + { + printf("%s\n", buff); + bpos = bpstart; + cpos = cpstart; + a += 16; + memset(buff,' ',80); + k = sprintf(buff + 1, "%.2x", a); + buff[k + 1] = ' '; + } + } + if (cpos > cpstart) + { + printf("%s\n", buff); + } +} + + + +int main(int argc, char * argv[]) +{ + int sg_fd, k, num, rsp_len; + char * file_name = 0; + char ebuff[EBUFF_SZ]; + unsigned char rsp_buff[MX_ALLOC_LEN]; + int rsp_buff_size = MX_ALLOC_LEN; + unsigned int u; + int self_test_code = 0; + int do_pf = 1; + int do_doff = 0; + int do_hex = 0; + int do_list = 0; + int do_def_test = 0; + int do_uoff = 0; + int do_ext_time = 0; + int oflags = O_RDWR; + + for (k = 1; k < argc; ++k) { + if (0 == strncmp("-s=", argv[k], 3)) { + num = sscanf(argv[k] + 3, "%x", &u); + if ((1 != num) || (u > 7)) { + printf("Bad page code after '-s' switch\n"); + file_name = 0; + break; + } + self_test_code = u; + } + else if (0 == strcmp("-cpf", argv[k])) + do_pf = 0; + else if (0 == strcmp("-doff", argv[k])) + do_doff = 1; + else if (0 == strcmp("-h", argv[k])) + do_hex = 1; + else if (0 == strcmp("-l", argv[k])) + do_list = 1; + else if (0 == strcmp("-t", argv[k])) + do_def_test = 1; + else if (0 == strcmp("-uoff", argv[k])) + do_uoff = 1; + else if (0 == strcmp("-e", argv[k])) + do_ext_time = 1; + else if (0 == strcmp("-?", argv[k])) { + usage(); + return 0; + } + else if (0 == strcmp("-V", argv[k])) { + printf("Version string: %s\n", version_str); + exit(0); + } + else if (*argv[k] == '-') { + printf("Unrecognized switch: %s\n", argv[k]); + file_name = 0; + break; + } + else if (0 == file_name) + file_name = argv[k]; + else { + printf("too many arguments\n"); + file_name = 0; + break; + } + } + if ((do_doff || do_uoff) && (! do_def_test)) { + printf("setting -doff or -uoff only useful when -t is set\n"); + usage(); + return 1; + } + if ((self_test_code > 0) && do_def_test) { + printf("either set -s= or -t (not both)\n"); + usage(); + return 1; + } + if (0 == file_name) { + if (do_list) { + list_page_codes(); + return 0; + } + usage(); + return 1; + } + + if ((sg_fd = open(file_name, oflags)) < 0) { + snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", file_name); + perror(ebuff); + return 1; + } + /* Just to be safe, check we have a new sg device by trying an ioctl */ + if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { + printf( ME "%s doesn't seem to be a version 3 sg device\n", + file_name); + close(sg_fd); + return 1; + } + if (do_ext_time) { + if (0 == do_modes_0a(sg_fd, rsp_buff, 32, 1, 0)) { + /* Assume mode sense(10) response without block descriptors */ + num = (rsp_buff[0] << 8) + rsp_buff[1] - 6; + if (num >= 0xc) { + int secs; + + secs = (rsp_buff[18] << 8) + rsp_buff[19]; + printf("Previous extended self-test duration=%d seconds " + "(%.2f minutes)\n", secs, secs / 60.0); + } else + printf("Extended self-test duration not available\n"); + } else + printf("Extended self-test duration (mode page 0xa) failed\n"); + return 0; + } + if (do_list) { + memset(rsp_buff, 0, 4); + if (0 == do_senddiag(sg_fd, 0, do_pf, 0, 0, 0, rsp_buff, 4, 1)) { + if (0 == do_rcvdiag(sg_fd, 0, 0, rsp_buff, rsp_buff_size, 1)) { + printf("Supported diagnostic pages response:\n"); + rsp_len = (rsp_buff[2] << 8) + rsp_buff[3] + 4; + if (do_hex) + dStrHex((const char *)rsp_buff, rsp_len, 1); + else { + for (k = 0; k < (rsp_len - 4); ++k) + printf(" %s\n", find_page_code_desc(rsp_buff[k + 4])); + } + } + } + } + else if (0 == do_senddiag(sg_fd, self_test_code, do_pf, do_def_test, + do_doff, do_uoff, NULL, 0, 1)) { + if ((5 == self_test_code) || (6 == self_test_code)) + printf("Foreground self test returned GOOD status\n"); + else if (do_def_test && (! do_doff) && (! do_uoff)) + printf("Default self test returned GOOD status\n"); + } + close(sg_fd); + return 0; +} -- cgit v1.2.3