diff options
Diffstat (limited to 'sg_opcodes.c')
-rw-r--r-- | sg_opcodes.c | 1248 |
1 files changed, 922 insertions, 326 deletions
diff --git a/sg_opcodes.c b/sg_opcodes.c index fc2d0e57..017fb38c 100644 --- a/sg_opcodes.c +++ b/sg_opcodes.c @@ -1,32 +1,59 @@ +/* A utility program originally written for the Linux OS SCSI subsystem. + * Copyright (C) 2004-2007 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 REPORT SUPPORTED + OPERATION CODES [0xa3/0xc] and REPORT SUPPORTED TASK MANAGEMENT + FUNCTIONS [0xa3/0xd] commands. + */ + #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 <sys/stat.h> +#include <getopt.h> + #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" -#include "sg_io_linux.h" -/* A utility program for the Linux OS SCSI subsystem. -* Copyright (C) 2004-2006 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. +static char * version_str = "0.29 20070127"; - This program outputs information provided by a SCSI "Report supported - operation codes" command [0xa3/0xc]. +// #define USE_LINUX_SG_IO_IF 1 -*/ +/* Notes: + * - this file has both Linux specific pass through code using the + * SG_IO ioctl and a more generic sg_pt mechanism that is portable + * to other OSes. The code is conditionally compiled depending + * on the USE_LINUX_SG_IO_IF define and whether the Makefile + * indicates the OS is linux. + * N.B. Various Makefiles are set assuming this is not defined. + * + * - since support for the SCSI REPORT SUPPORTED OPERATION CODES and + * REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS is uncommon, dummy + * response code is provided. Uncomment the '#define TEST_CODE' + * line for test mode. + */ -static char * version_str = "0.26 20061015"; +#if defined(USE_LINUX_SG_IO_IF) && defined(SG3_UTILS_LINUX) + #define USE_SG_IO +#endif +#ifdef USE_SG_IO + #include <sys/ioctl.h> + #include <sys/types.h> + #include <sys/stat.h> + + #include "sg_io_linux.h" + #define EBUFF_SZ 256 + static char ebuff[EBUFF_SZ]; +#else + #include "sg_pt.h" +#endif #define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ #define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */ @@ -40,21 +67,29 @@ static char * version_str = "0.26 20061015"; #define NAME_BUFF_SZ 64 -#define EBUFF_SZ 256 static int peri_type = 0; /* ugly but not easy to pass to alpha compare */ +static int do_rsoc(int sg_fd, int rctd, int rep_opts, int rq_opcode, + int rq_servact, void * resp, int mx_resp_len, int noisy, + int verbose); +static int do_rstmf(int sg_fd, void * resp, int mx_resp_len, int noisy, + int verbose); + /* <<<<<<<<<<<<<<< start of test code */ -/* #define TEST_CODE */ +// #define TEST_CODE #ifdef TEST_CODE #warning "<<<< TEST_CODE response compiled in >>>>" #define DUMMY_CMDS 17 +#define DUMMY_CMD_LEN 8 +#define DUMMY_TO_CMDS 4 +#define DUMMY_TO_CMD_LEN 20 struct cmd_descript_t { - unsigned char d[8]; + unsigned char d[DUMMY_CMD_LEN]; }; struct dummy_resp_t { @@ -62,190 +97,355 @@ struct dummy_resp_t { struct cmd_descript_t descript[DUMMY_CMDS]; }; -static struct dummy_resp_t dummy_resp = { {0, 0, 0, 8 * DUMMY_CMDS}, - {{{0, 0, 0, 0, 0, 0, 0, 6}}, - {{0xa3, 0, 0, 0xc, 0, 1, 0, 12}}, - {{0x12, 0, 0, 0, 0, 0, 0, 6}}, - {{0x1d, 0, 0, 0, 0, 0, 0, 6}}, - {{0x25, 0, 0, 0, 0, 0, 0, 10}}, - {{0x28, 0, 0, 0, 0, 0, 0, 10}}, - {{0x2a, 0, 0, 0, 0, 0, 0, 10}}, - {{0x1a, 0, 0, 0, 0, 0, 0, 6}}, - {{0x15, 0, 0, 0, 0, 0, 0, 6}}, - {{0xa3, 0, 0, 0x5, 0, 1, 0, 12}}, - {{0x5a, 0, 0, 0, 0, 0, 0, 10}}, - {{0x55, 0, 0, 0, 0, 0, 0, 10}}, - {{2, 0, 0, 0, 0, 0, 0, 6}}, - {{3, 0, 0, 0, 0, 0, 0, 6}}, - {{4, 0, 0, 0, 0, 0, 0, 6}}, - {{0xa0, 0, 0, 0, 0, 0, 0, 12}}, - {{0x7f, 0, 0, 0x1, 0, 1, 0, 32}}, +static struct dummy_resp_t dummy_resp = { + {0, 0, 0, DUMMY_CMD_LEN * DUMMY_CMDS}, + {{{0, 0, 0, 0, 0, 0, 0, 6}}, /* tur */ + {{0xa3, 0, 0, 0xc, 0, 1, 0, 12}}, /* rsoc */ + {{0x12, 0, 0, 0, 0, 0, 0, 6}}, /* inq */ + {{0x1d, 0, 0, 0, 0, 0, 0, 6}}, /* sd */ + {{0x25, 0, 0, 0, 0, 0, 0, 10}}, /* rc */ + {{0x28, 0, 0, 0, 0, 0, 0, 10}}, /* r(10) */ + {{0x2a, 0, 0, 0, 0, 0, 0, 10}}, /* w(10) */ + {{0x1a, 0, 0, 0, 0, 0, 0, 6}}, /* ms(6) */ + {{0x15, 0, 0, 0, 0, 0, 0, 6}}, /* msel(6) */ + {{0xa3, 0, 0, 0x5, 0, 1, 0, 12}}, /* rii */ + {{0x5a, 0, 0, 0, 0, 0, 0, 10}}, /* ms(10) */ + {{0x55, 0, 0, 0, 0, 0, 0, 10}}, /* msel(10) */ + {{2, 0, 0, 0, 0, 0, 0, 6}}, /* ?? */ + {{3, 0, 0, 0, 0, 0, 0, 6}}, /* rs */ + {{4, 0, 0, 0, 0, 0, 0, 6}}, /* f */ + {{0xa0, 0, 0, 0, 0, 0, 0, 12}}, /* rl */ + {{0x7f, 0, 0, 0x3, 0, 1, 0, 32}}, /* vl:xdr(32) */ +}}; + +struct cmd_descript_to_t { + unsigned char d[DUMMY_TO_CMD_LEN]; +}; + +struct dummy_resp_to_t { + unsigned char cdl[4]; + struct cmd_descript_to_t descript[DUMMY_TO_CMDS]; +}; + +static struct dummy_resp_to_t dummy_to_resp = + {{0, 0, 0, DUMMY_TO_CMD_LEN * DUMMY_TO_CMDS}, + {{{0, 0, 0, 0, 0, 0x2, 0, 6, + 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}}, /* tur */ + {{0xa3, 0, 0, 0xc, 0, 0x3, 0, 12, + 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6}}, /* rsoc */ + {{4, 0, 0, 0, 0, 0x2, 0, 6, + 0, 0xa, 0, 0, 0, 0, 0x8, 0, 0, 0, 0x10, 0}}, /* f */ + {{0x7f, 0, 0, 0x3, 0, 0x3, 0, 32, + 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7}}, /* vl:xdr(32) */ }}; static unsigned char dummy_1_cmd[] = { 0, 3, 0, 6, 0x12, 0x3, 0xff, 0x0, 0xff, 0x1 }; +static unsigned char dummy_1_to_cmd[] = { + 0, 0x83, 0, 6, 0x12, 0x3, 0xff, 0x0, 0xff, 0x1, + 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, /* inq */ +}; + static unsigned char dummy_rsmft_r0 = 0xff; static unsigned char dummy_rsmft_r1 = 0x1; #endif /* <<<<<<<<<<<<<<< end of test code */ +static struct option long_options[] = { + {"alpha", 0, 0, 'a'}, + {"help", 0, 0, 'h'}, + {"hex", 0, 0, 'H'}, + {"new", 0, 0, 'N'}, + {"opcode", 1, 0, 'o'}, + {"old", 0, 0, 'O'}, + {"raw", 0, 0, 'r'}, + {"rctd", 0, 0, 'R'}, + {"sa", 1, 0, 's'}, + {"tmf", 0, 0, 't'}, + {"unsorted", 0, 0, 'u'}, + {"verbose", 0, 0, 'v'}, + {"version", 0, 0, 'V'}, + {0, 0, 0, 0}, +}; -/* Report Supported Operation Codes */ -/* Returns 0 when successful */ -static int do_rsoc(int sg_fd, int rep_opts, int rq_opcode, int rq_servact, - void * resp, int mx_resp_len, int noisy, int verbose) +struct opts_t { + int do_alpha; + int do_help; + int do_hex; + int do_opcode; + int do_raw; + int do_rctd; + int do_servact; + int do_verbose; + int do_version; + int do_unsorted; + int do_taskman; + const char * device_name; + int opt_new; +}; + +static void usage() { - int res, k; - unsigned char rsocCmdBlk[RSOC_CMD_LEN] = {SG_MAINTENANCE_IN, RSOC_SA, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - struct sg_io_hdr io_hdr; + fprintf(stderr, + "Usage: sg_opcodes [--alpha] [--help] [--hex] [--opcode=OP] " + "[--raw] [--rctd]\n" + " [--sa=SA] [--tmf] [--unsorted] [--verbose] " + "[--version]\n" + " DEVICE\n" + " where:\n" + " --alpha|-a output list of operation codes sorted " + "alphabetically\n" + " --help|-h print usage message then exit\n" + " --hex|-H output response in hex\n" + " --opcode=OP|-o OP first byte of command to query\n" + " (decimal, prefix with '0x' for hex)\n" + " --raw|-r output response in binary to stdout\n" + " --rctd|-R set RCTD (return command timeout " + "descriptor) bit\n" + " --sa=SA|-s SA service action in addition to opcode\n" + " (decimal, prefix with '0x' for hex)\n" + " --tmf|-t output list of supported task management " + "functions\n" + " --unsorted|-u output list of operation codes as is " + "(unsorted)\n" + " --verbose|-v increase verbosity\n" + " --version|-V print vesrion string then exit\n\n" + "Performs a SCSI REPORT SUPPORTED OPERATION CODES or REPORT " + "SUPPORTED\nTASK MANAGEMENT FUNCTIONS command\n"); +} - if (rep_opts) - rsocCmdBlk[2] = (rep_opts & 0x7); - if (rq_opcode > 0) - rsocCmdBlk[3] = (rq_opcode & 0xff); - if (rq_servact > 0) { - rsocCmdBlk[4] = (unsigned char)((rq_servact >> 8) & 0xff); - rsocCmdBlk[5] = (unsigned char)(rq_servact & 0xff); +static void usage_old() +{ + fprintf(stderr, + "Usage: sg_opcodes [-a] [-H] [-o=OP] [-r] [-R] [-s=SA]" + " [-t] [-u]\n" + " [-v] [-V] DEVICE\n" + " where:\n" + " -a output list of operation codes sorted " + "alphabetically\n" + " -H print response in hex\n" + " -o=OP first byte of command to query (in hex)\n" + " -r output response in binary to stdout\n" + " -R set RCTD (return command timeout " + "descriptor) bit\n" + " -s=SA in addition to opcode (in hex)\n" + " -t output list of supported task management functions\n" + " -u output list of operation codes as is (unsorted)\n" + " -v verbose\n" + " -V output version string\n" + " -? output this usage message\n\n" + "Performs a SCSI REPORT SUPPORTED OPERATION CODES (or REPORT " + "TASK MANAGEMENT\nFUNCTIONS) command\n"); +} - } - rsocCmdBlk[6] = (unsigned char)((mx_resp_len >> 24) & 0xff); - rsocCmdBlk[7] = (unsigned char)((mx_resp_len >> 16) & 0xff); - rsocCmdBlk[8] = (unsigned char)((mx_resp_len >> 8) & 0xff); - rsocCmdBlk[9] = (unsigned char)(mx_resp_len & 0xff); +static int process_cl_new(struct opts_t * optsp, int argc, char * argv[]) +{ + int c, n; - if (verbose) { - fprintf(stderr, " Report Supported Operation Codes cmd: "); - for (k = 0; k < RSOC_CMD_LEN; ++k) - fprintf(stderr, "%02x ", rsocCmdBlk[k]); - fprintf(stderr, "\n"); - } - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(rsocCmdBlk); - 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 = rsocCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; + while (1) { + int option_index = 0; - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (rsoc) error"); - return -1; - } - if (verbose > 2) - fprintf(stderr, " duration=%u ms\n", io_hdr.duration); - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_LIB_CAT_RECOVERED: - sg_chk_n_print3("Report supported operation codes", &io_hdr, - verbose > 1); - /* fall through */ - case SG_LIB_CAT_CLEAN: - return 0; - default: - if (noisy | verbose) { - char ebuff[EBUFF_SZ]; + c = getopt_long(argc, argv, "ahHNo:OrRs:tuvV", long_options, + &option_index); + if (c == -1) + break; - if (0 == rep_opts) - snprintf(ebuff, EBUFF_SZ, "RSOC error, rep_opts=0 (all) "); - else if (1 == rep_opts) - snprintf(ebuff, EBUFF_SZ, "RSOC error, rq_opcode=0x%x ", - rq_opcode); - else - snprintf(ebuff, EBUFF_SZ, "RSOC error, rq_opcode=0x%x, " - "rq_sa=0x%x ", rq_opcode, rq_servact); - sg_chk_n_print3(ebuff, &io_hdr, verbose > 1); + switch (c) { + case 'a': + optsp->do_alpha = 1; + break; + case 'h': + case '?': + ++optsp->do_help; + break; + case 'H': + ++optsp->do_hex; + break; + case 'N': + break; /* ignore */ + case 'o': + n = sg_get_num(optarg); + if ((n < 0) || (n > 255)) { + fprintf(stderr, "bad argument to '--opcode'\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + optsp->do_opcode = n; + break; + case 'O': + optsp->opt_new = 0; + return 0; + case 'r': + ++optsp->do_raw; + break; + case 'R': + ++optsp->do_rctd; + break; + case 's': + n = sg_get_num(optarg); + if (n < 0) { + fprintf(stderr, "bad argument to '--sa'\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + optsp->do_servact = n; + break; + case 't': + ++optsp->do_taskman; + break; + case 'u': + ++optsp->do_unsorted; + break; + case 'v': + ++optsp->do_verbose; + break; + case 'V': + ++optsp->do_version; + break; + default: + fprintf(stderr, "unrecognised switch code %c [0x%x]\n", c, c); + if (optsp->do_help) + break; + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + if (optind < argc) { + if (NULL == optsp->device_name) { + optsp->device_name = argv[optind]; + ++optind; + } + if (optind < argc) { + for (; optind < argc; ++optind) + fprintf(stderr, "Unexpected extra argument: %s\n", + argv[optind]); + usage(); + return SG_LIB_SYNTAX_ERROR; } - return res; } + return 0; } -/* Report Supported Task Management Function */ -/* Returns 0 when successful */ -static int do_rstmf(int sg_fd, void * resp, int mx_resp_len, int noisy, - int verbose) +static int process_cl_old(struct opts_t * optsp, int argc, char * argv[]) { - int res, k; - unsigned char rstmfCmdBlk[RSTMF_CMD_LEN] = {SG_MAINTENANCE_IN, RSTMF_SA, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned char sense_b[SENSE_BUFF_LEN]; - struct sg_io_hdr io_hdr; - - rstmfCmdBlk[6] = (unsigned char)((mx_resp_len >> 24) & 0xff); - rstmfCmdBlk[7] = (unsigned char)((mx_resp_len >> 16) & 0xff); - rstmfCmdBlk[8] = (unsigned char)((mx_resp_len >> 8) & 0xff); - rstmfCmdBlk[9] = (unsigned char)(mx_resp_len & 0xff); + int k, jmp_out, plen, n, num; + const char * cp; - if (verbose) { - fprintf(stderr, " Report Supported Task Management Functions cmd: "); - for (k = 0; k < RSTMF_CMD_LEN; ++k) - fprintf(stderr, "%02x ", rstmfCmdBlk[k]); - fprintf(stderr, "\n"); + for (k = 1; k < argc; ++k) { + cp = argv[k]; + plen = strlen(cp); + if (plen <= 0) + continue; + if ('-' == *cp) { + for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) { + switch (*cp) { + case 'a': + ++optsp->do_alpha; + break; + case 'H': + ++optsp->do_hex; + break; + case 'N': + optsp->opt_new = 1; + return 0; + case 'O': + break; + case 'R': + ++optsp->do_rctd; + break; + case 't': + ++optsp->do_taskman; + break; + case 'u': + ++optsp->do_unsorted; + break; + case 'v': + ++optsp->do_verbose; + break; + case 'V': + ++optsp->do_version; + break; + case 'h': + case '?': + ++optsp->do_help; + break; + default: + jmp_out = 1; + break; + } + if (jmp_out) + break; + } + if (plen <= 0) + continue; + if (0 == strncmp("o=", cp, 2)) { + num = sscanf(cp + 2, "%x", (unsigned int *)&n); + if ((1 != num) || (n > 255)) { + fprintf(stderr, "Bad number after 'o=' option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + optsp->do_opcode = n; + } else if (0 == strncmp("s=", cp, 2)) { + num = sscanf(cp + 2, "%x", (unsigned int *)&n); + if (1 != num) { + fprintf(stderr, "Bad number after 's=' option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + optsp->do_servact = n; + } else if (0 == strncmp("-old", cp, 4)) + ; + else if (jmp_out) { + fprintf(stderr, "Unrecognized option: %s\n", cp); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + } else if (NULL == optsp->device_name) + optsp->device_name = cp; + else { + fprintf(stderr, "too many arguments, got: %s, not expecting: " + "%s\n", optsp->device_name, cp); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } } - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(rstmfCmdBlk); - 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 = rstmfCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; + return 0; +} - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (rstmf) error"); - return -1; - } - if (verbose > 2) - fprintf(stderr, " duration=%u ms\n", io_hdr.duration); - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_LIB_CAT_RECOVERED: - sg_chk_n_print3("Report supported task management fns", &io_hdr, - verbose > 1); - /* fall through */ - case SG_LIB_CAT_CLEAN: - return 0; - default: - if (noisy | verbose) { - char ebuff[EBUFF_SZ]; - snprintf(ebuff, EBUFF_SZ, "RSTMF error "); - sg_chk_n_print3(ebuff, &io_hdr, verbose > 1); - } - return res; +static int process_cl(struct opts_t * optsp, int argc, char * argv[]) +{ + int res; + char * cp; + + cp = getenv("SG3_UTILS_OLD_OPTS"); + if (cp) { + optsp->opt_new = 0; + res = process_cl_old(optsp, argc, argv); + if ((0 == res) && optsp->opt_new) + res = process_cl_new(optsp, argc, argv); + } else { + optsp->opt_new = 1; + res = process_cl_new(optsp, argc, argv); + if ((0 == res) && (0 == optsp->opt_new)) + res = process_cl_old(optsp, argc, argv); } + return res; } -static void usage() +static void dStrRaw(const char* str, int len) { - fprintf(stderr, - "Usage: sg_opcodes [-a] [-o=<opcode> [-s=<service_action>] ]" - " [-t] [-u] [-v]\n" - " [-V] <scsi_device>\n" - " where:\n" - " -a output list of operation codes sorted " - "alphabetically\n" - " -o=<opcode> first byte of command to query (in hex)\n" - " -s=<service_action> in addition to opcode (in hex)\n" - " -t output list of supported task management functions\n" - " -u output list of operation codes as is (unsorted)\n" - " -v verbose\n" - " -V output version string\n" - " -? output this usage message\n\n" - "Performs a REPORT SUPPORTED OPERATION CODES (or supported task " - "management\nfunctions) SCSI command\n"); + int k; + + for (k = 0 ; k < len; ++k) + printf("%c", str[k]); } /* returns -1 when left < right, 0 when left == right, else returns 1 */ -int opcode_num_compare(const void * left, const void * right) +static int opcode_num_compare(const void * left, const void * right) { const unsigned char * ll = *(unsigned char **)left; const unsigned char * rr = *(unsigned char **)right; @@ -275,7 +475,7 @@ int opcode_num_compare(const void * left, const void * right) } /* returns -1 when left < right, 0 when left == right, else returns 1 */ -int opcode_alpha_compare(const void * left, const void * right) +static int opcode_alpha_compare(const void * left, const void * right) { const unsigned char * ll = *(unsigned char **)left; const unsigned char * rr = *(unsigned char **)right; @@ -304,10 +504,11 @@ int opcode_alpha_compare(const void * left, const void * right) return strncmp(l_name_buff, r_name_buff, NAME_BUFF_SZ); } -void list_all_codes(unsigned char * rsoc_buff, int rsoc_len, int unsorted, - int alpha) +static void list_all_codes(unsigned char * rsoc_buff, int rsoc_len, + int unsorted, int alpha, int rctd) { - int k, cd_len, serv_act; + int k, j, cd_len, serv_act, len; + unsigned long to; unsigned char * ucp; char name_buff[NAME_BUFF_SZ]; char sa_buff[8]; @@ -324,12 +525,19 @@ void list_all_codes(unsigned char * rsoc_buff, int rsoc_len, int unsorted, printf("sg_opcodes: no commands to display\n"); return; } - printf("\nOpcode Service CDB Name\n"); - printf( "(hex) action(h) size \n"); - printf("-----------------------------------------------\n"); + if (rctd) { + printf("\nOpcode Service CDB Nominal Recommended Name\n"); + printf( "(hex) action(h) size timeout timeout(sec) \n"); + printf("-----------------------------------------------------------" + "-----\n"); + } else { + printf("\nOpcode Service CDB Name\n"); + printf( "(hex) action(h) size \n"); + printf("-----------------------------------------------\n"); + } /* N.B. SPC-4 does _not_ requiring any ordering of response */ if (! unsorted) { - sort_arr = malloc(cd_len * sizeof(unsigned char *)); + sort_arr = (unsigned char **)malloc(cd_len * sizeof(unsigned char *)); if (NULL == sort_arr) { printf("sg_opcodes: no memory to sort operation codes, " "try '-u'\n"); @@ -337,13 +545,16 @@ void list_all_codes(unsigned char * rsoc_buff, int rsoc_len, int unsorted, } memset(sort_arr, 0, cd_len * sizeof(unsigned char *)); ucp = rsoc_buff + 4; - for (k = 0; k < cd_len; k += 8, ucp += 8) - sort_arr[(k / 8)] = ucp; - qsort(sort_arr, (cd_len / 8), sizeof(unsigned char *), + for (k = 0, j = 0; k < cd_len; ++j, k += len, ucp += len) { + sort_arr[j] = ucp; + len = (ucp[5] & 0x2) ? 20 : 8; + } + qsort(sort_arr, j, sizeof(unsigned char *), (alpha ? opcode_alpha_compare : opcode_num_compare)); } - for (k = 0; k < cd_len; k += 8) { - ucp = unsorted ? (rsoc_buff + 4 + k) : sort_arr[(k / 8)]; + for (k = 0, j = 0; k < cd_len; ++j, k += len) { + ucp = unsorted ? (rsoc_buff + 4 + k) : sort_arr[j]; + len = (ucp[5] & 0x2) ? 20 : 8; if (ucp[5] & 1) { serv_act = ((ucp[2] << 8) | ucp[3]); sg_get_opcode_sa_name(ucp[0], serv_act, peri_type, @@ -354,161 +565,252 @@ void list_all_codes(unsigned char * rsoc_buff, int rsoc_len, int unsorted, NAME_BUFF_SZ, name_buff); memset(sa_buff, ' ', sizeof(sa_buff)); } - printf(" %.2x %.4s %3d %s\n", - ucp[0], sa_buff, ((ucp[6] << 8) | ucp[7]), name_buff); + if (rctd) { + if (ucp[5] & 0x2) { + printf(" %.2x %.4s %3d", ucp[0], sa_buff, + ((ucp[6] << 8) | ucp[7])); + to = (ucp[12] << 24) + (ucp[13] << 16) + (ucp[14] << 8) + + ucp[15]; + if (0 == to) + printf(" -"); + else + printf(" %8lu", to); + to = (ucp[16] << 24) + (ucp[17] << 16) + (ucp[18] << 8) + + ucp[19]; + if (0 == to) + printf(" -"); + else + printf(" %8lu", to); + printf(" %s\n", name_buff); + } else + printf(" %.2x %.4s %3d " + "%s\n", ucp[0], sa_buff, ((ucp[6] << 8) | ucp[7]), + name_buff); + } else + printf(" %.2x %.4s %3d %s\n", + ucp[0], sa_buff, ((ucp[6] << 8) | ucp[7]), name_buff); + } +} + +static void decode_cmd_to_descriptor(unsigned char * dp, int max_b_len, + char * b) +{ + int len; + unsigned long to; + + if ((max_b_len < 2) || (NULL == dp)) + return; + b[max_b_len - 1] = '\0'; + --max_b_len; + len = (dp[0] << 8) + dp[1]; + if (10 != len) { + snprintf(b, max_b_len, "command timeout descriptor length %d " + "(expect 10)", len); + return; + } + to = (dp[4] << 24) + (dp[5] << 16) + (dp[6] << 8) + dp[7]; + if (0 == to) + snprintf(b, max_b_len, "no nominal timeout, "); + else + snprintf(b, max_b_len, "nominal timeout: %lu secs, ", to); + len = strlen(b); + max_b_len -= len; + b += len; + to = (dp[8] << 24) + (dp[9] << 16) + (dp[10] << 8) + dp[11]; + if (0 == to) + snprintf(b, max_b_len, "no recommended timeout"); + else + snprintf(b, max_b_len, "recommended timeout: %lu secs", to); + return; +} + +static void list_one(unsigned char * rsoc_buff, int cd_len, int rep_opts, + int do_opcode, int do_servact) +{ + int k; + char name_buff[NAME_BUFF_SZ]; + unsigned char * ucp; + const char * cp; + int v = 0; + + + printf("\n Opcode=0x%.2x", do_opcode); + if (rep_opts > 1) + printf(" Service_action=0x%.4x", do_servact); + printf("\n"); + sg_get_opcode_sa_name(((do_opcode > 0) ? do_opcode : 0), + ((do_servact > 0) ? do_servact : 0), + peri_type, NAME_BUFF_SZ, name_buff); + printf(" Command_name: %s\n", name_buff); + switch((int)(rsoc_buff[1] & 7)) { + case 0: + cp = "not currently available"; + break; + case 1: + cp = "NOT supported"; + break; + case 3: + cp = "supported (conforming to SCSI standard)"; + v = 1; + break; + case 5: + cp = "supported (in a vendor specific manner)"; + v = 1; + break; + default: + snprintf(name_buff, NAME_BUFF_SZ, "support reserved [0x%x]", + rsoc_buff[1] & 7); + cp = name_buff; + break; + } + printf(" Command %s\n", cp); + if (v) { + printf(" Usage data: "); + ucp = rsoc_buff + 4; + for (k = 0; k < cd_len; ++k) + printf("%.2x ", ucp[k]); + printf("\n"); + } + if (0x80 & rsoc_buff[1]) { /* CTDP */ + ucp = rsoc_buff + 4 + cd_len; + decode_cmd_to_descriptor(ucp, NAME_BUFF_SZ, name_buff); + printf(" %s\n", name_buff); } } int main(int argc, char * argv[]) { - int sg_fd, k, num, cd_len, plen, jmp_out, res; - const char * file_name = 0; - char ebuff[EBUFF_SZ]; + int sg_fd, cd_len, res, len; unsigned char rsoc_buff[MX_ALLOC_LEN]; - unsigned char * ucp; - char name_buff[NAME_BUFF_SZ]; - int do_alpha = 0; - int do_opcode = -1; - int do_servact = -1; - int do_verbose = 0; - int do_unsorted = 0; - int do_taskman = 0; int rep_opts = 0; const char * cp; char buff[48]; struct sg_simple_inquiry_resp inq_resp; const char * op_name; + struct opts_t opts; - for (k = 1; k < argc; ++k) { - cp = argv[k]; - plen = strlen(cp); - if (plen <= 0) - continue; - if ('-' == *cp) { - for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) { - switch (*cp) { - case 'a': - do_alpha = 1; - break; - case 't': - do_taskman = 1; - break; - case 'u': - do_unsorted = 1; - break; - case 'v': - ++do_verbose; - break; - case 'V': - fprintf(stderr, "Version string: %s\n", version_str); - exit(0); - case 'h': - case '?': - usage(); - return 0; - default: - jmp_out = 1; - break; - } - if (jmp_out) - break; - } - if (plen <= 0) - continue; - if (0 == strncmp("o=", cp, 2)) { - num = sscanf(cp + 2, "%x", (unsigned int *)&do_opcode); - if ((1 != num) || (do_opcode > 255)) { - fprintf(stderr, "Bad number after 'o=' option\n"); - usage(); - return SG_LIB_SYNTAX_ERROR; - } - } else if (0 == strncmp("s=", cp, 2)) { - num = sscanf(cp + 2, "%x", (unsigned int *)&do_servact); - if (1 != num) { - fprintf(stderr, "Bad number after 's=' option\n"); - usage(); - return SG_LIB_SYNTAX_ERROR; - } - } else if (jmp_out) { - fprintf(stderr, "Unrecognized option: %s\n", cp); - usage(); - return SG_LIB_SYNTAX_ERROR; - } - } else if (0 == file_name) - file_name = cp; - else { - fprintf(stderr, "too many arguments, got: %s, not expecting: " - "%s\n", file_name, cp); + memset(&opts, 0, sizeof(opts)); + opts.do_opcode = -1; + opts.do_servact = -1; + res = process_cl(&opts, argc, argv); + if (res) + return SG_LIB_SYNTAX_ERROR; + if (opts.do_help) { + if (opts.opt_new) usage(); - return SG_LIB_SYNTAX_ERROR; - } + else + usage_old(); + return 0; + } + if (opts.do_version) { + fprintf(stderr, "Version string: %s\n", version_str); + return 0; } - if (0 == file_name) { - fprintf(stderr, "No <scsi_device> argument given\n"); - usage(); + if (NULL == opts.device_name) { + fprintf(stderr, "No DEVICE argument given\n"); + if (opts.opt_new) + usage(); + else + usage_old(); return SG_LIB_SYNTAX_ERROR; } - if ((-1 != do_servact) && (-1 == do_opcode)) { + if ((-1 != opts.do_servact) && (-1 == opts.do_opcode)) { fprintf(stderr, "When '-s' is chosen, so must '-o' be chosen\n"); - usage(); + if (opts.opt_new) + usage(); + else + usage_old(); return SG_LIB_SYNTAX_ERROR; } - if (do_unsorted && do_alpha) + if (opts.do_unsorted && opts.do_alpha) fprintf(stderr, "warning: unsorted ('-u') and alpha ('-a') options " "chosen, ignoring alpha\n"); - if (do_taskman && ((-1 != do_opcode) || do_alpha || do_unsorted)) { + if (opts.do_taskman && ((-1 != opts.do_opcode) || opts.do_alpha || + opts.do_unsorted)) { fprintf(stderr, "warning: task management functions ('-t') chosen " "so alpha ('-a'),\n unsorted ('-u') and opcode " "('-o') options ignored\n"); } - op_name = do_taskman ? "Report supported task management functions" : + op_name = opts.do_taskman ? "Report supported task management functions" : "Report supported operation codes"; - if ((sg_fd = open(file_name, O_RDONLY | O_NONBLOCK)) < 0) { - snprintf(ebuff, EBUFF_SZ, "sg_opcodes: error opening file: %s (ro)", - file_name); +#ifdef USE_SG_IO + if ((sg_fd = open(opts.device_name, O_RDONLY | O_NONBLOCK)) < 0) { + snprintf(ebuff, EBUFF_SZ, "sg_opcodes: error opening file (ro): %s", + opts.device_name); perror(ebuff); return SG_LIB_FILE_ERROR; } - if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, do_verbose)) { - printf(" %.8s %.16s %.4s\n", inq_resp.vendor, inq_resp.product, - inq_resp.revision); +#else + if ((sg_fd = scsi_pt_open_device(opts.device_name, 1 /* RO */, + opts.do_verbose)) < 0) { + fprintf(stderr, "sg_opcodes: error opening file (ro): %s: %s\n", + opts.device_name, safe_strerror(-sg_fd)); + return SG_LIB_FILE_ERROR; + } +#endif + if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, opts.do_verbose)) { peri_type = inq_resp.peripheral_type; - cp = sg_get_pdt_str(peri_type, sizeof(buff), buff); - if (strlen(cp) > 0) - printf(" Peripheral device type: %s\n", cp); - else - printf(" Peripheral device type: 0x%x\n", peri_type); + if (0 == opts.do_raw) { + printf(" %.8s %.16s %.4s\n", inq_resp.vendor, inq_resp.product, + inq_resp.revision); + cp = sg_get_pdt_str(peri_type, sizeof(buff), buff); + if (strlen(cp) > 0) + printf(" Peripheral device type: %s\n", cp); + else + printf(" Peripheral device type: 0x%x\n", peri_type); + } } else { - printf("sg_opcodes: %s doesn't respond to a SCSI INQUIRY\n", file_name); + fprintf(stderr, "sg_opcodes: %s doesn't respond to a SCSI " + "INQUIRY\n", opts.device_name); return SG_LIB_CAT_OTHER; } +#ifdef USE_SG_IO close(sg_fd); +#else + res = sg_cmds_close_device(sg_fd); + if (res < 0) { + fprintf(stderr, "close error: %s\n", safe_strerror(-res)); + return SG_LIB_FILE_ERROR; + } +#endif + #ifndef TEST_CODE if (5 == peri_type) { - printf("'%s' command not supported\nfor CD/DVD devices\n", op_name); + printf("'%s' command not supported\nfor CD/DVD devices\n", + op_name); return SG_LIB_CAT_OTHER; } #endif - if ((sg_fd = open(file_name, O_RDWR | O_NONBLOCK)) < 0) { +#ifdef USE_SG_IO + if ((sg_fd = open(opts.device_name, O_RDWR | O_NONBLOCK)) < 0) { snprintf(ebuff, EBUFF_SZ, "sg_opcodes: error opening file: %s (rw)", - file_name); + opts.device_name); perror(ebuff); return SG_LIB_FILE_ERROR; } - if (do_opcode >= 0) - rep_opts = ((do_servact >= 0) ? 2 : 1); +#else + if ((sg_fd = scsi_pt_open_device(opts.device_name, 0 /* RW */, + opts.do_verbose)) < 0) { + fprintf(stderr, "sg_opcodes: error opening file (rw): %s: %s\n", + opts.device_name, safe_strerror(-sg_fd)); + return SG_LIB_FILE_ERROR; + } +#endif + if (opts.do_opcode >= 0) + rep_opts = ((opts.do_servact >= 0) ? 2 : 1); memset(rsoc_buff, 0, sizeof(rsoc_buff)); #ifndef TEST_CODE - if (do_taskman) + if (opts.do_taskman) res = do_rstmf(sg_fd, rsoc_buff, sizeof(rsoc_buff), 0, - do_verbose); + opts.do_verbose); else - res = do_rsoc(sg_fd, rep_opts, do_opcode, do_servact, rsoc_buff, - sizeof(rsoc_buff), 0, do_verbose); + res = do_rsoc(sg_fd, opts.do_rctd, rep_opts, opts.do_opcode, + opts.do_servact, rsoc_buff, sizeof(rsoc_buff), 0, + opts.do_verbose); switch (res) { case 0: case SG_LIB_CAT_RECOVERED: @@ -526,21 +828,46 @@ int main(int argc, char * argv[]) fprintf(stderr, "%s: operation not supported\n", op_name); goto err_out; case SG_LIB_CAT_ILLEGAL_REQ: - fprintf(stderr, "%s: bad field in cdb\n", op_name); + fprintf(stderr, "bad field in cdb including %s not supported\n", + op_name); goto err_out; default: fprintf(stderr, "%s failed\n", op_name); goto err_out; } #else - if (do_taskman) { + if (opts.do_taskman) { rsoc_buff[0] = dummy_rsmft_r0; rsoc_buff[1] = dummy_rsmft_r1; - } else - memcpy(rsoc_buff, (unsigned char *)&dummy_resp, sizeof(dummy_resp)); + } else if (opts.do_rctd) { + if (0 == rep_opts) { +#if 1 + memcpy(rsoc_buff, (unsigned char *)&dummy_to_resp, + sizeof(dummy_to_resp)); +#else + memcpy(rsoc_buff, (unsigned char *)&dummy_resp, + sizeof(dummy_resp)); +#endif + } else + memcpy(rsoc_buff, dummy_1_to_cmd, sizeof(dummy_1_to_cmd)); + } else { + if (0 == rep_opts) + memcpy(rsoc_buff, (unsigned char *)&dummy_resp, + sizeof(dummy_resp)); + else + memcpy(rsoc_buff, dummy_1_cmd, sizeof(dummy_1_cmd)); + } #endif - if (do_taskman) { + if (opts.do_taskman) { + if (opts.do_raw) { + dStrRaw((const char *)rsoc_buff, 4); + goto err_out; + } printf("\nTask Management Functions supported by device:\n"); + if (opts.do_hex) { + dStrHex((const char *)rsoc_buff, 4, 1); + goto err_out; + } if (rsoc_buff[0] & 0x80) printf(" Abort task\n"); if (rsoc_buff[0] & 0x40) @@ -559,47 +886,316 @@ int main(int argc, char * argv[]) printf(" Wakeup\n"); if (rsoc_buff[1] & 0x1) printf(" I_T nexus reset\n"); - } else if (0 == rep_opts) /* list all supported operation codes */ - list_all_codes(rsoc_buff, sizeof(rsoc_buff), do_unsorted, do_alpha); - else { /* asked about specific command */ - const char * cp; - int v = 0; - -#ifdef TEST_CODE - memcpy(rsoc_buff, dummy_1_cmd, sizeof(dummy_1_cmd)); -#endif - printf("\n Opcode=0x%.2x", do_opcode); - if (rep_opts > 1) - printf(" Service_action=0x%.4x", do_servact); - printf("\n"); - sg_get_opcode_sa_name(((do_opcode > 0) ? do_opcode : 0), - ((do_servact > 0) ? do_servact : 0), - peri_type, NAME_BUFF_SZ, name_buff); - printf(" Command_name: %s\n", name_buff); - switch((int)(rsoc_buff[1] & 7)) { - case 0: cp = "not currently available"; break; - case 1: cp = "NOT supported"; break; - case 3: cp = "supported (conforming to SCSI standard)"; v = 1; break; - case 5: cp = "supported (in a vendor specific manner)"; v = 1; break; - default: - snprintf(name_buff, NAME_BUFF_SZ, "support reserved [0x%x]", - rsoc_buff[1] & 7); - cp = name_buff; - break; + } else if (0 == rep_opts) { /* list all supported operation codes */ + len = ((rsoc_buff[0] << 24) | (rsoc_buff[1] << 16) | + (rsoc_buff[2] << 8) | rsoc_buff[3]) + 4; + if (len > (int)sizeof(rsoc_buff)) + len = sizeof(rsoc_buff); + if (opts.do_raw) { + dStrRaw((const char *)rsoc_buff, len); + goto err_out; + } + if (opts.do_hex) { + dStrHex((const char *)rsoc_buff, len, 1); + goto err_out; + } + list_all_codes(rsoc_buff, sizeof(rsoc_buff), opts.do_unsorted, + opts.do_alpha, opts.do_rctd); + } else { /* asked about specific command */ + cd_len = ((rsoc_buff[2] << 8) | rsoc_buff[3]); + len = cd_len + 4; + if (len > (int)sizeof(rsoc_buff)) + len = sizeof(rsoc_buff); + if (opts.do_raw) { + dStrRaw((const char *)rsoc_buff, len); + goto err_out; } - printf(" Command %s\n", cp); - if (v) { - printf(" Usage data: "); - cd_len = ((rsoc_buff[2] << 8) | rsoc_buff[3]); - ucp = rsoc_buff + 4; - for (k = 0; k < cd_len; ++k) - printf("%.2x ", ucp[k]); - printf("\n"); + if (opts.do_hex) { + dStrHex((const char *)rsoc_buff, len, 1); + goto err_out; } + list_one(rsoc_buff, cd_len, rep_opts, opts.do_opcode, + opts.do_servact); } res = 0; err_out: +#ifdef USE_SG_IO close(sg_fd); +#else + sg_cmds_close_device(sg_fd); +#endif return res; } + + +#ifdef USE_SG_IO +/* Report Supported Operation Codes */ +/* Returns 0 when successful */ +static int do_rsoc(int sg_fd, int rctd, int rep_opts, int rq_opcode, + int rq_servact, void * resp, int mx_resp_len, int noisy, + int verbose) +{ + int res, k; + unsigned char rsocCmdBlk[RSOC_CMD_LEN] = {SG_MAINTENANCE_IN, RSOC_SA, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char sense_b[SENSE_BUFF_LEN]; + struct sg_io_hdr io_hdr; + + if (rctd) + rsocCmdBlk[2] |= 0x80; + if (rep_opts) + rsocCmdBlk[2] |= (rep_opts & 0x7); + if (rq_opcode > 0) + rsocCmdBlk[3] = (rq_opcode & 0xff); + if (rq_servact > 0) { + rsocCmdBlk[4] = (unsigned char)((rq_servact >> 8) & 0xff); + rsocCmdBlk[5] = (unsigned char)(rq_servact & 0xff); + + } + rsocCmdBlk[6] = (unsigned char)((mx_resp_len >> 24) & 0xff); + rsocCmdBlk[7] = (unsigned char)((mx_resp_len >> 16) & 0xff); + rsocCmdBlk[8] = (unsigned char)((mx_resp_len >> 8) & 0xff); + rsocCmdBlk[9] = (unsigned char)(mx_resp_len & 0xff); + + if (verbose) { + fprintf(stderr, " Report Supported Operation Codes cmd: "); + for (k = 0; k < RSOC_CMD_LEN; ++k) + fprintf(stderr, "%02x ", rsocCmdBlk[k]); + fprintf(stderr, "\n"); + } + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(rsocCmdBlk); + 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 = rsocCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = DEF_TIMEOUT; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + perror("SG_IO (rsoc) error"); + return -1; + } + if (verbose > 2) + fprintf(stderr, " duration=%u ms\n", io_hdr.duration); + res = sg_err_category3(&io_hdr); + switch (res) { + case SG_LIB_CAT_RECOVERED: + sg_chk_n_print3("Report supported operation codes", &io_hdr, + verbose > 1); + /* fall through */ + case SG_LIB_CAT_CLEAN: + return 0; + default: + if (noisy | verbose) { + char ebuff[EBUFF_SZ]; + + if (0 == rep_opts) + snprintf(ebuff, EBUFF_SZ, "RSOC error, rep_opts=0 (all) "); + else if (1 == rep_opts) + snprintf(ebuff, EBUFF_SZ, "RSOC error, rq_opcode=0x%x ", + rq_opcode); + else + snprintf(ebuff, EBUFF_SZ, "RSOC error, rq_opcode=0x%x, " + "rq_sa=0x%x ", rq_opcode, rq_servact); + sg_chk_n_print3(ebuff, &io_hdr, verbose > 1); + } + return res; + } +} + +/* Report Supported Task Management Function */ +/* Returns 0 when successful */ +static int do_rstmf(int sg_fd, void * resp, int mx_resp_len, int noisy, + int verbose) +{ + int res, k; + unsigned char rstmfCmdBlk[RSTMF_CMD_LEN] = {SG_MAINTENANCE_IN, RSTMF_SA, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char sense_b[SENSE_BUFF_LEN]; + struct sg_io_hdr io_hdr; + + rstmfCmdBlk[6] = (unsigned char)((mx_resp_len >> 24) & 0xff); + rstmfCmdBlk[7] = (unsigned char)((mx_resp_len >> 16) & 0xff); + rstmfCmdBlk[8] = (unsigned char)((mx_resp_len >> 8) & 0xff); + rstmfCmdBlk[9] = (unsigned char)(mx_resp_len & 0xff); + + if (verbose) { + fprintf(stderr, " Report Supported Task Management Functions " + "cmd: "); + for (k = 0; k < RSTMF_CMD_LEN; ++k) + fprintf(stderr, "%02x ", rstmfCmdBlk[k]); + fprintf(stderr, "\n"); + } + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(rstmfCmdBlk); + 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 = rstmfCmdBlk; + io_hdr.sbp = sense_b; + io_hdr.timeout = DEF_TIMEOUT; + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + perror("SG_IO (rstmf) error"); + return -1; + } + if (verbose > 2) + fprintf(stderr, " duration=%u ms\n", io_hdr.duration); + res = sg_err_category3(&io_hdr); + switch (res) { + case SG_LIB_CAT_RECOVERED: + sg_chk_n_print3("Report supported task management fns", &io_hdr, + verbose > 1); + /* fall through */ + case SG_LIB_CAT_CLEAN: + return 0; + default: + if (noisy | verbose) { + char ebuff[EBUFF_SZ]; + snprintf(ebuff, EBUFF_SZ, "RSTMF error "); + sg_chk_n_print3(ebuff, &io_hdr, verbose > 1); + } + return res; + } +} + +#else /* use generic pass through code instead */ + +static int do_rsoc(int sg_fd, int rctd, int rep_opts, int rq_opcode, + int rq_servact, void * resp, int mx_resp_len, int noisy, + int verbose) +{ + int k, ret, res, sense_cat; + unsigned char rsocCmdBlk[RSOC_CMD_LEN] = {SG_MAINTENANCE_IN, RSOC_SA, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char sense_b[SENSE_BUFF_LEN]; + void * ptvp; + + if (rctd) + rsocCmdBlk[2] |= 0x80; + if (rep_opts) + rsocCmdBlk[2] |= (rep_opts & 0x7); + if (rq_opcode > 0) + rsocCmdBlk[3] = (rq_opcode & 0xff); + if (rq_servact > 0) { + rsocCmdBlk[4] = (unsigned char)((rq_servact >> 8) & 0xff); + rsocCmdBlk[5] = (unsigned char)(rq_servact & 0xff); + + } + rsocCmdBlk[6] = (unsigned char)((mx_resp_len >> 24) & 0xff); + rsocCmdBlk[7] = (unsigned char)((mx_resp_len >> 16) & 0xff); + rsocCmdBlk[8] = (unsigned char)((mx_resp_len >> 8) & 0xff); + rsocCmdBlk[9] = (unsigned char)(mx_resp_len & 0xff); + + if (verbose) { + fprintf(stderr, " Report Supported Operation Codes cmd: "); + for (k = 0; k < RSOC_CMD_LEN; ++k) + fprintf(stderr, "%02x ", rsocCmdBlk[k]); + fprintf(stderr, "\n"); + } + ptvp = construct_scsi_pt_obj(); + if (NULL == ptvp) { + fprintf(sg_warnings_strm, "Report Supported Operation Codes: out " + "of memory\n"); + return -1; + } + set_scsi_pt_cdb(ptvp, rsocCmdBlk, sizeof(rsocCmdBlk)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_TIMEOUT, verbose); + ret = sg_cmds_process_resp(ptvp, "Report Supported Operation Codes", res, + mx_resp_len, sense_b, noisy, verbose, + &sense_cat); + if (-1 == ret) + ; + else if (-2 == ret) { + switch (sense_cat) { + case SG_LIB_CAT_NOT_READY: + case SG_LIB_CAT_UNIT_ATTENTION: + case SG_LIB_CAT_INVALID_OP: + case SG_LIB_CAT_ILLEGAL_REQ: + case SG_LIB_CAT_ABORTED_COMMAND: + ret = sense_cat; + break; + case SG_LIB_CAT_RECOVERED: + case SG_LIB_CAT_NO_SENSE: + ret = 0; + break; + default: + ret = -1; + break; + } + } else + ret = 0; + + destruct_scsi_pt_obj(ptvp); + return ret; +} + +static int do_rstmf(int sg_fd, void * resp, int mx_resp_len, int noisy, + int verbose) +{ + int k, ret, res, sense_cat; + unsigned char rstmfCmdBlk[RSTMF_CMD_LEN] = {SG_MAINTENANCE_IN, RSTMF_SA, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char sense_b[SENSE_BUFF_LEN]; + void * ptvp; + + rstmfCmdBlk[6] = (unsigned char)((mx_resp_len >> 24) & 0xff); + rstmfCmdBlk[7] = (unsigned char)((mx_resp_len >> 16) & 0xff); + rstmfCmdBlk[8] = (unsigned char)((mx_resp_len >> 8) & 0xff); + rstmfCmdBlk[9] = (unsigned char)(mx_resp_len & 0xff); + + if (verbose) { + fprintf(stderr, " Report Supported Task Management Functions " + "cmd: "); + for (k = 0; k < RSTMF_CMD_LEN; ++k) + fprintf(stderr, "%02x ", rstmfCmdBlk[k]); + fprintf(stderr, "\n"); + } + ptvp = construct_scsi_pt_obj(); + if (NULL == ptvp) { + fprintf(sg_warnings_strm, "Report Supported Task Management " + "Functions: out of memory\n"); + return -1; + } + set_scsi_pt_cdb(ptvp, rstmfCmdBlk, sizeof(rstmfCmdBlk)); + set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); + set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); + res = do_scsi_pt(ptvp, sg_fd, DEF_TIMEOUT, verbose); + ret = sg_cmds_process_resp(ptvp, "Report Supported Task management " + "functions", res, mx_resp_len, sense_b, noisy, + verbose, &sense_cat); + if (-1 == ret) + ; + else if (-2 == ret) { + switch (sense_cat) { + case SG_LIB_CAT_NOT_READY: + case SG_LIB_CAT_UNIT_ATTENTION: + case SG_LIB_CAT_INVALID_OP: + case SG_LIB_CAT_ILLEGAL_REQ: + case SG_LIB_CAT_ABORTED_COMMAND: + ret = sense_cat; + break; + case SG_LIB_CAT_RECOVERED: + case SG_LIB_CAT_NO_SENSE: + ret = 0; + break; + default: + ret = -1; + break; + } + } else + ret = 0; + + destruct_scsi_pt_obj(ptvp); + return ret; +} + +#endif |