aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2007-06-27 02:51:54 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2007-06-27 02:51:54 +0000
commit6e64b86f968c387672eac691acad20f91b4bd526 (patch)
tree14ba576188efe92a7f2b8ec3df10c70a842e4dff
parent0529313d55953cc1b84650f29411afc2670417aa (diff)
downloadsg3_utils-6e64b86f968c387672eac691acad20f91b4bd526.tar.gz
Load sg3_utils-1.03 into trunk/.
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@29 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--archive/sg_err.h4
-rw-r--r--sg3_utils.spec128
-rw-r--r--sg_inq.885
-rw-r--r--sg_logs.870
-rw-r--r--sg_modes.876
-rw-r--r--sg_senddiag.890
-rw-r--r--sg_senddiag.c452
7 files changed, 902 insertions, 3 deletions
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 <dgilbert@interlog.com>
+ Kurt Garloff <garloff@suse.de> [sg_test_rwbuff]
+ Peter Allworth [contribution to sg_dd and sgp_dd]
+ Martin Schwenke <martin@meltin.net> [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=<opcode_page>\fR]
+[\fI-p\fR] [\fI-r\fR] [\fI-V\fR] [\fI-36\fR] [\fI-?\fR] \fI<sg_device>\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=<opcode>' 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=<page>' 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=<opcode_page>
+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 <dgilbert@interlog.com>.
+.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=<page_control>\fR] [\fI-h\fR] [\fI-l\fR]
+[\fI-p=<page_code>\fR] [\fI-paramp=<parameter_pointer>\fR] [\fI-ppc\fR]
+[\fI-sp\fR] [\fI-V\fR] [\fI-?\fR] \fI<sg_device>\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=<page_code>
+log page code to fetch. Should be a hexadecimal number between 0 and 3f
+inclusive.
+.TP
+-paramp=<parameter_pointer>
+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 <dgilbert@interlog.com>.
+.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=<page_control>\fR] [\fI-d\fR] [\fI-h\fR] [\fI-l\fR]
+[\fI-p=<page_code>\fR] [\fI-V\fR] [\fI-6\fR] [\fI-?\fR] [\fI<sg_device>\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>
+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 <dgilbert@interlog.com>.
+.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=<self_test_code>\fR]
+[\fI-t\fR] [\fI-uoff\fR] [\fI-V\fR] [\fI-?\fR] \fI<sg_device>\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=<self_test_code>
+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=<num>' 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 <dgilbert@interlog.com>.
+.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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#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=<self_test_code>]\n\t\t [-t] [-uoff] [-V] [<sg_device>]'\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=<self_test_code> (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=<num> 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;
+}