aboutsummaryrefslogtreecommitdiff
path: root/sg_senddiag.c
diff options
context:
space:
mode:
Diffstat (limited to 'sg_senddiag.c')
-rw-r--r--sg_senddiag.c452
1 files changed, 452 insertions, 0 deletions
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;
+}