/
githubmirror
/
sg3_utils
Обзор
Документация
Войти
/
githubmirror
/
sg3_utils
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
release-1.13
sg_opcodes.c
584 строки
19 KB
Douglas Gilbert
Load sg3_utils-1.13 into trunk/.
27 июн 2007, 07:06
27 июн 2007, 07:06
e459cae
Код
Авторство
О чём код?
#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 "sg_include.h" #include "sg_lib.h" #include "sg_cmds.h" /* A utility program for the Linux OS SCSI subsystem. * Copyright (C) 2004-2005 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" command [0xa3/0xc]. */ static char * version_str = "0.17 20050309"; #define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ #define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */ #define SG_MAINTENANCE_IN 0xa3 #define RSOC_SA 0xc #define RSTMF_SA 0xd #define RSOC_CMD_LEN 12 #define RSTMF_CMD_LEN 12 #define MX_ALLOC_LEN 8192 #define NAME_BUFF_SZ 64 #define EBUFF_SZ 256 static int peri_type = 0; /* ugly but not easy to pass to alpha compare */ /* <<<<<<<<<<<<<<< start of test code */ /* #define TEST_CODE */ #ifdef TEST_CODE #warning "<<<< TEST_CODE response compiled in >>>>" #define DUMMY_CMDS 17 struct cmd_descript_t { unsigned char d[8]; }; struct dummy_resp_t { unsigned char cdl[4]; 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 unsigned char dummy_1_cmd[] = { 0, 3, 0, 6, 0x12, 0x3, 0xff, 0x0, 0xff, 0x1 }; static unsigned char dummy_rsmft_r0 = 0xff; #endif /* <<<<<<<<<<<<<<< end of test code */ /* Report Supported Operation Codes */ /* Returns 0 when successful, else -1 */ 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) { 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 (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; } res = sg_err_category3(&io_hdr); switch (res) { case SG_LIB_CAT_RECOVERED: sg_chk_n_print3("Report supported operation codes", &io_hdr); /* 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); } return -1; } } /* Report Supported Task Management Function */ /* Returns 0 when successful, else -1 */ 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; } res = sg_err_category3(&io_hdr); switch (res) { case SG_LIB_CAT_RECOVERED: sg_chk_n_print3("Report supported task management fns", &io_hdr); /* 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); } return -1; } } static void usage() { fprintf(stderr, "Usage: 'sg_opcodes [-a] [-o=<opcode> [-s=<service_action>] ]" " [-t] [-u] [-v]\n" " [-V] <scsi_device>'\n" " where -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"); } static const char * scsi_ptype_strs[] = { /* 0 */ "disk", "tape", "printer", "processor", "write once optical disk", /* 5 */ "cd/dvd", "scanner", "optical memory device", "medium changer", "communications", /* 0xa */ "graphics [0xa]", "graphics [0xb]", "storage array controller", "enclosure services device", "simplified direct access device", "optical card reader/writer device", /* 0x10 */ "bridge controller commands", "object based storage", "automation/driver interface", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19", "0x1a", "0x1b", "0x1c", "0x1d", "well known logical unit", "no physical device on this lu", }; static const char * get_ptype_str(int scsi_ptype) { int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]); return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : ""; } /* returns -1 when left < right, 0 when left == right, else returns 1 */ int opcode_num_compare(const void * left, const void * right) { const unsigned char * ll = *(unsigned char **)left; const unsigned char * rr = *(unsigned char **)right; int l_serv_act = 0; int r_serv_act = 0; int l_opc, r_opc; if (NULL == ll) return -1; if (NULL == rr) return -1; l_opc = ll[0]; if (ll[5] & 1) l_serv_act = ((ll[2] << 8) | ll[3]); r_opc = rr[0]; if (rr[5] & 1) r_serv_act = ((rr[2] << 8) | rr[3]); if (l_opc < r_opc) return -1; if (l_opc > r_opc) return 1; if (l_serv_act < r_serv_act) return -1; if (l_serv_act > r_serv_act) return 1; return 0; } /* returns -1 when left < right, 0 when left == right, else returns 1 */ int opcode_alpha_compare(const void * left, const void * right) { const unsigned char * ll = *(unsigned char **)left; const unsigned char * rr = *(unsigned char **)right; int l_serv_act = 0; int r_serv_act = 0; char l_name_buff[NAME_BUFF_SZ]; char r_name_buff[NAME_BUFF_SZ]; int l_opc, r_opc; if (NULL == ll) return -1; if (NULL == rr) return -1; l_opc = ll[0]; if (ll[5] & 1) l_serv_act = ((ll[2] << 8) | ll[3]); l_name_buff[0] = '\0'; sg_get_opcode_sa_name(l_opc, l_serv_act, peri_type, NAME_BUFF_SZ, l_name_buff); r_opc = rr[0]; if (rr[5] & 1) r_serv_act = ((rr[2] << 8) | rr[3]); r_name_buff[0] = '\0'; sg_get_opcode_sa_name(r_opc, r_serv_act, peri_type, NAME_BUFF_SZ, r_name_buff); 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) { int k, cd_len, serv_act; unsigned char * ucp; char name_buff[NAME_BUFF_SZ]; char sa_buff[8]; unsigned char ** sort_arr = NULL; cd_len = ((rsoc_buff[0] << 24) | (rsoc_buff[1] << 16) | (rsoc_buff[2] << 8) | rsoc_buff[3]); if (cd_len > (rsoc_len - 4)) { printf("sg_opcodes: command data length=%d, allocation=%d; " "truncate\n", cd_len, rsoc_len - 4); cd_len = ((rsoc_len - 4) / 8) * 8; } if (0 == cd_len) { printf("sg_opcodes: no commands to display\n"); return; } printf("\nOpcode Service CDB Name\n"); printf( "(hex) action(h) size \n"); printf("-----------------------------------------------\n"); /* N.B. SPC-3 does _not_ requiring any ordering of response */ if (! unsorted) { sort_arr = malloc(cd_len * sizeof(unsigned char *)); if (NULL == sort_arr) { printf("sg_opcodes: no memory to sort operation codes, " "try '-u'\n"); return; } 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 *), (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)]; if (ucp[5] & 1) { serv_act = ((ucp[2] << 8) | ucp[3]); sg_get_opcode_sa_name(ucp[0], serv_act, peri_type, NAME_BUFF_SZ, name_buff); snprintf(sa_buff, sizeof(sa_buff), "%.4x", serv_act); } else { sg_get_opcode_name(ucp[0], peri_type, 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); } } int main(int argc, char * argv[]) { int sg_fd, k, num, cd_len; char * file_name = 0; char ebuff[EBUFF_SZ]; 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; int ret = 0; const char * cp; struct sg_simple_inquiry_resp inq_resp; for (k = 1; k < argc; ++k) { if (0 == strncmp("-o=", argv[k], 3)) { num = sscanf(argv[k] + 3, "%x", (unsigned int *)&do_opcode); if ((1 != num) || (do_opcode > 255)) { fprintf(stderr, "Bad number after '-o' switch\n"); file_name = 0; break; } } else if (0 == strncmp("-s=", argv[k], 3)) { num = sscanf(argv[k] + 3, "%x", (unsigned int *)&do_servact); if (1 != num) { fprintf(stderr, "Bad number after '-s' switch\n"); file_name = 0; break; } } else if (0 == strcmp("-a", argv[k])) do_alpha = 1; else if (0 == strcmp("-t", argv[k])) do_taskman = 1; else if (0 == strcmp("-u", argv[k])) do_unsorted = 1; else if (0 == strcmp("-v", argv[k])) ++do_verbose; else if (0 == strcmp("-?", argv[k])) { file_name = 0; break; } else if (0 == strcmp("-V", argv[k])) { fprintf(stderr, "Version string: %s\n", version_str); exit(0); } else if (*argv[k] == '-') { fprintf(stderr, "Unrecognized switch: %s\n", argv[k]); file_name = 0; break; } else if (0 == file_name) file_name = argv[k]; else { fprintf(stderr, "too many arguments\n"); file_name = 0; break; } } if (0 == file_name) { usage(); return 1; } if ((-1 != do_servact) && (-1 == do_opcode)) { fprintf(stderr, "When '-s' is chosen, so must '-o' be chosen\n"); usage(); return 1; } if (do_unsorted && 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)) { fprintf(stderr, "warning: task management functions ('-t') chosen " "so alpha ('-a'),\n unsorted ('-u') and opcode " "('-o') options ignored\n"); } if ((sg_fd = open(file_name, O_RDONLY | O_NONBLOCK)) < 0) { snprintf(ebuff, EBUFF_SZ, "sg_opcodes: error opening file: %s (ro)", file_name); perror(ebuff); return 1; } 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); peri_type = inq_resp.peripheral_type; cp = get_ptype_str(peri_type); 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); return 1; } close(sg_fd); #ifndef TEST_CODE if (5 == peri_type) { if (do_taskman) printf("'Report supported task management functions' command not " "supported\nfor CD/DVD devices\n"); else printf("'Report supported operation codes' command not " "supported for CD/DVD devices\n"); return 1; } #endif if ((sg_fd = open(file_name, O_RDWR | O_NONBLOCK)) < 0) { snprintf(ebuff, EBUFF_SZ, "sg_opcodes: error opening file: %s (rw)", file_name); perror(ebuff); return 1; } if (do_opcode >= 0) rep_opts = ((do_servact >= 0) ? 2 : 1); memset(rsoc_buff, 0, sizeof(rsoc_buff)); #ifndef TEST_CODE if (do_taskman) { if (0 != do_rstmf(sg_fd, rsoc_buff, sizeof(rsoc_buff), 1, do_verbose)) return 1; } else { if (0 != do_rsoc(sg_fd, rep_opts, do_opcode, do_servact, rsoc_buff, sizeof(rsoc_buff), 1, do_verbose)) return 1; } #else if (do_taskman) rsoc_buff[0] = dummy_rsmft_r0; else memcpy(rsoc_buff, (unsigned char *)&dummy_resp, sizeof(dummy_resp)); #endif if (do_taskman) { printf("\nTask Management Functions supported by device:\n"); if (rsoc_buff[0] & 0x80) printf(" Abort task\n"); if (rsoc_buff[0] & 0x40) printf(" Abort task set\n"); if (rsoc_buff[0] & 0x20) printf(" Clear ACA\n"); if (rsoc_buff[0] & 0x10) printf(" Clear task set\n"); if (rsoc_buff[0] & 0x8) printf(" Logical unit reset\n"); if (rsoc_buff[0] & 0x4) printf(" Query task\n"); if (rsoc_buff[0] & 0x2) printf(" Target reset\n"); if (rsoc_buff[0] & 0x1) printf(" Wakeup\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; } 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"); } } close(sg_fd); return ret; }