2002-10-12 13:10:01 +02:00
|
|
|
/*
|
2006-08-09 22:40:20 +02:00
|
|
|
* scsicmds.cpp
|
2002-10-12 13:10:01 +02:00
|
|
|
*
|
2019-12-29 14:13:06 +01:00
|
|
|
* Home page of code is: https://www.smartmontools.org
|
2002-10-15 16:24:27 +02:00
|
|
|
*
|
2015-05-10 18:42:50 +02:00
|
|
|
* Copyright (C) 2002-8 Bruce Allen
|
2002-10-12 13:10:01 +02:00
|
|
|
* Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
|
2023-02-10 21:50:16 +01:00
|
|
|
* Copyright (C) 2003-2023 Douglas Gilbert <dgilbert@interlog.com>
|
2003-04-01 08:24:27 +02:00
|
|
|
*
|
2018-08-19 20:45:53 +02:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
2002-10-14 17:26:35 +02:00
|
|
|
*
|
2003-04-09 14:45:04 +02:00
|
|
|
*
|
2003-04-01 08:24:27 +02:00
|
|
|
* In the SCSI world "SMART" is a dead or withdrawn standard. In recent
|
|
|
|
* SCSI standards (since SCSI-3) it goes under the awkward name of
|
2003-04-06 05:55:41 +02:00
|
|
|
* "Informational Exceptions" ["IE" or "IEC" (with the "C" for "control")].
|
2003-04-01 08:24:27 +02:00
|
|
|
* The relevant information is spread around several SCSI draft
|
|
|
|
* standards available at http://www.t10.org . Reference is made in the
|
|
|
|
* code to the following acronyms:
|
|
|
|
* - SAM [SCSI Architectural model, versions 2 or 3]
|
|
|
|
* - SPC [SCSI Primary commands, versions 2 or 3]
|
|
|
|
* - SBC [SCSI Block commands, versions 2]
|
2003-04-09 14:45:04 +02:00
|
|
|
*
|
|
|
|
* Some SCSI disk vendors have snippets of "SMART" information in their
|
|
|
|
* product manuals.
|
2002-10-12 13:10:01 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2011-02-14 23:31:01 +01:00
|
|
|
#include <errno.h>
|
2013-06-17 10:45:10 +02:00
|
|
|
#include <ctype.h>
|
2004-03-13 16:03:57 +01:00
|
|
|
|
|
|
|
#include "config.h"
|
2018-08-04 21:09:46 +02:00
|
|
|
|
2002-10-12 13:10:01 +02:00
|
|
|
#include "scsicmds.h"
|
2008-07-25 23:16:01 +02:00
|
|
|
#include "dev_interface.h"
|
2003-03-26 09:15:51 +01:00
|
|
|
#include "utility.h"
|
2018-04-16 17:12:21 +02:00
|
|
|
#include "sg_unaligned.h"
|
2002-10-12 13:10:01 +02:00
|
|
|
|
2010-04-30 16:32:49 +02:00
|
|
|
const char *scsicmds_c_cvsid="$Id$"
|
2010-10-28 21:20:33 +02:00
|
|
|
SCSICMDS_H_CVSID;
|
2002-10-12 13:10:01 +02:00
|
|
|
|
2018-01-07 23:46:30 +01:00
|
|
|
static const char * logSenStr = "Log Sense";
|
|
|
|
|
2010-10-28 21:20:33 +02:00
|
|
|
// Print SCSI debug messages?
|
|
|
|
unsigned char scsi_debugmode = 0;
|
2003-01-04 02:37:48 +01:00
|
|
|
|
2023-02-10 07:18:05 +01:00
|
|
|
supported_vpd_pages * supported_vpd_pages_p = nullptr;
|
2013-02-22 00:55:35 +01:00
|
|
|
|
2023-01-12 05:51:49 +01:00
|
|
|
#define RSOC_RESP_SZ 4096
|
2023-02-10 07:18:05 +01:00
|
|
|
#define RSOC_ALL_CMDS_CTDP_0 8
|
|
|
|
#define RSOC_ALL_CMDS_CTDP_1 20
|
|
|
|
#define RSOC_1_CMD_CTDP_0 36
|
|
|
|
|
2023-02-12 06:13:17 +01:00
|
|
|
// Check if LOG SENSE cdb supports changing the Subpage Code field
|
2023-02-10 07:18:05 +01:00
|
|
|
static scsi_cmd_support
|
2023-02-10 18:29:04 +01:00
|
|
|
chk_lsense_spc(scsi_device * device)
|
2023-02-10 07:18:05 +01:00
|
|
|
{
|
|
|
|
int r_len = 0;
|
|
|
|
int err;
|
|
|
|
uint8_t rsoc_1cmd_rsp[RSOC_1_CMD_CTDP_0] = {};
|
|
|
|
uint8_t * rp = rsoc_1cmd_rsp;
|
|
|
|
|
|
|
|
err = scsiRSOCcmd(device, false /*rctd */ , 1 /* '1 cmd' format */,
|
|
|
|
LOG_SENSE, 0, rp, RSOC_1_CMD_CTDP_0, r_len);
|
|
|
|
if (err) {
|
|
|
|
if (scsi_debugmode)
|
|
|
|
pout("%s Failed [%s]\n", __func__, scsiErrString(err));
|
|
|
|
return SC_NO_SUPPORT;
|
|
|
|
}
|
|
|
|
if (r_len < 8) {
|
|
|
|
if (scsi_debugmode)
|
|
|
|
pout("%s response to short [%d]\n", __func__, r_len);
|
|
|
|
return SC_NO_SUPPORT;
|
|
|
|
}
|
|
|
|
/* check the "subpage code" field in LOG SENSE cdb usage data */
|
|
|
|
return rp[7] ? SC_SUPPORT : SC_NO_SUPPORT; /* 4 + ls_cdb_byte3 */
|
|
|
|
}
|
|
|
|
|
2023-01-12 05:51:49 +01:00
|
|
|
bool
|
|
|
|
scsi_device::query_cmd_support()
|
|
|
|
{
|
|
|
|
bool res = true;
|
|
|
|
int k, err, cd_len, bump;
|
|
|
|
int r_len = 0;
|
|
|
|
uint8_t * rp = (uint8_t *)calloc(sizeof(uint8_t), RSOC_RESP_SZ);
|
|
|
|
const uint8_t * last_rp;
|
|
|
|
uint8_t * cmdp;
|
|
|
|
static const int max_bytes_of_cmds = RSOC_RESP_SZ - 4;
|
|
|
|
|
2023-02-10 07:18:05 +01:00
|
|
|
if (nullptr == rp)
|
2023-01-12 05:51:49 +01:00
|
|
|
return false;
|
|
|
|
rsoc_queried = true;
|
|
|
|
/* request 'all commands' format: 4 bytes header, 20 bytes per command */
|
2023-02-10 07:18:05 +01:00
|
|
|
err = scsiRSOCcmd(this, false /* rctd */, 0 /* 'all' format */, 0, 0,
|
|
|
|
rp, RSOC_RESP_SZ, r_len);
|
2023-01-12 05:51:49 +01:00
|
|
|
if (err) {
|
|
|
|
rsoc_sup = SC_NO_SUPPORT;
|
|
|
|
if (scsi_debugmode)
|
|
|
|
pout("%s Failed [%s]\n", __func__, scsiErrString(err));
|
|
|
|
res = false;
|
|
|
|
goto fini;
|
|
|
|
}
|
|
|
|
if (r_len < 4) {
|
|
|
|
pout("%s response too short\n", __func__);
|
|
|
|
res = false;
|
|
|
|
goto fini;
|
|
|
|
}
|
|
|
|
rsoc_sup = SC_SUPPORT;
|
|
|
|
cd_len = sg_get_unaligned_be32(rp + 0);
|
|
|
|
if (cd_len > max_bytes_of_cmds) {
|
|
|
|
if (scsi_debugmode)
|
|
|
|
pout("%s: truncate %d byte response to %d bytes\n", __func__,
|
|
|
|
cd_len, max_bytes_of_cmds);
|
|
|
|
cd_len = max_bytes_of_cmds;
|
|
|
|
}
|
|
|
|
last_rp = rp + cd_len;
|
2023-02-12 06:13:17 +01:00
|
|
|
logsense_sup = SC_NO_SUPPORT;
|
2023-02-10 07:18:05 +01:00
|
|
|
logsense_spc_sup = SC_NO_SUPPORT;
|
2023-01-12 05:51:49 +01:00
|
|
|
rdefect10_sup = SC_NO_SUPPORT;
|
|
|
|
rdefect12_sup = SC_NO_SUPPORT;
|
|
|
|
rcap16_sup = SC_NO_SUPPORT;
|
|
|
|
|
|
|
|
for (k = 0, cmdp = rp + 4; cmdp < last_rp; ++k, cmdp += bump) {
|
|
|
|
bool sa_valid = !! (0x1 & cmdp[5]);
|
|
|
|
bool ctdp = !! (0x2 & cmdp[5]);
|
|
|
|
uint8_t opcode = cmdp[0];
|
|
|
|
uint16_t sa;
|
|
|
|
|
2023-02-10 07:18:05 +01:00
|
|
|
bump = ctdp ? RSOC_ALL_CMDS_CTDP_1 : RSOC_ALL_CMDS_CTDP_0;
|
2023-01-12 05:51:49 +01:00
|
|
|
sa = sa_valid ? sg_get_unaligned_be16(cmdp + 2) : 0;
|
|
|
|
|
|
|
|
switch (opcode) {
|
2023-02-10 07:18:05 +01:00
|
|
|
case LOG_SENSE:
|
2023-02-12 06:13:17 +01:00
|
|
|
logsense_sup = SC_SUPPORT;
|
2023-02-10 18:29:04 +01:00
|
|
|
logsense_spc_sup = chk_lsense_spc(this);
|
2023-02-10 07:18:05 +01:00
|
|
|
break;
|
2023-01-12 05:51:49 +01:00
|
|
|
case READ_DEFECT_10:
|
|
|
|
rdefect10_sup = SC_SUPPORT;
|
|
|
|
break;
|
|
|
|
case READ_DEFECT_12:
|
|
|
|
rdefect12_sup = SC_SUPPORT;
|
|
|
|
break;
|
|
|
|
case SERVICE_ACTION_IN_16:
|
|
|
|
if (sa_valid && (SAI_READ_CAPACITY_16 == sa))
|
|
|
|
rcap16_sup = SC_SUPPORT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-02-10 07:18:05 +01:00
|
|
|
if (scsi_debugmode > 3) {
|
2023-01-12 05:51:49 +01:00
|
|
|
pout("%s: decoded %d supported commands\n", __func__, k);
|
2023-02-12 06:13:17 +01:00
|
|
|
pout(" LOG SENSE %ssupported\n",
|
|
|
|
(SC_SUPPORT == logsense_sup) ? "" : "not ");
|
2023-02-10 07:18:05 +01:00
|
|
|
pout(" LOG SENSE subpage code %ssupported\n",
|
|
|
|
(SC_SUPPORT == logsense_spc_sup) ? "" : "not ");
|
|
|
|
pout(" READ DEFECT 10 %ssupported\n",
|
|
|
|
(SC_SUPPORT == rdefect10_sup) ? "" : "not ");
|
|
|
|
pout(" READ DEFECT 12 %ssupported\n",
|
|
|
|
(SC_SUPPORT == rdefect12_sup) ? "" : "not ");
|
|
|
|
pout(" READ CAPACITY 16 %ssupported\n",
|
|
|
|
(SC_SUPPORT == rcap16_sup) ? "" : "not ");
|
|
|
|
}
|
2023-01-12 05:51:49 +01:00
|
|
|
|
|
|
|
fini:
|
|
|
|
free(rp);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* May track more in the future */
|
|
|
|
enum scsi_cmd_support
|
|
|
|
scsi_device::cmd_support_level(uint8_t opcode, bool sa_valid,
|
2023-02-12 06:13:17 +01:00
|
|
|
uint16_t sa, bool for_lsense_spc) const
|
2023-01-12 05:51:49 +01:00
|
|
|
{
|
|
|
|
enum scsi_cmd_support scs = SC_SUPPORT_UNKNOWN;
|
|
|
|
|
|
|
|
switch (opcode) {
|
2023-02-10 07:18:05 +01:00
|
|
|
case LOG_SENSE: /* checking if LOG SENSE _subpages_ supported */
|
2023-02-12 06:13:17 +01:00
|
|
|
scs = for_lsense_spc ? logsense_spc_sup : logsense_sup;
|
2023-02-10 07:18:05 +01:00
|
|
|
break;
|
2023-01-12 05:51:49 +01:00
|
|
|
case READ_DEFECT_10:
|
|
|
|
scs = rdefect10_sup;
|
|
|
|
break;
|
|
|
|
case READ_DEFECT_12:
|
|
|
|
scs = rdefect12_sup;
|
|
|
|
break;
|
|
|
|
case SERVICE_ACTION_IN_16:
|
|
|
|
if (sa_valid && (SAI_READ_CAPACITY_16 == sa))
|
|
|
|
scs = rcap16_sup;
|
|
|
|
break;
|
|
|
|
case MAINTENANCE_IN_12:
|
|
|
|
if (sa_valid && (MI_REP_SUP_OPCODES == sa))
|
|
|
|
scs = rsoc_sup;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return scs;
|
|
|
|
}
|
2013-02-22 00:55:35 +01:00
|
|
|
|
|
|
|
supported_vpd_pages::supported_vpd_pages(scsi_device * device) : num_valid(0)
|
|
|
|
{
|
2022-02-27 08:53:55 +01:00
|
|
|
unsigned char b[0xfc] = {}; /* pre SPC-3 INQUIRY max response size */
|
|
|
|
|
2013-02-22 00:55:35 +01:00
|
|
|
if (device && (0 == scsiInquiryVpd(device, SCSI_VPD_SUPPORTED_VPD_PAGES,
|
|
|
|
b, sizeof(b)))) {
|
2018-04-16 17:12:21 +02:00
|
|
|
num_valid = sg_get_unaligned_be16(b + 2);
|
2015-10-20 18:03:57 +02:00
|
|
|
int n = sizeof(pages);
|
2013-02-22 00:55:35 +01:00
|
|
|
if (num_valid > n)
|
|
|
|
num_valid = n;
|
|
|
|
memcpy(pages, b + 4, num_valid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
supported_vpd_pages::is_supported(int vpd_page_num) const
|
|
|
|
{
|
|
|
|
/* Supported VPD pages numbers start at offset 4 and should be in
|
|
|
|
* ascending order but don't assume that. */
|
|
|
|
for (int k = 0; k < num_valid; ++k) {
|
|
|
|
if (vpd_page_num == pages[k])
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-01-03 02:09:59 +01:00
|
|
|
/* Simple ASCII printable (does not use locale), includes space and excludes
|
|
|
|
* DEL (0x7f). Note all UTF-8 encoding apart from <= 0x7f have top bit set. */
|
|
|
|
static inline int
|
|
|
|
my_isprint(uint8_t ch)
|
|
|
|
{
|
|
|
|
return ((ch >= ' ') && (ch < 0x7f));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
trimTrailingSpaces(char * b)
|
|
|
|
{
|
|
|
|
int n = strlen(b);
|
|
|
|
|
|
|
|
while ((n > 0) && (' ' == b[n - 1]))
|
|
|
|
b[--n] = '\0';
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read binary starting at 'up' for 'len' bytes and output as ASCII
|
2023-01-09 18:51:50 +01:00
|
|
|
* hexadecimal to the passed function 'out'. See dStrHex() below for more. */
|
|
|
|
static void
|
|
|
|
dStrHexHelper(const uint8_t * up, int len, int no_ascii,
|
|
|
|
void (*out)(const char * s, void * ctx), void * ctx = nullptr)
|
2002-10-12 13:10:01 +02:00
|
|
|
{
|
2023-01-09 18:51:50 +01:00
|
|
|
static const int line_len = 80;
|
|
|
|
static const int cpstart = 60; // offset of start of ASCII rendering
|
2023-01-03 02:09:59 +01:00
|
|
|
uint8_t c;
|
2023-01-09 18:51:50 +01:00
|
|
|
char buff[line_len + 2]; // room for trailing null
|
2003-03-24 11:48:31 +01:00
|
|
|
int a = 0;
|
2023-01-03 02:09:59 +01:00
|
|
|
int bpstart = 5;
|
2003-03-24 11:48:31 +01:00
|
|
|
int cpos = cpstart;
|
|
|
|
int bpos = bpstart;
|
2023-01-03 02:09:59 +01:00
|
|
|
int i, k, blen;
|
2023-03-21 20:47:46 +01:00
|
|
|
char e[line_len + 4];
|
2023-01-09 18:51:50 +01:00
|
|
|
static const int elen = sizeof(e);
|
2023-01-03 02:09:59 +01:00
|
|
|
|
|
|
|
if (len <= 0)
|
|
|
|
return;
|
|
|
|
blen = (int)sizeof(buff);
|
2023-01-09 18:51:50 +01:00
|
|
|
memset(buff, ' ', line_len);
|
|
|
|
buff[line_len] = '\0';
|
2023-01-03 02:09:59 +01:00
|
|
|
if (no_ascii < 0) {
|
|
|
|
bpstart = 0;
|
|
|
|
bpos = bpstart;
|
|
|
|
for (k = 0; k < len; k++) {
|
|
|
|
c = *up++;
|
|
|
|
if (bpos == (bpstart + (8 * 3)))
|
|
|
|
bpos++;
|
|
|
|
snprintf(&buff[bpos], blen - bpos, "%.2x", (int)(uint8_t)c);
|
|
|
|
buff[bpos + 2] = ' ';
|
|
|
|
if ((k > 0) && (0 == ((k + 1) % 16))) {
|
|
|
|
trimTrailingSpaces(buff);
|
2023-01-09 18:51:50 +01:00
|
|
|
if (no_ascii)
|
2023-01-10 20:27:49 +01:00
|
|
|
snprintf(e, elen, "%s\n", buff);
|
|
|
|
else
|
2023-01-09 18:51:50 +01:00
|
|
|
snprintf(e, elen, "%.76s\n", buff);
|
2023-01-10 20:27:49 +01:00
|
|
|
out(e, ctx);
|
2023-01-03 02:09:59 +01:00
|
|
|
bpos = bpstart;
|
2023-01-09 18:51:50 +01:00
|
|
|
memset(buff, ' ', line_len);
|
2023-01-03 02:09:59 +01:00
|
|
|
} else
|
|
|
|
bpos += 3;
|
|
|
|
}
|
|
|
|
if (bpos > bpstart) {
|
|
|
|
buff[bpos + 2] = '\0';
|
|
|
|
trimTrailingSpaces(buff);
|
2023-01-10 20:27:49 +01:00
|
|
|
snprintf(e, elen, "%s\n", buff);
|
|
|
|
out(e, ctx);
|
2023-01-03 02:09:59 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* no_ascii>=0, start each line with address (offset) */
|
|
|
|
k = snprintf(buff + 1, blen - 1, "%.2x", a);
|
2003-03-24 11:48:31 +01:00
|
|
|
buff[k + 1] = ' ';
|
|
|
|
|
2023-01-03 02:09:59 +01:00
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
c = *up++;
|
2003-03-24 11:48:31 +01:00
|
|
|
bpos += 3;
|
|
|
|
if (bpos == (bpstart + (9 * 3)))
|
|
|
|
bpos++;
|
2023-01-03 02:09:59 +01:00
|
|
|
snprintf(&buff[bpos], blen - bpos, "%.2x", (int)(uint8_t)c);
|
2003-03-24 11:48:31 +01:00
|
|
|
buff[bpos + 2] = ' ';
|
|
|
|
if (no_ascii)
|
|
|
|
buff[cpos++] = ' ';
|
|
|
|
else {
|
2023-01-03 02:09:59 +01:00
|
|
|
if (! my_isprint(c))
|
|
|
|
c = '.';
|
2003-03-24 11:48:31 +01:00
|
|
|
buff[cpos++] = c;
|
|
|
|
}
|
2023-01-03 02:09:59 +01:00
|
|
|
if (cpos > (cpstart + 15)) {
|
|
|
|
if (no_ascii)
|
|
|
|
trimTrailingSpaces(buff);
|
2023-01-09 18:51:50 +01:00
|
|
|
if (no_ascii)
|
2023-01-10 20:27:49 +01:00
|
|
|
snprintf(e, elen, "%s\n", buff);
|
|
|
|
else
|
2023-01-09 18:51:50 +01:00
|
|
|
snprintf(e, elen, "%.76s\n", buff);
|
2023-01-10 20:27:49 +01:00
|
|
|
out(e, ctx);
|
2003-03-24 11:48:31 +01:00
|
|
|
bpos = bpstart;
|
|
|
|
cpos = cpstart;
|
|
|
|
a += 16;
|
2023-01-09 18:51:50 +01:00
|
|
|
memset(buff, ' ', line_len);
|
2023-01-03 02:09:59 +01:00
|
|
|
k = snprintf(buff + 1, blen - 1, "%.2x", a);
|
2003-03-24 11:48:31 +01:00
|
|
|
buff[k + 1] = ' ';
|
|
|
|
}
|
|
|
|
}
|
2023-01-10 20:27:49 +01:00
|
|
|
if (cpos > cpstart) {
|
2023-01-03 02:09:59 +01:00
|
|
|
buff[cpos] = '\0';
|
|
|
|
if (no_ascii)
|
|
|
|
trimTrailingSpaces(buff);
|
2023-01-10 20:27:49 +01:00
|
|
|
snprintf(e, elen, "%s\n", buff);
|
|
|
|
out(e, ctx);
|
2003-03-24 11:48:31 +01:00
|
|
|
}
|
2002-10-12 13:10:01 +02:00
|
|
|
}
|
2003-03-31 02:19:34 +02:00
|
|
|
|
2023-01-09 18:51:50 +01:00
|
|
|
/* Read binary starting at 'up' for 'len' bytes and output as ASCII
|
2023-02-10 07:18:05 +01:00
|
|
|
* hexadecimal into file pointer (fp). If fp is nullptr, then send to
|
2023-01-10 20:27:49 +01:00
|
|
|
* pout(). See dStrHex() below for more. */
|
2023-01-09 18:51:50 +01:00
|
|
|
void
|
|
|
|
dStrHexFp(const uint8_t * up, int len, int no_ascii, FILE * fp)
|
|
|
|
{
|
|
|
|
/* N.B. Use of lamba requires C++11 or later. */
|
2023-02-10 07:18:05 +01:00
|
|
|
if ((nullptr == up) || (len < 1))
|
|
|
|
return;
|
|
|
|
else if (fp)
|
2023-01-10 20:27:49 +01:00
|
|
|
dStrHexHelper(up, len, no_ascii,
|
|
|
|
[](const char * s, void * ctx)
|
|
|
|
{ fputs(s, reinterpret_cast<FILE *>(ctx)); },
|
2023-01-12 05:51:49 +01:00
|
|
|
fp);
|
2023-01-10 20:27:49 +01:00
|
|
|
else
|
|
|
|
dStrHexHelper(up, len, no_ascii,
|
|
|
|
[](const char * s, void *){ pout("%s", s); });
|
2023-01-09 18:51:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read binary starting at 'up' for 'len' bytes and output as ASCII
|
|
|
|
* hexadecimal into pout(). 16 bytes per line are output with an
|
|
|
|
* additional space between 8th and 9th byte on each line (for readability).
|
|
|
|
* 'no_ascii' selects one of 3 output format types:
|
|
|
|
* > 0 each line has address then up to 16 ASCII-hex bytes
|
2023-01-10 20:27:49 +01:00
|
|
|
* = 0 in addition, the bytes are rendered in ASCII to the right
|
|
|
|
* of each line, non-printable characters shown as '.'
|
2023-01-09 18:51:50 +01:00
|
|
|
* < 0 only the ASCII-hex bytes are listed (i.e. without address) */
|
2023-01-03 02:09:59 +01:00
|
|
|
void
|
|
|
|
dStrHex(const uint8_t * up, int len, int no_ascii)
|
|
|
|
{
|
2023-01-09 18:51:50 +01:00
|
|
|
/* N.B. Use of lamba requires C++11 or later. */
|
|
|
|
dStrHexHelper(up, len, no_ascii,
|
|
|
|
[](const char * s, void *){ pout("%s", s); });
|
2023-01-03 02:09:59 +01:00
|
|
|
}
|
|
|
|
|
2017-12-29 18:41:15 +01:00
|
|
|
/* This is a heuristic that takes into account the command bytes and length
|
|
|
|
* to decide whether the presented unstructured sequence of bytes could be
|
|
|
|
* a SCSI command. If so it returns true otherwise false. Vendor specific
|
|
|
|
* SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
|
|
|
|
* to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
|
|
|
|
* only SCSI commands considered above 16 bytes of length are the Variable
|
|
|
|
* Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e).
|
|
|
|
* Both have an inbuilt length field which can be cross checked with clen.
|
|
|
|
* No NVMe commands (64 bytes long plus some extra added by some OSes) have
|
|
|
|
* opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
|
|
|
|
* structures that are sent across the wire. The FIS register structure is
|
|
|
|
* used to move a command from a SATA host to device, but the ATA 'command'
|
|
|
|
* is not the first byte. So it is harder to say what will happen if a
|
2018-12-02 17:07:26 +01:00
|
|
|
* FIS structure is presented as a SCSI command, hopefully there is a low
|
2017-12-29 18:41:15 +01:00
|
|
|
* probability this function will yield true in that case. */
|
|
|
|
bool
|
|
|
|
is_scsi_cdb(const uint8_t * cdbp, int clen)
|
|
|
|
{
|
|
|
|
if (clen < 6)
|
|
|
|
return false;
|
2020-03-05 17:17:31 +01:00
|
|
|
uint8_t opcode = cdbp[0];
|
|
|
|
uint8_t top3bits = opcode >> 5;
|
2017-12-31 05:29:16 +01:00
|
|
|
if (0x3 == top3bits) { /* Opcodes 0x60 to 0x7f */
|
2020-03-05 17:17:31 +01:00
|
|
|
int ilen, sa;
|
2017-12-29 18:41:15 +01:00
|
|
|
if ((clen < 12) || (clen % 4))
|
|
|
|
return false; /* must be modulo 4 and 12 or more bytes */
|
|
|
|
switch (opcode) {
|
|
|
|
case 0x7e: /* Extended cdb (XCDB) */
|
2018-04-16 17:12:21 +02:00
|
|
|
ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
|
2017-12-29 18:41:15 +01:00
|
|
|
return (ilen == clen);
|
|
|
|
case 0x7f: /* Variable Length cdb */
|
|
|
|
ilen = 8 + cdbp[7];
|
2018-04-16 17:12:21 +02:00
|
|
|
sa = sg_get_unaligned_be16(cdbp + 8);
|
2017-12-29 18:41:15 +01:00
|
|
|
/* service action (sa) 0x0 is reserved */
|
|
|
|
return ((ilen == clen) && sa);
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (clen <= 16) {
|
|
|
|
switch (clen) {
|
|
|
|
case 6:
|
|
|
|
if (top3bits > 0x5) /* vendor */
|
|
|
|
return true;
|
|
|
|
return (0x0 == top3bits); /* 6 byte cdb */
|
|
|
|
case 10:
|
|
|
|
if (top3bits > 0x5) /* vendor */
|
|
|
|
return true;
|
|
|
|
return ((0x1 == top3bits) || (0x2 == top3bits)); /* 10 byte cdb */
|
|
|
|
case 16:
|
|
|
|
if (top3bits > 0x5) /* vendor */
|
|
|
|
return true;
|
|
|
|
return (0x4 == top3bits); /* 16 byte cdb */
|
|
|
|
case 12:
|
|
|
|
if (top3bits > 0x5) /* vendor */
|
|
|
|
return true;
|
|
|
|
return (0x5 == top3bits); /* 12 byte cdb */
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* NVMe probably falls out here, clen > 16 and (opcode < 0x60 or
|
|
|
|
* opcode > 0x7f). */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-05-29 07:08:10 +02:00
|
|
|
enum scsi_sa_t {
|
|
|
|
scsi_sa_none = 0,
|
|
|
|
scsi_sa_b1b4n5, /* for cdb byte 1, bit 4, number 5 bits */
|
|
|
|
scsi_sa_b8b7n16,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct scsi_sa_var_map {
|
|
|
|
uint8_t cdb0;
|
|
|
|
enum scsi_sa_t sa_var;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct scsi_sa_var_map sa_var_a[] = {
|
|
|
|
{0x3b, scsi_sa_b1b4n5}, /* Write buffer modes_s */
|
|
|
|
{0x3c, scsi_sa_b1b4n5}, /* Read buffer(10) modes_s */
|
|
|
|
{0x48, scsi_sa_b1b4n5}, /* Sanitize sa_s */
|
|
|
|
{0x5e, scsi_sa_b1b4n5}, /* Persistent reserve in sa_s */
|
|
|
|
{0x5f, scsi_sa_b1b4n5}, /* Persistent reserve out sa_s */
|
|
|
|
{0x7f, scsi_sa_b8b7n16}, /* Variable length commands */
|
|
|
|
{0x83, scsi_sa_b1b4n5}, /* Extended copy out/cmd sa_s */
|
|
|
|
{0x84, scsi_sa_b1b4n5}, /* Extended copy in sa_s */
|
|
|
|
{0x8c, scsi_sa_b1b4n5}, /* Read attribute sa_s */
|
|
|
|
{0x9b, scsi_sa_b1b4n5}, /* Read buffer(16) modes_s */
|
|
|
|
{0x9e, scsi_sa_b1b4n5}, /* Service action in (16) */
|
|
|
|
{0x9f, scsi_sa_b1b4n5}, /* Service action out (16) */
|
|
|
|
{0xa3, scsi_sa_b1b4n5}, /* Maintenance in */
|
|
|
|
{0xa4, scsi_sa_b1b4n5}, /* Maintenance out */
|
|
|
|
{0xa9, scsi_sa_b1b4n5}, /* Service action out (12) */
|
|
|
|
{0xab, scsi_sa_b1b4n5}, /* Service action in (12) */
|
|
|
|
};
|
|
|
|
|
2003-03-31 02:19:34 +02:00
|
|
|
struct scsi_opcode_name {
|
2017-12-27 17:22:21 +01:00
|
|
|
uint8_t opcode;
|
2023-01-12 05:51:49 +01:00
|
|
|
bool sa_valid; /* Service action (next field) valid */
|
2022-05-28 04:33:52 +02:00
|
|
|
uint16_t sa;
|
2003-03-31 02:19:34 +02:00
|
|
|
const char * name;
|
|
|
|
};
|
|
|
|
|
2022-05-28 04:33:52 +02:00
|
|
|
/* Array assumed to be sorted by opcode then service action (sa) */
|
2003-03-31 02:19:34 +02:00
|
|
|
static struct scsi_opcode_name opcode_name_arr[] = {
|
|
|
|
/* in ascending opcode order */
|
2022-05-28 04:33:52 +02:00
|
|
|
{TEST_UNIT_READY, false, 0, "test unit ready"}, /* 0x00 */
|
|
|
|
{REQUEST_SENSE, false, 0, "request sense"}, /* 0x03 */
|
|
|
|
{INQUIRY, false, 0, "inquiry"}, /* 0x12 */
|
|
|
|
{MODE_SELECT_6, false, 0, "mode select(6)"}, /* 0x15 */
|
|
|
|
{MODE_SENSE_6, false, 0, "mode sense(6)"}, /* 0x1a */
|
|
|
|
{START_STOP_UNIT, false, 0, "start stop unit"}, /* 0x1b */
|
|
|
|
{RECEIVE_DIAGNOSTIC, false, 0, "receive diagnostic"}, /* 0x1c */
|
|
|
|
{SEND_DIAGNOSTIC, false, 0, "send diagnostic"}, /* 0x1d */
|
|
|
|
{READ_CAPACITY_10, false, 0, "read capacity(10)"}, /* 0x25 */
|
|
|
|
{READ_DEFECT_10, false, 0, "read defect list(10)"}, /* 0x37 */
|
|
|
|
{LOG_SELECT, false, 0, "log select"}, /* 0x4c */
|
|
|
|
{LOG_SENSE, false, 0, "log sense"}, /* 0x4d */
|
|
|
|
{MODE_SELECT_10, false, 0, "mode select(10)"}, /* 0x55 */
|
|
|
|
{MODE_SENSE_10, false, 0, "mode sense(10)"}, /* 0x5a */
|
|
|
|
{SAT_ATA_PASSTHROUGH_16, false, 0, "ata pass-through(16)"}, /* 0x85 */
|
|
|
|
{SERVICE_ACTION_IN_16, true, SAI_READ_CAPACITY_16, "read capacity(16)"},
|
|
|
|
/* 0x9e,0x10 */
|
|
|
|
{SERVICE_ACTION_IN_16, true, SAI_GET_PHY_ELEM_STATUS,
|
|
|
|
"get physical element status"}, /* 0x9e,0x17 */
|
|
|
|
{REPORT_LUNS, false, 0, "report luns"}, /* 0xa0 */
|
|
|
|
{SAT_ATA_PASSTHROUGH_12, false, 0, "ata pass-through(12)"}, /* 0xa1 */
|
|
|
|
{MAINTENANCE_IN_12, true, MI_REP_SUP_OPCODES,
|
2022-07-17 04:22:29 +02:00
|
|
|
"report supported operation codes"}, /* 0xa3,0xc */
|
2022-05-28 04:33:52 +02:00
|
|
|
{READ_DEFECT_12, false, 0, "read defect list(12)"}, /* 0xb7 */
|
2003-03-31 02:19:34 +02:00
|
|
|
};
|
|
|
|
|
2010-11-21 17:45:16 +01:00
|
|
|
static const char * vendor_specific = "<vendor specific>";
|
|
|
|
|
|
|
|
/* Need to expand to take service action into account. For commands
|
|
|
|
* of interest the service action is in the 2nd command byte */
|
2013-02-22 00:55:35 +01:00
|
|
|
const char *
|
2022-05-29 07:08:10 +02:00
|
|
|
scsi_get_opcode_name(const uint8_t * cdbp)
|
2003-03-31 02:19:34 +02:00
|
|
|
{
|
2022-05-29 07:08:10 +02:00
|
|
|
uint8_t opcode = cdbp[0];
|
|
|
|
uint8_t cdb0;
|
|
|
|
enum scsi_sa_t sa_var = scsi_sa_none;
|
|
|
|
bool sa_valid = false;
|
|
|
|
uint16_t sa = 0;
|
|
|
|
int k;
|
|
|
|
static const int sa_var_len = sizeof(sa_var_a) /
|
|
|
|
sizeof(sa_var_a[0]);
|
2022-02-27 08:53:55 +01:00
|
|
|
static const int len = sizeof(opcode_name_arr) /
|
|
|
|
sizeof(opcode_name_arr[0]);
|
2003-03-31 02:19:34 +02:00
|
|
|
|
2010-11-21 17:45:16 +01:00
|
|
|
if (opcode >= 0xc0)
|
2011-03-24 12:55:40 +01:00
|
|
|
return vendor_specific;
|
2022-05-29 07:08:10 +02:00
|
|
|
for (k = 0; k < sa_var_len; ++k) {
|
|
|
|
cdb0 = sa_var_a[k].cdb0;
|
|
|
|
if (opcode == cdb0) {
|
|
|
|
sa_var = sa_var_a[k].sa_var;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (opcode < cdb0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (sa_var) {
|
|
|
|
case scsi_sa_none:
|
|
|
|
break;
|
|
|
|
case scsi_sa_b1b4n5:
|
|
|
|
sa_valid = true;
|
|
|
|
sa = cdbp[1] & 0x1f;
|
|
|
|
break;
|
|
|
|
case scsi_sa_b8b7n16:
|
|
|
|
sa_valid = true;
|
|
|
|
sa = sg_get_unaligned_be16(cdbp + 8);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (k = 0; k < len; ++k) {
|
2015-10-20 18:03:57 +02:00
|
|
|
struct scsi_opcode_name * onp = &opcode_name_arr[k];
|
2022-05-28 04:33:52 +02:00
|
|
|
|
|
|
|
if (opcode == onp->opcode) {
|
|
|
|
if ((! sa_valid) && (! onp->sa_valid))
|
|
|
|
return onp->name;
|
|
|
|
if (sa_valid && onp->sa_valid) {
|
|
|
|
if (sa == onp->sa)
|
|
|
|
return onp->name;
|
|
|
|
}
|
2022-05-29 07:08:10 +02:00
|
|
|
/* should not see sa_valid and ! onp->sa_valid (or vice versa) */
|
2022-05-28 04:33:52 +02:00
|
|
|
} else if (opcode < onp->opcode)
|
2023-02-10 07:18:05 +01:00
|
|
|
return nullptr;
|
2003-03-31 02:19:34 +02:00
|
|
|
}
|
2023-02-10 07:18:05 +01:00
|
|
|
return nullptr;
|
2003-03-31 02:19:34 +02:00
|
|
|
}
|
2002-10-12 13:10:01 +02:00
|
|
|
|
2013-02-22 00:55:35 +01:00
|
|
|
void
|
|
|
|
scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
|
|
|
|
struct scsi_sense_disect * out)
|
2003-03-30 13:23:27 +02:00
|
|
|
{
|
2003-11-20 02:03:26 +01:00
|
|
|
memset(out, 0, sizeof(struct scsi_sense_disect));
|
2006-06-09 02:48:48 +02:00
|
|
|
if (SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) {
|
2015-10-20 18:03:57 +02:00
|
|
|
int resp_code = (io_buf->sensep[0] & 0x7f);
|
2013-02-22 00:55:35 +01:00
|
|
|
out->resp_code = resp_code;
|
2006-06-09 02:48:48 +02:00
|
|
|
if (resp_code >= 0x72) {
|
|
|
|
out->sense_key = (io_buf->sensep[1] & 0xf);
|
|
|
|
out->asc = io_buf->sensep[2];
|
|
|
|
out->ascq = io_buf->sensep[3];
|
|
|
|
} else if (resp_code >= 0x70) {
|
|
|
|
out->sense_key = (io_buf->sensep[2] & 0xf);
|
|
|
|
if (io_buf->resp_sense_len > 13) {
|
|
|
|
out->asc = io_buf->sensep[12];
|
|
|
|
out->ascq = io_buf->sensep[13];
|
|
|
|
}
|
2003-03-30 13:23:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-22 00:55:35 +01:00
|
|
|
int
|
|
|
|
scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
|
2003-04-09 14:45:04 +02:00
|
|
|
{
|
2005-01-14 01:28:32 +01:00
|
|
|
switch (sinfo->sense_key) {
|
2007-03-23 04:47:28 +01:00
|
|
|
case SCSI_SK_NO_SENSE:
|
|
|
|
case SCSI_SK_RECOVERED_ERR:
|
2022-02-27 08:53:55 +01:00
|
|
|
case SCSI_SK_COMPLETED:
|
2007-03-23 04:47:28 +01:00
|
|
|
return SIMPLE_NO_ERROR;
|
2005-01-14 01:28:32 +01:00
|
|
|
case SCSI_SK_NOT_READY:
|
2013-02-17 05:25:42 +01:00
|
|
|
if (SCSI_ASC_NO_MEDIUM == sinfo->asc)
|
2003-11-20 02:03:26 +01:00
|
|
|
return SIMPLE_ERR_NO_MEDIUM;
|
|
|
|
else if (SCSI_ASC_NOT_READY == sinfo->asc) {
|
|
|
|
if (0x1 == sinfo->ascq)
|
|
|
|
return SIMPLE_ERR_BECOMING_READY;
|
|
|
|
else
|
|
|
|
return SIMPLE_ERR_NOT_READY;
|
|
|
|
} else
|
|
|
|
return SIMPLE_ERR_NOT_READY;
|
2005-01-14 01:28:32 +01:00
|
|
|
case SCSI_SK_MEDIUM_ERROR:
|
|
|
|
case SCSI_SK_HARDWARE_ERROR:
|
2005-04-02 14:02:59 +02:00
|
|
|
return SIMPLE_ERR_MEDIUM_HARDWARE;
|
2005-01-14 01:28:32 +01:00
|
|
|
case SCSI_SK_ILLEGAL_REQUEST:
|
2003-04-09 14:45:04 +02:00
|
|
|
if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc)
|
2003-05-12 00:47:46 +02:00
|
|
|
return SIMPLE_ERR_BAD_OPCODE;
|
2013-02-22 00:55:35 +01:00
|
|
|
else if (SCSI_ASC_INVALID_FIELD == sinfo->asc)
|
2003-05-12 00:47:46 +02:00
|
|
|
return SIMPLE_ERR_BAD_FIELD;
|
2003-04-09 14:45:04 +02:00
|
|
|
else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc)
|
2003-05-12 00:47:46 +02:00
|
|
|
return SIMPLE_ERR_BAD_PARAM;
|
2005-01-14 01:28:32 +01:00
|
|
|
else
|
|
|
|
return SIMPLE_ERR_BAD_PARAM; /* all other illegal request */
|
|
|
|
case SCSI_SK_UNIT_ATTENTION:
|
2004-07-11 17:18:38 +02:00
|
|
|
return SIMPLE_ERR_TRY_AGAIN;
|
2007-03-23 04:47:28 +01:00
|
|
|
case SCSI_SK_ABORTED_COMMAND:
|
|
|
|
return SIMPLE_ERR_ABORTED_COMMAND;
|
2022-02-27 08:53:55 +01:00
|
|
|
case SCSI_SK_DATA_PROTECT:
|
|
|
|
return SIMPLE_ERR_PROTECTION;
|
|
|
|
case SCSI_SK_MISCOMPARE:
|
|
|
|
return SIMPLE_ERR_MISCOMPARE;
|
2005-01-14 01:28:32 +01:00
|
|
|
default:
|
2007-03-23 04:47:28 +01:00
|
|
|
return SIMPLE_ERR_UNKNOWN;
|
2005-01-14 01:28:32 +01:00
|
|
|
}
|
2003-04-09 14:45:04 +02:00
|
|
|
}
|
|
|
|
|
2013-02-22 00:55:35 +01:00
|
|
|
const char *
|
|
|
|
scsiErrString(int scsiErr)
|
2003-04-17 05:12:19 +02:00
|
|
|
{
|
|
|
|
if (scsiErr < 0)
|
|
|
|
return strerror(-scsiErr);
|
|
|
|
switch (scsiErr) {
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_NO_ERROR:
|
2003-04-17 05:12:19 +02:00
|
|
|
return "no error";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_NOT_READY:
|
2003-04-17 05:12:19 +02:00
|
|
|
return "device not ready";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_BAD_OPCODE:
|
2003-04-17 05:12:19 +02:00
|
|
|
return "unsupported scsi opcode";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_BAD_FIELD:
|
2003-05-12 00:47:46 +02:00
|
|
|
return "unsupported field in scsi command";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_BAD_PARAM:
|
2003-04-17 05:12:19 +02:00
|
|
|
return "badly formed scsi parameters";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_BAD_RESP:
|
2003-06-17 08:10:54 +02:00
|
|
|
return "scsi response fails sanity test";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_NO_MEDIUM:
|
2003-11-20 02:03:26 +01:00
|
|
|
return "no medium present";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_BECOMING_READY:
|
2003-11-20 02:03:26 +01:00
|
|
|
return "device will be ready soon";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_TRY_AGAIN:
|
2004-07-11 17:18:38 +02:00
|
|
|
return "unit attention reported, try again";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_MEDIUM_HARDWARE:
|
2005-01-14 01:28:32 +01:00
|
|
|
return "medium or hardware error (serious)";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_UNKNOWN:
|
2007-03-23 04:47:28 +01:00
|
|
|
return "unknown error (unexpected sense key)";
|
2013-02-17 05:25:42 +01:00
|
|
|
case SIMPLE_ERR_ABORTED_COMMAND:
|
2007-12-03 03:14:20 +01:00
|
|
|
return "aborted command";
|
2022-02-27 08:53:55 +01:00
|
|
|
case SIMPLE_ERR_PROTECTION:
|
|
|
|
return "data protection error";
|
|
|
|
case SIMPLE_ERR_MISCOMPARE:
|
|
|
|
return "miscompare";
|
2003-04-17 05:12:19 +02:00
|
|
|
default:
|
|
|
|
return "unknown error";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-05 07:07:16 +01:00
|
|
|
static const char * sense_key_desc[] = {
|
|
|
|
"No Sense", /* Filemark, ILI and/or EOM; progress
|
|
|
|
indication (during FORMAT); power
|
|
|
|
condition sensing (REQUEST SENSE) */
|
|
|
|
"Recovered Error", /* The last command completed successfully
|
|
|
|
but used error correction */
|
|
|
|
"Not Ready", /* The addressed target is not ready */
|
|
|
|
"Medium Error", /* Data error detected on the medium */
|
|
|
|
"Hardware Error", /* Controller or device failure */
|
|
|
|
"Illegal Request",
|
|
|
|
"Unit Attention", /* Removable medium was changed, or
|
|
|
|
the target has been reset */
|
|
|
|
"Data Protect", /* Access to the data is blocked */
|
|
|
|
"Blank Check", /* Reached unexpected written or unwritten
|
|
|
|
region of the medium */
|
|
|
|
"Vendor specific(9)", /* Vendor specific */
|
|
|
|
"Copy Aborted", /* COPY or COMPARE was aborted */
|
|
|
|
"Aborted Command", /* The target aborted the command */
|
|
|
|
"Equal", /* SEARCH DATA found data equal (obsolete) */
|
|
|
|
"Volume Overflow", /* Medium full with data to be written */
|
|
|
|
"Miscompare", /* Source data and data on the medium
|
|
|
|
do not agree */
|
|
|
|
"Completed" /* may occur for successful cmd (spc4r23) */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Yield string associated with sense_key value. Returns 'buff'. */
|
|
|
|
char *
|
|
|
|
scsi_get_sense_key_str(int sense_key, int buff_len, char * buff)
|
|
|
|
{
|
|
|
|
if (1 == buff_len) {
|
|
|
|
buff[0] = '\0';
|
|
|
|
return buff;
|
|
|
|
}
|
|
|
|
if ((sense_key >= 0) && (sense_key < 16))
|
|
|
|
snprintf(buff, buff_len, "%s", sense_key_desc[sense_key]);
|
|
|
|
else
|
|
|
|
snprintf(buff, buff_len, "invalid value: 0x%x", sense_key);
|
|
|
|
return buff;
|
|
|
|
}
|
|
|
|
|
2011-03-26 00:04:36 +01:00
|
|
|
/* Iterates to next designation descriptor in the device identification
|
|
|
|
* VPD page. The 'initial_desig_desc' should point to start of first
|
|
|
|
* descriptor with 'page_len' being the number of valid bytes in that
|
|
|
|
* and following descriptors. To start, 'off' should point to a negative
|
|
|
|
* value, thereafter it should point to the value yielded by the previous
|
|
|
|
* call. If 0 returned then 'initial_desig_desc + *off' should be a valid
|
|
|
|
* descriptor; returns -1 if normal end condition and -2 for an abnormal
|
|
|
|
* termination. Matches association, designator_type and/or code_set when
|
|
|
|
* any of those values are greater than or equal to zero. */
|
2013-02-22 00:55:35 +01:00
|
|
|
int
|
|
|
|
scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
|
|
|
|
int * off, int m_assoc, int m_desig_type, int m_code_set)
|
2011-03-26 00:04:36 +01:00
|
|
|
{
|
|
|
|
const unsigned char * ucp;
|
2015-10-20 18:03:57 +02:00
|
|
|
int k;
|
2011-03-26 00:04:36 +01:00
|
|
|
|
|
|
|
for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) {
|
|
|
|
k = (k < 0) ? 0 : (k + ucp[k + 3] + 4);
|
|
|
|
if ((k + 4) > page_len)
|
|
|
|
break;
|
2015-10-20 18:03:57 +02:00
|
|
|
int c_set = (ucp[k] & 0xf);
|
2011-03-26 00:04:36 +01:00
|
|
|
if ((m_code_set >= 0) && (m_code_set != c_set))
|
|
|
|
continue;
|
2015-10-20 18:03:57 +02:00
|
|
|
int assoc = ((ucp[k + 1] >> 4) & 0x3);
|
2011-03-26 00:04:36 +01:00
|
|
|
if ((m_assoc >= 0) && (m_assoc != assoc))
|
|
|
|
continue;
|
2015-10-20 18:03:57 +02:00
|
|
|
int desig_type = (ucp[k + 1] & 0xf);
|
2011-03-26 00:04:36 +01:00
|
|
|
if ((m_desig_type >= 0) && (m_desig_type != desig_type))
|
|
|
|
continue;
|
|
|
|
*off = k;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return (k == page_len) ? -1 : -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode VPD page 0x83 logical unit designator into a string. If both
|
|
|
|
* numeric address and SCSI name string present, prefer the former.
|
|
|
|
* Returns 0 on success, -1 on error with error string in s. */
|
2013-02-22 00:55:35 +01:00
|
|
|
int
|
|
|
|
scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
|
|
|
|
int * transport)
|
2011-03-26 00:04:36 +01:00
|
|
|
{
|
|
|
|
if (transport)
|
2013-02-17 05:25:42 +01:00
|
|
|
*transport = -1;
|
2011-03-26 00:04:36 +01:00
|
|