xref: /aosp_15_r20/external/sg3_utils/lib/sg_lib.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche  * Copyright (c) 1999-2022 Douglas Gilbert.
3*44704f69SBart Van Assche  * All rights reserved.
4*44704f69SBart Van Assche  * Use of this source code is governed by a BSD-style
5*44704f69SBart Van Assche  * license that can be found in the BSD_LICENSE file.
6*44704f69SBart Van Assche  *
7*44704f69SBart Van Assche  * SPDX-License-Identifier: BSD-2-Clause
8*44704f69SBart Van Assche  */
9*44704f69SBart Van Assche 
10*44704f69SBart Van Assche /* NOTICE:
11*44704f69SBart Van Assche  *    On 5th October 2004 (v1.00) this file name was changed from sg_err.c
12*44704f69SBart Van Assche  *    to sg_lib.c and the previous GPL was changed to a FreeBSD license.
13*44704f69SBart Van Assche  *    The intention is to maintain this file and the related sg_lib.h file
14*44704f69SBart Van Assche  *    as open source and encourage their unencumbered use.
15*44704f69SBart Van Assche  *
16*44704f69SBart Van Assche  * CONTRIBUTIONS:
17*44704f69SBart Van Assche  *    This file started out as a copy of SCSI opcodes, sense keys and
18*44704f69SBart Van Assche  *    additional sense codes (ASC/ASCQ) kept in the Linux SCSI subsystem
19*44704f69SBart Van Assche  *    in the kernel source file: drivers/scsi/constant.c . That file
20*44704f69SBart Van Assche  *    bore this notice: "Copyright (C) 1993, 1994, 1995 Eric Youngdale"
21*44704f69SBart Van Assche  *    and a GPL notice.
22*44704f69SBart Van Assche  *
23*44704f69SBart Van Assche  *    Much of the data in this file is derived from SCSI draft standards
24*44704f69SBart Van Assche  *    found at https://www.t10.org with the "SCSI Primary Commands-4" (SPC-4)
25*44704f69SBart Van Assche  *    being the central point of reference.
26*44704f69SBart Van Assche  *
27*44704f69SBart Van Assche  *    Contributions:
28*44704f69SBart Van Assche  *      sense key specific field decoding [Trent Piepho 20031116]
29*44704f69SBart Van Assche  *
30*44704f69SBart Van Assche  */
31*44704f69SBart Van Assche 
32*44704f69SBart Van Assche #define _POSIX_C_SOURCE 200809L         /* for posix_memalign() */
33*44704f69SBart Van Assche #include <stdio.h>
34*44704f69SBart Van Assche #include <stdlib.h>
35*44704f69SBart Van Assche #include <stdarg.h>
36*44704f69SBart Van Assche #include <stdbool.h>
37*44704f69SBart Van Assche #include <stdint.h>
38*44704f69SBart Van Assche #include <string.h>
39*44704f69SBart Van Assche #include <ctype.h>
40*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
41*44704f69SBart Van Assche #include <inttypes.h>
42*44704f69SBart Van Assche #include <errno.h>
43*44704f69SBart Van Assche #include <unistd.h>
44*44704f69SBart Van Assche #include <fcntl.h>
45*44704f69SBart Van Assche #include <sys/types.h>
46*44704f69SBart Van Assche #include <sys/stat.h>
47*44704f69SBart Van Assche 
48*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
49*44704f69SBart Van Assche #include "config.h"
50*44704f69SBart Van Assche #endif
51*44704f69SBart Van Assche 
52*44704f69SBart Van Assche #include "sg_lib.h"
53*44704f69SBart Van Assche #include "sg_lib_data.h"
54*44704f69SBart Van Assche #include "sg_unaligned.h"
55*44704f69SBart Van Assche #include "sg_pr2serr.h"
56*44704f69SBart Van Assche 
57*44704f69SBart Van Assche /* sg_lib_version_str (and datestamp) defined in sg_lib_data.c file */
58*44704f69SBart Van Assche 
59*44704f69SBart Van Assche #define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d  /* corresponding ASC is 0 */
60*44704f69SBart Van Assche 
61*44704f69SBart Van Assche typedef unsigned int my_uint;    /* convenience to save a few line wraps */
62*44704f69SBart Van Assche 
63*44704f69SBart Van Assche FILE * sg_warnings_strm = NULL;        /* would like to default to stderr */
64*44704f69SBart Van Assche 
65*44704f69SBart Van Assche 
66*44704f69SBart Van Assche int
pr2ws(const char * fmt,...)67*44704f69SBart Van Assche pr2ws(const char * fmt, ...)
68*44704f69SBart Van Assche {
69*44704f69SBart Van Assche     va_list args;
70*44704f69SBart Van Assche     int n;
71*44704f69SBart Van Assche 
72*44704f69SBart Van Assche     va_start(args, fmt);
73*44704f69SBart Van Assche     n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
74*44704f69SBart Van Assche     va_end(args);
75*44704f69SBart Van Assche     return n;
76*44704f69SBart Van Assche }
77*44704f69SBart Van Assche 
78*44704f69SBart Van Assche /* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of
79*44704f69SBart Van Assche  * functions. Returns number of chars placed in cp excluding the
80*44704f69SBart Van Assche  * trailing null char. So for cp_max_len > 0 the return value is always
81*44704f69SBart Van Assche  * < cp_max_len; for cp_max_len <= 1 the return value is 0 and no chars are
82*44704f69SBart Van Assche  * written to cp. Note this means that when cp_max_len = 1, this function
83*44704f69SBart Van Assche  * assumes that cp[0] is the null character and does nothing (and returns
84*44704f69SBart Van Assche  * 0). Linux kernel has a similar function called  scnprintf(). Public
85*44704f69SBart Van Assche  * declaration in sg_pr2serr.h header  */
86*44704f69SBart Van Assche int
sg_scnpr(char * cp,int cp_max_len,const char * fmt,...)87*44704f69SBart Van Assche sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...)
88*44704f69SBart Van Assche {
89*44704f69SBart Van Assche     va_list args;
90*44704f69SBart Van Assche     int n;
91*44704f69SBart Van Assche 
92*44704f69SBart Van Assche     if (cp_max_len < 2)
93*44704f69SBart Van Assche         return 0;
94*44704f69SBart Van Assche     va_start(args, fmt);
95*44704f69SBart Van Assche     n = vsnprintf(cp, cp_max_len, fmt, args);
96*44704f69SBart Van Assche     va_end(args);
97*44704f69SBart Van Assche     return (n < cp_max_len) ? n : (cp_max_len - 1);
98*44704f69SBart Van Assche }
99*44704f69SBart Van Assche 
100*44704f69SBart Van Assche /* Simple ASCII printable (does not use locale), includes space and excludes
101*44704f69SBart Van Assche  * DEL (0x7f). */
102*44704f69SBart Van Assche static inline int
my_isprint(int ch)103*44704f69SBart Van Assche my_isprint(int ch)
104*44704f69SBart Van Assche {
105*44704f69SBart Van Assche     return ((ch >= ' ') && (ch < 0x7f));
106*44704f69SBart Van Assche }
107*44704f69SBart Van Assche 
108*44704f69SBart Van Assche /* DSENSE is 'descriptor sense' as opposed to the older 'fixed sense'.
109*44704f69SBart Van Assche  * Only (currently) used in SNTL. */
110*44704f69SBart Van Assche bool
sg_get_initial_dsense(void)111*44704f69SBart Van Assche sg_get_initial_dsense(void)
112*44704f69SBart Van Assche {
113*44704f69SBart Van Assche     int k;
114*44704f69SBart Van Assche     const char * cp;
115*44704f69SBart Van Assche 
116*44704f69SBart Van Assche     cp = getenv("SG3_UTILS_DSENSE");
117*44704f69SBart Van Assche     if (cp) {
118*44704f69SBart Van Assche         if (1 == sscanf(cp, "%d", &k))
119*44704f69SBart Van Assche             return k ? true : false;
120*44704f69SBart Van Assche     }
121*44704f69SBart Van Assche     return false;
122*44704f69SBart Van Assche }
123*44704f69SBart Van Assche 
124*44704f69SBart Van Assche /* Searches 'arr' for match on 'value' then 'peri_type'. If matches
125*44704f69SBart Van Assche    'value' but not 'peri_type' then yields first 'value' match entry.
126*44704f69SBart Van Assche    Last element of 'arr' has NULL 'name'. If no match returns NULL. */
127*44704f69SBart Van Assche static const struct sg_lib_value_name_t *
get_value_name(const struct sg_lib_value_name_t * arr,int value,int peri_type)128*44704f69SBart Van Assche get_value_name(const struct sg_lib_value_name_t * arr, int value,
129*44704f69SBart Van Assche                int peri_type)
130*44704f69SBart Van Assche {
131*44704f69SBart Van Assche     const struct sg_lib_value_name_t * vp = arr;
132*44704f69SBart Van Assche     const struct sg_lib_value_name_t * holdp;
133*44704f69SBart Van Assche 
134*44704f69SBart Van Assche     if (peri_type < 0)
135*44704f69SBart Van Assche         peri_type = 0;
136*44704f69SBart Van Assche     for (; vp->name; ++vp) {
137*44704f69SBart Van Assche         if (value == vp->value) {
138*44704f69SBart Van Assche             if (sg_pdt_s_eq(peri_type, vp->peri_dev_type))
139*44704f69SBart Van Assche                 return vp;
140*44704f69SBart Van Assche             holdp = vp;
141*44704f69SBart Van Assche             while ((vp + 1)->name && (value == (vp + 1)->value)) {
142*44704f69SBart Van Assche                 ++vp;
143*44704f69SBart Van Assche                 if (sg_pdt_s_eq(peri_type, vp->peri_dev_type))
144*44704f69SBart Van Assche                     return vp;
145*44704f69SBart Van Assche             }
146*44704f69SBart Van Assche             return holdp;
147*44704f69SBart Van Assche         }
148*44704f69SBart Van Assche     }
149*44704f69SBart Van Assche     return NULL;
150*44704f69SBart Van Assche }
151*44704f69SBart Van Assche 
152*44704f69SBart Van Assche /* If this function is not called, sg_warnings_strm will be NULL and all users
153*44704f69SBart Van Assche  * (mainly fprintf() ) need to check and substitute stderr as required */
154*44704f69SBart Van Assche void
sg_set_warnings_strm(FILE * warnings_strm)155*44704f69SBart Van Assche sg_set_warnings_strm(FILE * warnings_strm)
156*44704f69SBart Van Assche {
157*44704f69SBart Van Assche     sg_warnings_strm = warnings_strm;
158*44704f69SBart Van Assche }
159*44704f69SBart Van Assche 
160*44704f69SBart Van Assche /* Take care to minimize printf() parsing delays when printing commands */
161*44704f69SBart Van Assche static char bin2hexascii[] = {'0', '1', '2', '3', '4', '5', '6', '7',
162*44704f69SBart Van Assche                               '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
163*44704f69SBart Van Assche 
164*44704f69SBart Van Assche 
165*44704f69SBart Van Assche /* Given a SCSI command pointed to by cdbp of sz bytes this function forms
166*44704f69SBart Van Assche  * a SCSI command in ASCII surrounded by square brackets in 'b'. 'b' is at
167*44704f69SBart Van Assche  * least blen bytes long. If cmd_name is true then the command is prefixed
168*44704f69SBart Van Assche  * by its SCSI command name (e.g.  "VERIFY(10) [2f ...]". The command is
169*44704f69SBart Van Assche  * shown as spaced separated pairs of hexadecimal digits (i.e. 0-9, a-f).
170*44704f69SBart Van Assche  * Each pair represents byte. The leftmost pair of digits is cdbp[0] . If
171*44704f69SBart Van Assche  * sz <= 0 then this function tries to guess the length of the command. */
172*44704f69SBart Van Assche char *
sg_get_command_str(const uint8_t * cdbp,int sz,bool cmd_name,int blen,char * b)173*44704f69SBart Van Assche sg_get_command_str(const uint8_t * cdbp, int sz, bool cmd_name, int blen,
174*44704f69SBart Van Assche                    char * b)
175*44704f69SBart Van Assche {
176*44704f69SBart Van Assche     int k, j, jj;
177*44704f69SBart Van Assche 
178*44704f69SBart Van Assche     if ((cdbp == NULL) || (b == NULL) || (blen < 1))
179*44704f69SBart Van Assche         return b;
180*44704f69SBart Van Assche     if (cmd_name && (blen > 16)) {
181*44704f69SBart Van Assche         sg_get_command_name(cdbp, 0, blen, b);
182*44704f69SBart Van Assche         j = (int)strlen(b);
183*44704f69SBart Van Assche         if (j < (blen - 1))
184*44704f69SBart Van Assche             b[j++] = ' ';
185*44704f69SBart Van Assche     } else
186*44704f69SBart Van Assche         j = 0;
187*44704f69SBart Van Assche     if (j >= blen)
188*44704f69SBart Van Assche         goto fini;
189*44704f69SBart Van Assche     b[j++] = '[';
190*44704f69SBart Van Assche     if (j >= blen)
191*44704f69SBart Van Assche         goto fini;
192*44704f69SBart Van Assche     if (sz <= 0) {
193*44704f69SBart Van Assche         if (SG_VARIABLE_LENGTH_CMD == cdbp[0])
194*44704f69SBart Van Assche             sz = cdbp[7] + 8;
195*44704f69SBart Van Assche         else
196*44704f69SBart Van Assche             sz = sg_get_command_size(cdbp[0]);
197*44704f69SBart Van Assche     }
198*44704f69SBart Van Assche     jj = j;
199*44704f69SBart Van Assche     for (k = 0; (k < sz) && (j < (blen - 3)); ++k, j += 3, ++cdbp) {
200*44704f69SBart Van Assche         b[j] = bin2hexascii[(*cdbp >> 4) & 0xf];
201*44704f69SBart Van Assche         b[j + 1] = bin2hexascii[*cdbp & 0xf];
202*44704f69SBart Van Assche         b[j + 2] = ' ';
203*44704f69SBart Van Assche     }
204*44704f69SBart Van Assche     if (j > jj)
205*44704f69SBart Van Assche         --j;    /* don't want trailing space before ']' */
206*44704f69SBart Van Assche     if (j >= blen)
207*44704f69SBart Van Assche         goto fini;
208*44704f69SBart Van Assche     b[j++] = ']';
209*44704f69SBart Van Assche fini:
210*44704f69SBart Van Assche     if (j >= blen)
211*44704f69SBart Van Assche         b[blen - 1] = '\0';     /* truncated string */
212*44704f69SBart Van Assche     else
213*44704f69SBart Van Assche         b[j] = '\0';
214*44704f69SBart Van Assche     return b;
215*44704f69SBart Van Assche }
216*44704f69SBart Van Assche 
217*44704f69SBart Van Assche #define CMD_NAME_LEN 128
218*44704f69SBart Van Assche 
219*44704f69SBart Van Assche void
sg_print_command_len(const uint8_t * cdbp,int sz)220*44704f69SBart Van Assche sg_print_command_len(const uint8_t * cdbp, int sz)
221*44704f69SBart Van Assche {
222*44704f69SBart Van Assche     char buff[CMD_NAME_LEN];
223*44704f69SBart Van Assche 
224*44704f69SBart Van Assche     sg_get_command_str(cdbp, sz, true, sizeof(buff), buff);
225*44704f69SBart Van Assche     pr2ws("%s\n", buff);
226*44704f69SBart Van Assche }
227*44704f69SBart Van Assche 
228*44704f69SBart Van Assche void
sg_print_command(const uint8_t * cdbp)229*44704f69SBart Van Assche sg_print_command(const uint8_t * cdbp)
230*44704f69SBart Van Assche {
231*44704f69SBart Van Assche     sg_print_command_len(cdbp, 0);
232*44704f69SBart Van Assche }
233*44704f69SBart Van Assche 
234*44704f69SBart Van Assche bool
sg_scsi_status_is_good(int sstatus)235*44704f69SBart Van Assche sg_scsi_status_is_good(int sstatus)
236*44704f69SBart Van Assche {
237*44704f69SBart Van Assche     sstatus &= 0xfe;
238*44704f69SBart Van Assche     switch (sstatus) {
239*44704f69SBart Van Assche     case SAM_STAT_GOOD:
240*44704f69SBart Van Assche     case SAM_STAT_CONDITION_MET:
241*44704f69SBart Van Assche         return true;
242*44704f69SBart Van Assche     default:
243*44704f69SBart Van Assche         return false;
244*44704f69SBart Van Assche     }
245*44704f69SBart Van Assche }
246*44704f69SBart Van Assche 
247*44704f69SBart Van Assche bool
sg_scsi_status_is_bad(int sstatus)248*44704f69SBart Van Assche sg_scsi_status_is_bad(int sstatus)
249*44704f69SBart Van Assche {
250*44704f69SBart Van Assche     sstatus &= 0xfe;
251*44704f69SBart Van Assche     switch (sstatus) {
252*44704f69SBart Van Assche     case SAM_STAT_GOOD:
253*44704f69SBart Van Assche     case SAM_STAT_CONDITION_MET:
254*44704f69SBart Van Assche         return false;
255*44704f69SBart Van Assche     default:
256*44704f69SBart Van Assche         return true;
257*44704f69SBart Van Assche     }
258*44704f69SBart Van Assche }
259*44704f69SBart Van Assche 
260*44704f69SBart Van Assche void
sg_get_scsi_status_str(int scsi_status,int buff_len,char * buff)261*44704f69SBart Van Assche sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff)
262*44704f69SBart Van Assche {
263*44704f69SBart Van Assche     const struct sg_lib_simple_value_name_t * sstatus_p;
264*44704f69SBart Van Assche 
265*44704f69SBart Van Assche     if ((NULL == buff) || (buff_len < 1))
266*44704f69SBart Van Assche         return;
267*44704f69SBart Van Assche     else if (1 == buff_len) {
268*44704f69SBart Van Assche         buff[0] = '\0';
269*44704f69SBart Van Assche         return;
270*44704f69SBart Van Assche     }
271*44704f69SBart Van Assche     scsi_status &= 0x7e; /* sanitize as much as possible */
272*44704f69SBart Van Assche     for (sstatus_p = sg_lib_sstatus_str_arr; sstatus_p->name; ++sstatus_p) {
273*44704f69SBart Van Assche         if (scsi_status == sstatus_p->value)
274*44704f69SBart Van Assche             break;
275*44704f69SBart Van Assche     }
276*44704f69SBart Van Assche     if (sstatus_p->name)
277*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "%s", sstatus_p->name);
278*44704f69SBart Van Assche     else
279*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "Unknown status [0x%x]", scsi_status);
280*44704f69SBart Van Assche }
281*44704f69SBart Van Assche 
282*44704f69SBart Van Assche void
sg_print_scsi_status(int scsi_status)283*44704f69SBart Van Assche sg_print_scsi_status(int scsi_status)
284*44704f69SBart Van Assche {
285*44704f69SBart Van Assche     char buff[128];
286*44704f69SBart Van Assche 
287*44704f69SBart Van Assche     sg_get_scsi_status_str(scsi_status, sizeof(buff) - 1, buff);
288*44704f69SBart Van Assche     buff[sizeof(buff) - 1] = '\0';
289*44704f69SBart Van Assche     pr2ws("%s ", buff);
290*44704f69SBart Van Assche }
291*44704f69SBart Van Assche 
292*44704f69SBart Van Assche /* Get sense key from sense buffer. If successful returns a sense key value
293*44704f69SBart Van Assche  * between 0 and 15. If sense buffer cannot be decode, returns -1 . */
294*44704f69SBart Van Assche int
sg_get_sense_key(const uint8_t * sbp,int sb_len)295*44704f69SBart Van Assche sg_get_sense_key(const uint8_t * sbp, int sb_len)
296*44704f69SBart Van Assche {
297*44704f69SBart Van Assche     if ((NULL == sbp) || (sb_len < 2))
298*44704f69SBart Van Assche         return -1;
299*44704f69SBart Van Assche     switch (sbp[0] & 0x7f) {
300*44704f69SBart Van Assche     case 0x70:
301*44704f69SBart Van Assche     case 0x71:
302*44704f69SBart Van Assche         return (sb_len < 3) ? -1 : (sbp[2] & 0xf);
303*44704f69SBart Van Assche     case 0x72:
304*44704f69SBart Van Assche     case 0x73:
305*44704f69SBart Van Assche         return sbp[1] & 0xf;
306*44704f69SBart Van Assche     default:
307*44704f69SBart Van Assche         return -1;
308*44704f69SBart Van Assche     }
309*44704f69SBart Van Assche }
310*44704f69SBart Van Assche 
311*44704f69SBart Van Assche /* Yield string associated with sense_key value. Returns 'buff'. */
312*44704f69SBart Van Assche char *
sg_get_sense_key_str(int sense_key,int buff_len,char * buff)313*44704f69SBart Van Assche sg_get_sense_key_str(int sense_key, int buff_len, char * buff)
314*44704f69SBart Van Assche {
315*44704f69SBart Van Assche     if (1 == buff_len) {
316*44704f69SBart Van Assche         buff[0] = '\0';
317*44704f69SBart Van Assche         return buff;
318*44704f69SBart Van Assche     }
319*44704f69SBart Van Assche     if ((sense_key >= 0) && (sense_key < 16))
320*44704f69SBart Van Assche          sg_scnpr(buff, buff_len, "%s", sg_lib_sense_key_desc[sense_key]);
321*44704f69SBart Van Assche     else
322*44704f69SBart Van Assche          sg_scnpr(buff, buff_len, "invalid value: 0x%x", sense_key);
323*44704f69SBart Van Assche     return buff;
324*44704f69SBart Van Assche }
325*44704f69SBart Van Assche 
326*44704f69SBart Van Assche /* Yield string associated with ASC/ASCQ values. Returns 'buff'. */
327*44704f69SBart Van Assche char *
sg_get_additional_sense_str(int asc,int ascq,bool add_sense_leadin,int buff_len,char * buff)328*44704f69SBart Van Assche sg_get_additional_sense_str(int asc, int ascq, bool add_sense_leadin,
329*44704f69SBart Van Assche                             int buff_len, char * buff)
330*44704f69SBart Van Assche {
331*44704f69SBart Van Assche     int k, num, rlen;
332*44704f69SBart Van Assche     bool found = false;
333*44704f69SBart Van Assche 
334*44704f69SBart Van Assche     if (1 == buff_len) {
335*44704f69SBart Van Assche         buff[0] = '\0';
336*44704f69SBart Van Assche         return buff;
337*44704f69SBart Van Assche     }
338*44704f69SBart Van Assche     for (k = 0; sg_lib_asc_ascq_range[k].text; ++k) {
339*44704f69SBart Van Assche         struct sg_lib_asc_ascq_range_t * ei2p = &sg_lib_asc_ascq_range[k];
340*44704f69SBart Van Assche 
341*44704f69SBart Van Assche         if ((ei2p->asc == asc) &&
342*44704f69SBart Van Assche             (ascq >= ei2p->ascq_min)  &&
343*44704f69SBart Van Assche             (ascq <= ei2p->ascq_max)) {
344*44704f69SBart Van Assche             found = true;
345*44704f69SBart Van Assche             if (add_sense_leadin)
346*44704f69SBart Van Assche                 num = sg_scnpr(buff, buff_len, "Additional sense: ");
347*44704f69SBart Van Assche             else
348*44704f69SBart Van Assche                 num = 0;
349*44704f69SBart Van Assche             rlen = buff_len - num;
350*44704f69SBart Van Assche             sg_scnpr(buff + num, ((rlen > 0) ? rlen : 0), ei2p->text, ascq);
351*44704f69SBart Van Assche         }
352*44704f69SBart Van Assche     }
353*44704f69SBart Van Assche     if (found)
354*44704f69SBart Van Assche         return buff;
355*44704f69SBart Van Assche 
356*44704f69SBart Van Assche     for (k = 0; sg_lib_asc_ascq[k].text; ++k) {
357*44704f69SBart Van Assche         struct sg_lib_asc_ascq_t * eip = &sg_lib_asc_ascq[k];
358*44704f69SBart Van Assche 
359*44704f69SBart Van Assche         if (eip->asc == asc &&
360*44704f69SBart Van Assche             eip->ascq == ascq) {
361*44704f69SBart Van Assche             found = true;
362*44704f69SBart Van Assche             if (add_sense_leadin)
363*44704f69SBart Van Assche                 sg_scnpr(buff, buff_len, "Additional sense: %s", eip->text);
364*44704f69SBart Van Assche             else
365*44704f69SBart Van Assche                 sg_scnpr(buff, buff_len, "%s", eip->text);
366*44704f69SBart Van Assche         }
367*44704f69SBart Van Assche     }
368*44704f69SBart Van Assche     if (! found) {
369*44704f69SBart Van Assche         if (asc >= 0x80)
370*44704f69SBart Van Assche             sg_scnpr(buff, buff_len, "vendor specific ASC=%02x, ASCQ=%02x "
371*44704f69SBart Van Assche                      "(hex)", asc, ascq);
372*44704f69SBart Van Assche         else if (ascq >= 0x80)
373*44704f69SBart Van Assche             sg_scnpr(buff, buff_len, "ASC=%02x, vendor specific qualification "
374*44704f69SBart Van Assche                      "ASCQ=%02x (hex)", asc, ascq);
375*44704f69SBart Van Assche         else
376*44704f69SBart Van Assche             sg_scnpr(buff, buff_len, "ASC=%02x, ASCQ=%02x (hex)", asc, ascq);
377*44704f69SBart Van Assche     }
378*44704f69SBart Van Assche     return buff;
379*44704f69SBart Van Assche }
380*44704f69SBart Van Assche 
381*44704f69SBart Van Assche /* Yield string associated with ASC/ASCQ values. Returns 'buff'. */
382*44704f69SBart Van Assche char *
sg_get_asc_ascq_str(int asc,int ascq,int buff_len,char * buff)383*44704f69SBart Van Assche sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff)
384*44704f69SBart Van Assche {
385*44704f69SBart Van Assche     return sg_get_additional_sense_str(asc, ascq, true, buff_len, buff);
386*44704f69SBart Van Assche }
387*44704f69SBart Van Assche 
388*44704f69SBart Van Assche /* Attempt to find the first SCSI sense data descriptor that matches the
389*44704f69SBart Van Assche  * given 'desc_type'. If found return pointer to start of sense data
390*44704f69SBart Van Assche  * descriptor; otherwise (including fixed format sense data) returns NULL. */
391*44704f69SBart Van Assche const uint8_t *
sg_scsi_sense_desc_find(const uint8_t * sbp,int sb_len,int desc_type)392*44704f69SBart Van Assche sg_scsi_sense_desc_find(const uint8_t * sbp, int sb_len,
393*44704f69SBart Van Assche                         int desc_type)
394*44704f69SBart Van Assche {
395*44704f69SBart Van Assche     int add_sb_len, desc_len, k;
396*44704f69SBart Van Assche     const uint8_t * descp;
397*44704f69SBart Van Assche 
398*44704f69SBart Van Assche     if ((sb_len < 8) || (0 == (add_sb_len = sbp[7])))
399*44704f69SBart Van Assche         return NULL;
400*44704f69SBart Van Assche     if ((sbp[0] < 0x72) || (sbp[0] > 0x73))
401*44704f69SBart Van Assche         return NULL;
402*44704f69SBart Van Assche     add_sb_len = (add_sb_len < (sb_len - 8)) ?  add_sb_len : (sb_len - 8);
403*44704f69SBart Van Assche     descp = &sbp[8];
404*44704f69SBart Van Assche     for (desc_len = 0, k = 0; k < add_sb_len; k += desc_len) {
405*44704f69SBart Van Assche         int add_d_len;
406*44704f69SBart Van Assche 
407*44704f69SBart Van Assche         descp += desc_len;
408*44704f69SBart Van Assche         add_d_len = (k < (add_sb_len - 1)) ? descp[1]: -1;
409*44704f69SBart Van Assche         desc_len = add_d_len + 2;
410*44704f69SBart Van Assche         if (descp[0] == desc_type)
411*44704f69SBart Van Assche             return descp;
412*44704f69SBart Van Assche         if (add_d_len < 0) /* short descriptor ?? */
413*44704f69SBart Van Assche             break;
414*44704f69SBart Van Assche     }
415*44704f69SBart Van Assche     return NULL;
416*44704f69SBart Van Assche }
417*44704f69SBart Van Assche 
418*44704f69SBart Van Assche /* Returns true if valid bit set, false if valid bit clear. Irrespective the
419*44704f69SBart Van Assche  * information field is written out via 'info_outp' (except when it is
420*44704f69SBart Van Assche  * NULL). Handles both fixed and descriptor sense formats. */
421*44704f69SBart Van Assche bool
sg_get_sense_info_fld(const uint8_t * sbp,int sb_len,uint64_t * info_outp)422*44704f69SBart Van Assche sg_get_sense_info_fld(const uint8_t * sbp, int sb_len,
423*44704f69SBart Van Assche                       uint64_t * info_outp)
424*44704f69SBart Van Assche {
425*44704f69SBart Van Assche     const uint8_t * bp;
426*44704f69SBart Van Assche 
427*44704f69SBart Van Assche     if (info_outp)
428*44704f69SBart Van Assche         *info_outp = 0;
429*44704f69SBart Van Assche     if (sb_len < 7)
430*44704f69SBart Van Assche         return false;
431*44704f69SBart Van Assche     switch (sbp[0] & 0x7f) {
432*44704f69SBart Van Assche     case 0x70:
433*44704f69SBart Van Assche     case 0x71:
434*44704f69SBart Van Assche         if (info_outp)
435*44704f69SBart Van Assche             *info_outp = sg_get_unaligned_be32(sbp + 3);
436*44704f69SBart Van Assche         return !!(sbp[0] & 0x80);
437*44704f69SBart Van Assche     case 0x72:
438*44704f69SBart Van Assche     case 0x73:
439*44704f69SBart Van Assche         bp = sg_scsi_sense_desc_find(sbp, sb_len, 0 /* info desc */);
440*44704f69SBart Van Assche         if (bp && (0xa == bp[1])) {
441*44704f69SBart Van Assche             uint64_t ull = sg_get_unaligned_be64(bp + 4);
442*44704f69SBart Van Assche 
443*44704f69SBart Van Assche             if (info_outp)
444*44704f69SBart Van Assche                 *info_outp = ull;
445*44704f69SBart Van Assche             return !!(bp[2] & 0x80);   /* since spc3r23 should be set */
446*44704f69SBart Van Assche         } else
447*44704f69SBart Van Assche             return false;
448*44704f69SBart Van Assche     default:
449*44704f69SBart Van Assche         return false;
450*44704f69SBart Van Assche     }
451*44704f69SBart Van Assche }
452*44704f69SBart Van Assche 
453*44704f69SBart Van Assche /* Returns true if fixed format or command specific information descriptor
454*44704f69SBart Van Assche  * is found in the descriptor sense; else false. If available the command
455*44704f69SBart Van Assche  * specific information field (4 byte integer in fixed format, 8 byte
456*44704f69SBart Van Assche  * integer in descriptor format) is written out via 'cmd_spec_outp'.
457*44704f69SBart Van Assche  * Handles both fixed and descriptor sense formats. */
458*44704f69SBart Van Assche bool
sg_get_sense_cmd_spec_fld(const uint8_t * sbp,int sb_len,uint64_t * cmd_spec_outp)459*44704f69SBart Van Assche sg_get_sense_cmd_spec_fld(const uint8_t * sbp, int sb_len,
460*44704f69SBart Van Assche                           uint64_t * cmd_spec_outp)
461*44704f69SBart Van Assche {
462*44704f69SBart Van Assche     const uint8_t * bp;
463*44704f69SBart Van Assche 
464*44704f69SBart Van Assche     if (cmd_spec_outp)
465*44704f69SBart Van Assche         *cmd_spec_outp = 0;
466*44704f69SBart Van Assche     if (sb_len < 7)
467*44704f69SBart Van Assche         return false;
468*44704f69SBart Van Assche     switch (sbp[0] & 0x7f) {
469*44704f69SBart Van Assche     case 0x70:
470*44704f69SBart Van Assche     case 0x71:
471*44704f69SBart Van Assche         if (cmd_spec_outp)
472*44704f69SBart Van Assche             *cmd_spec_outp = sg_get_unaligned_be32(sbp + 8);
473*44704f69SBart Van Assche         return true;
474*44704f69SBart Van Assche     case 0x72:
475*44704f69SBart Van Assche     case 0x73:
476*44704f69SBart Van Assche         bp = sg_scsi_sense_desc_find(sbp, sb_len,
477*44704f69SBart Van Assche                                      1 /* command specific info desc */);
478*44704f69SBart Van Assche         if (bp && (0xa == bp[1])) {
479*44704f69SBart Van Assche             if (cmd_spec_outp)
480*44704f69SBart Van Assche                 *cmd_spec_outp = sg_get_unaligned_be64(bp + 4);
481*44704f69SBart Van Assche             return true;
482*44704f69SBart Van Assche         } else
483*44704f69SBart Van Assche             return false;
484*44704f69SBart Van Assche     default:
485*44704f69SBart Van Assche         return false;
486*44704f69SBart Van Assche     }
487*44704f69SBart Van Assche }
488*44704f69SBart Van Assche 
489*44704f69SBart Van Assche /* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
490*44704f69SBart Van Assche  * In descriptor format if the stream commands descriptor not found
491*44704f69SBart Van Assche  * then returns false. Writes true or false corresponding to these bits to
492*44704f69SBart Van Assche  * the last three arguments if they are non-NULL. */
493*44704f69SBart Van Assche bool
sg_get_sense_filemark_eom_ili(const uint8_t * sbp,int sb_len,bool * filemark_p,bool * eom_p,bool * ili_p)494*44704f69SBart Van Assche sg_get_sense_filemark_eom_ili(const uint8_t * sbp, int sb_len,
495*44704f69SBart Van Assche                               bool * filemark_p, bool * eom_p, bool * ili_p)
496*44704f69SBart Van Assche {
497*44704f69SBart Van Assche     const uint8_t * bp;
498*44704f69SBart Van Assche 
499*44704f69SBart Van Assche     if (sb_len < 7)
500*44704f69SBart Van Assche         return false;
501*44704f69SBart Van Assche     switch (sbp[0] & 0x7f) {
502*44704f69SBart Van Assche     case 0x70:
503*44704f69SBart Van Assche     case 0x71:
504*44704f69SBart Van Assche         if (sbp[2] & 0xe0) {
505*44704f69SBart Van Assche             if (filemark_p)
506*44704f69SBart Van Assche                 *filemark_p = !!(sbp[2] & 0x80);
507*44704f69SBart Van Assche             if (eom_p)
508*44704f69SBart Van Assche                 *eom_p = !!(sbp[2] & 0x40);
509*44704f69SBart Van Assche             if (ili_p)
510*44704f69SBart Van Assche                 *ili_p = !!(sbp[2] & 0x20);
511*44704f69SBart Van Assche             return true;
512*44704f69SBart Van Assche         } else
513*44704f69SBart Van Assche             return false;
514*44704f69SBart Van Assche     case 0x72:
515*44704f69SBart Van Assche     case 0x73:
516*44704f69SBart Van Assche        /* Look for stream commands sense data descriptor */
517*44704f69SBart Van Assche         bp = sg_scsi_sense_desc_find(sbp, sb_len, 4);
518*44704f69SBart Van Assche         if (bp && (bp[1] >= 2)) {
519*44704f69SBart Van Assche             if (bp[3] & 0xe0) {
520*44704f69SBart Van Assche                 if (filemark_p)
521*44704f69SBart Van Assche                     *filemark_p = !!(bp[3] & 0x80);
522*44704f69SBart Van Assche                 if (eom_p)
523*44704f69SBart Van Assche                     *eom_p = !!(bp[3] & 0x40);
524*44704f69SBart Van Assche                 if (ili_p)
525*44704f69SBart Van Assche                     *ili_p = !!(bp[3] & 0x20);
526*44704f69SBart Van Assche                 return true;
527*44704f69SBart Van Assche             }
528*44704f69SBart Van Assche         }
529*44704f69SBart Van Assche         return false;
530*44704f69SBart Van Assche     default:
531*44704f69SBart Van Assche         return false;
532*44704f69SBart Van Assche     }
533*44704f69SBart Van Assche }
534*44704f69SBart Van Assche 
535*44704f69SBart Van Assche /* Returns true if SKSV is set and sense key is NO_SENSE or NOT_READY. Also
536*44704f69SBart Van Assche  * returns true if progress indication sense data descriptor found. Places
537*44704f69SBart Van Assche  * progress field from sense data where progress_outp points. If progress
538*44704f69SBart Van Assche  * field is not available returns false and *progress_outp is unaltered.
539*44704f69SBart Van Assche  * Handles both fixed and descriptor sense formats.
540*44704f69SBart Van Assche  * Hint: if true is returned *progress_outp may be multiplied by 100 then
541*44704f69SBart Van Assche  * divided by 65536 to get the percentage completion. */
542*44704f69SBart Van Assche bool
sg_get_sense_progress_fld(const uint8_t * sbp,int sb_len,int * progress_outp)543*44704f69SBart Van Assche sg_get_sense_progress_fld(const uint8_t * sbp, int sb_len,
544*44704f69SBart Van Assche                           int * progress_outp)
545*44704f69SBart Van Assche {
546*44704f69SBart Van Assche     const uint8_t * bp;
547*44704f69SBart Van Assche     int sk, sk_pr;
548*44704f69SBart Van Assche 
549*44704f69SBart Van Assche     if (sb_len < 7)
550*44704f69SBart Van Assche         return false;
551*44704f69SBart Van Assche     switch (sbp[0] & 0x7f) {
552*44704f69SBart Van Assche     case 0x70:
553*44704f69SBart Van Assche     case 0x71:
554*44704f69SBart Van Assche         sk = (sbp[2] & 0xf);
555*44704f69SBart Van Assche         if ((sb_len < 18) ||
556*44704f69SBart Van Assche             ((SPC_SK_NO_SENSE != sk) && (SPC_SK_NOT_READY != sk)))
557*44704f69SBart Van Assche             return false;
558*44704f69SBart Van Assche         if (sbp[15] & 0x80) {        /* SKSV bit set */
559*44704f69SBart Van Assche             if (progress_outp)
560*44704f69SBart Van Assche                 *progress_outp = sg_get_unaligned_be16(sbp + 16);
561*44704f69SBart Van Assche             return true;
562*44704f69SBart Van Assche         } else
563*44704f69SBart Van Assche             return false;
564*44704f69SBart Van Assche     case 0x72:
565*44704f69SBart Van Assche     case 0x73:
566*44704f69SBart Van Assche         /* sense key specific progress (0x2) or progress descriptor (0xa) */
567*44704f69SBart Van Assche         sk = (sbp[1] & 0xf);
568*44704f69SBart Van Assche         sk_pr = (SPC_SK_NO_SENSE == sk) || (SPC_SK_NOT_READY == sk);
569*44704f69SBart Van Assche         if (sk_pr && ((bp = sg_scsi_sense_desc_find(sbp, sb_len, 2))) &&
570*44704f69SBart Van Assche             (0x6 == bp[1]) && (0x80 & bp[4])) {
571*44704f69SBart Van Assche             if (progress_outp)
572*44704f69SBart Van Assche                 *progress_outp = sg_get_unaligned_be16(bp + 5);
573*44704f69SBart Van Assche             return true;
574*44704f69SBart Van Assche         } else if (((bp = sg_scsi_sense_desc_find(sbp, sb_len, 0xa))) &&
575*44704f69SBart Van Assche                    ((0x6 == bp[1]))) {
576*44704f69SBart Van Assche             if (progress_outp)
577*44704f69SBart Van Assche                 *progress_outp = sg_get_unaligned_be16(bp + 6);
578*44704f69SBart Van Assche             return true;
579*44704f69SBart Van Assche         } else
580*44704f69SBart Van Assche             return false;
581*44704f69SBart Van Assche     default:
582*44704f69SBart Van Assche         return false;
583*44704f69SBart Van Assche     }
584*44704f69SBart Van Assche }
585*44704f69SBart Van Assche 
586*44704f69SBart Van Assche char *
sg_get_pdt_str(int pdt,int buff_len,char * buff)587*44704f69SBart Van Assche sg_get_pdt_str(int pdt, int buff_len, char * buff)
588*44704f69SBart Van Assche {
589*44704f69SBart Van Assche     if ((pdt < 0) || (pdt > PDT_MAX))
590*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "bad pdt");
591*44704f69SBart Van Assche     else
592*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "%s", sg_lib_pdt_strs[pdt]);
593*44704f69SBart Van Assche     return buff;
594*44704f69SBart Van Assche }
595*44704f69SBart Van Assche 
596*44704f69SBart Van Assche /* Returns true if left argument is "equal" to the right argument. l_pdt_s
597*44704f69SBart Van Assche  * is a compound PDT (SCSI Peripheral Device Type) or a negative number
598*44704f69SBart Van Assche  * which represents a wildcard (i.e. match anything). r_pdt_s has a similar
599*44704f69SBart Van Assche  * form. PDT values are 5 bits long (0 to 31) and a compound pdt_s is
600*44704f69SBart Van Assche  * formed by shifting the second (upper) PDT by eight bits to the left and
601*44704f69SBart Van Assche  * OR-ing it with the first PDT. The pdt_s values must be defined so
602*44704f69SBart Van Assche  * PDT_DISK (0) is _not_ the upper value in a compound pdt_s. */
603*44704f69SBart Van Assche bool
sg_pdt_s_eq(int l_pdt_s,int r_pdt_s)604*44704f69SBart Van Assche sg_pdt_s_eq(int l_pdt_s, int r_pdt_s)
605*44704f69SBart Van Assche {
606*44704f69SBart Van Assche     bool upper_l = !!(l_pdt_s & PDT_UPPER_MASK);
607*44704f69SBart Van Assche     bool upper_r = !!(r_pdt_s & PDT_UPPER_MASK);
608*44704f69SBart Van Assche 
609*44704f69SBart Van Assche     if ((l_pdt_s < 0) || (r_pdt_s < 0))
610*44704f69SBart Van Assche         return true;
611*44704f69SBart Van Assche     if (!upper_l && !upper_r)
612*44704f69SBart Van Assche         return l_pdt_s == r_pdt_s;
613*44704f69SBart Van Assche     else if (upper_l && upper_r)
614*44704f69SBart Van Assche         return (((PDT_UPPER_MASK & l_pdt_s) == (PDT_UPPER_MASK & r_pdt_s)) ||
615*44704f69SBart Van Assche                 ((PDT_LOWER_MASK & l_pdt_s) == (PDT_LOWER_MASK & r_pdt_s)));
616*44704f69SBart Van Assche     else if (upper_l)
617*44704f69SBart Van Assche         return (((PDT_LOWER_MASK & l_pdt_s) == r_pdt_s) ||
618*44704f69SBart Van Assche                 ((PDT_UPPER_MASK & l_pdt_s) >> 8) == r_pdt_s);
619*44704f69SBart Van Assche     return (((PDT_LOWER_MASK & r_pdt_s) == l_pdt_s) ||
620*44704f69SBart Van Assche             ((PDT_UPPER_MASK & r_pdt_s) >> 8) == l_pdt_s);
621*44704f69SBart Van Assche }
622*44704f69SBart Van Assche 
623*44704f69SBart Van Assche int
sg_lib_pdt_decay(int pdt)624*44704f69SBart Van Assche sg_lib_pdt_decay(int pdt)
625*44704f69SBart Van Assche {
626*44704f69SBart Van Assche     if ((pdt < 0) || (pdt > PDT_MAX))
627*44704f69SBart Van Assche         return 0;
628*44704f69SBart Van Assche     return sg_lib_pdt_decay_arr[pdt];
629*44704f69SBart Van Assche }
630*44704f69SBart Van Assche 
631*44704f69SBart Van Assche char *
sg_get_trans_proto_str(int tpi,int buff_len,char * buff)632*44704f69SBart Van Assche sg_get_trans_proto_str(int tpi, int buff_len, char * buff)
633*44704f69SBart Van Assche {
634*44704f69SBart Van Assche     if ((tpi < 0) || (tpi > 15))
635*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "bad tpi");
636*44704f69SBart Van Assche     else
637*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "%s", sg_lib_transport_proto_strs[tpi]);
638*44704f69SBart Van Assche     return buff;
639*44704f69SBart Van Assche }
640*44704f69SBart Van Assche 
641*44704f69SBart Van Assche #define TRANSPORT_ID_MIN_LEN 24
642*44704f69SBart Van Assche 
643*44704f69SBart Van Assche char *
sg_decode_transportid_str(const char * lip,uint8_t * bp,int bplen,bool only_one,int blen,char * b)644*44704f69SBart Van Assche sg_decode_transportid_str(const char * lip, uint8_t * bp, int bplen,
645*44704f69SBart Van Assche                           bool only_one, int blen, char * b)
646*44704f69SBart Van Assche {
647*44704f69SBart Van Assche     int num, k, n;
648*44704f69SBart Van Assche     uint64_t ull;
649*44704f69SBart Van Assche     int bump;
650*44704f69SBart Van Assche 
651*44704f69SBart Van Assche     if ((NULL == b) || (blen < 1))
652*44704f69SBart Van Assche         return b;
653*44704f69SBart Van Assche     else if (1 == blen) {
654*44704f69SBart Van Assche         b[0] = '\0';
655*44704f69SBart Van Assche         return b;
656*44704f69SBart Van Assche     }
657*44704f69SBart Van Assche     if (NULL == lip)
658*44704f69SBart Van Assche         lip = "";
659*44704f69SBart Van Assche     /* bump = TRANSPORT_ID_MIN_LEN; // some old compilers insisted on this */
660*44704f69SBart Van Assche     for (k = 0, n = 0; bplen > 0; ++k, bp += bump, bplen -= bump) {
661*44704f69SBart Van Assche         int proto_id, normal_len, tpid_format;
662*44704f69SBart Van Assche 
663*44704f69SBart Van Assche         if ((k > 0) && only_one)
664*44704f69SBart Van Assche             break;
665*44704f69SBart Van Assche         if ((bplen < 24) || (0 != (bplen % 4)))
666*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%sTransport Id short or not "
667*44704f69SBart Van Assche                           "multiple of 4 [length=%d]:\n", lip, blen);
668*44704f69SBart Van Assche         else
669*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%sTransport Id of initiator:\n",
670*44704f69SBart Van Assche                           lip);
671*44704f69SBart Van Assche         tpid_format = ((bp[0] >> 6) & 0x3);
672*44704f69SBart Van Assche         proto_id = (bp[0] & 0xf);
673*44704f69SBart Van Assche         normal_len = (bplen > TRANSPORT_ID_MIN_LEN) ?
674*44704f69SBart Van Assche                                 TRANSPORT_ID_MIN_LEN : bplen;
675*44704f69SBart Van Assche         switch (proto_id) {
676*44704f69SBart Van Assche         case TPROTO_FCP: /* Fibre channel */
677*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  FCP-2 World Wide Name:\n",
678*44704f69SBart Van Assche                           lip);
679*44704f69SBart Van Assche             if (0 != tpid_format)
680*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s  [Unexpected TPID format: "
681*44704f69SBart Van Assche                               "%d]\n", lip, tpid_format);
682*44704f69SBart Van Assche             n += hex2str(bp + 8, 8, lip, 1, blen - n, b + n);
683*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
684*44704f69SBart Van Assche             break;
685*44704f69SBart Van Assche         case TPROTO_SPI:        /* Scsi Parallel Interface, obsolete */
686*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  Parallel SCSI initiator SCSI "
687*44704f69SBart Van Assche                           "address: 0x%x\n", lip,
688*44704f69SBart Van Assche                           sg_get_unaligned_be16(bp + 2));
689*44704f69SBart Van Assche             if (0 != tpid_format)
690*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s  [Unexpected TPID format: "
691*44704f69SBart Van Assche                               "%d]\n", lip, tpid_format);
692*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  relative port number (of "
693*44704f69SBart Van Assche                           "corresponding target): 0x%x\n", lip,
694*44704f69SBart Van Assche                           sg_get_unaligned_be16(bp + 6));
695*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
696*44704f69SBart Van Assche             break;
697*44704f69SBart Van Assche         case TPROTO_SSA:
698*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  SSA (transport id not "
699*44704f69SBart Van Assche                           "defined):\n", lip);
700*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  TPID format: %d\n", lip,
701*44704f69SBart Van Assche                           tpid_format);
702*44704f69SBart Van Assche             n += hex2str(bp, normal_len, lip, 1, blen - n, b + n);
703*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
704*44704f69SBart Van Assche             break;
705*44704f69SBart Van Assche         case TPROTO_1394: /* IEEE 1394 */
706*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  IEEE 1394 EUI-64 name:\n",
707*44704f69SBart Van Assche                           lip);
708*44704f69SBart Van Assche             if (0 != tpid_format)
709*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s  [Unexpected TPID format: "
710*44704f69SBart Van Assche                               "%d]\n", lip, tpid_format);
711*44704f69SBart Van Assche             n += hex2str(&bp[8], 8, lip, 1, blen - n, b + n);
712*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
713*44704f69SBart Van Assche             break;
714*44704f69SBart Van Assche         case TPROTO_SRP:        /* SCSI over RDMA */
715*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  RDMA initiator port "
716*44704f69SBart Van Assche                           "identifier:\n", lip);
717*44704f69SBart Van Assche             if (0 != tpid_format)
718*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s  [Unexpected TPID format: "
719*44704f69SBart Van Assche                               "%d]\n", lip, tpid_format);
720*44704f69SBart Van Assche             n += hex2str(bp + 8, 16, lip, 1, blen - n, b + n);
721*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
722*44704f69SBart Van Assche             break;
723*44704f69SBart Van Assche         case TPROTO_ISCSI:
724*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  iSCSI ", lip);
725*44704f69SBart Van Assche             num = sg_get_unaligned_be16(bp + 2);
726*44704f69SBart Van Assche             if (0 == tpid_format)
727*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "name: %.*s\n", num, &bp[4]);
728*44704f69SBart Van Assche             else if (1 == tpid_format)
729*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "world wide unique port id: "
730*44704f69SBart Van Assche                               "%.*s\n", num, &bp[4]);
731*44704f69SBart Van Assche             else {
732*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "  [Unexpected TPID format: "
733*44704f69SBart Van Assche                               "%d]\n", tpid_format);
734*44704f69SBart Van Assche                 n += hex2str(bp, num + 4, lip, 0, blen - n, b + n);
735*44704f69SBart Van Assche             }
736*44704f69SBart Van Assche             bump = (((num + 4) < TRANSPORT_ID_MIN_LEN) ?
737*44704f69SBart Van Assche                          TRANSPORT_ID_MIN_LEN : num + 4);
738*44704f69SBart Van Assche             break;
739*44704f69SBart Van Assche         case TPROTO_SAS:
740*44704f69SBart Van Assche             ull = sg_get_unaligned_be64(bp + 4);
741*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  SAS address: 0x%" PRIx64 "\n",
742*44704f69SBart Van Assche                           lip, ull);
743*44704f69SBart Van Assche             if (0 != tpid_format)
744*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s  [Unexpected TPID format: "
745*44704f69SBart Van Assche                               "%d]\n", lip, tpid_format);
746*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
747*44704f69SBart Van Assche             break;
748*44704f69SBart Van Assche         case TPROTO_ADT:        /* no TransportID defined by T10 yet */
749*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  ADT:\n", lip);
750*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  TPID format: %d\n", lip,
751*44704f69SBart Van Assche                           tpid_format);
752*44704f69SBart Van Assche             n += hex2str(bp, normal_len, lip, 1, blen - n, b + n);
753*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
754*44704f69SBart Van Assche             break;
755*44704f69SBart Van Assche         case TPROTO_ATA:        /* no TransportID defined by T10 yet */
756*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  ATAPI:\n", lip);
757*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  TPID format: %d\n", lip,
758*44704f69SBart Van Assche                           tpid_format);
759*44704f69SBart Van Assche             n += hex2str(bp, normal_len, lip, 1, blen - n, b + n);
760*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
761*44704f69SBart Van Assche             break;
762*44704f69SBart Van Assche         case TPROTO_UAS:        /* no TransportID defined by T10 yet */
763*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  UAS:\n", lip);
764*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  TPID format: %d\n", lip,
765*44704f69SBart Van Assche                           tpid_format);
766*44704f69SBart Van Assche             n += hex2str(bp, normal_len, lip, 1, blen - n, b + n);
767*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
768*44704f69SBart Van Assche             break;
769*44704f69SBart Van Assche         case TPROTO_SOP:
770*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  SOP ", lip);
771*44704f69SBart Van Assche             num = sg_get_unaligned_be16(bp + 2);
772*44704f69SBart Van Assche             if (0 == tpid_format)
773*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "Routing ID: 0x%x\n", num);
774*44704f69SBart Van Assche             else {
775*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "  [Unexpected TPID format: "
776*44704f69SBart Van Assche                               "%d]\n", tpid_format);
777*44704f69SBart Van Assche                 n += hex2str(bp, normal_len, lip, 1, blen - n, b + n);
778*44704f69SBart Van Assche             }
779*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
780*44704f69SBart Van Assche             break;
781*44704f69SBart Van Assche         case TPROTO_PCIE:       /* no TransportID defined by T10 yet */
782*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  PCIE:\n", lip);
783*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  TPID format: %d\n", lip,
784*44704f69SBart Van Assche                           tpid_format);
785*44704f69SBart Van Assche             n += hex2str(bp, normal_len, lip, 1, blen - n, b + n);
786*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
787*44704f69SBart Van Assche             break;
788*44704f69SBart Van Assche         case TPROTO_NONE:       /* no TransportID defined by T10 */
789*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  No specified protocol\n",
790*44704f69SBart Van Assche                           lip);
791*44704f69SBart Van Assche             /* n += hex2str(bp, ((bplen > 24) ? 24 : bplen),
792*44704f69SBart Van Assche              *                 lip, 0, blen - n, b + n); */
793*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
794*44704f69SBart Van Assche             break;
795*44704f69SBart Van Assche         default:
796*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s  unknown protocol id=0x%x  "
797*44704f69SBart Van Assche                           "TPID format=%d\n", lip, proto_id, tpid_format);
798*44704f69SBart Van Assche             n += hex2str(bp, normal_len, lip, 1, blen - n, b + n);
799*44704f69SBart Van Assche             bump = TRANSPORT_ID_MIN_LEN;
800*44704f69SBart Van Assche             break;
801*44704f69SBart Van Assche         }
802*44704f69SBart Van Assche     }
803*44704f69SBart Van Assche     return b;
804*44704f69SBart Van Assche }
805*44704f69SBart Van Assche 
806*44704f69SBart Van Assche 
807*44704f69SBart Van Assche static const char * desig_code_set_str_arr[] =
808*44704f69SBart Van Assche {
809*44704f69SBart Van Assche     "Reserved [0x0]",
810*44704f69SBart Van Assche     "Binary",
811*44704f69SBart Van Assche     "ASCII",
812*44704f69SBart Van Assche     "UTF-8",
813*44704f69SBart Van Assche     "Reserved [0x4]", "Reserved [0x5]", "Reserved [0x6]", "Reserved [0x7]",
814*44704f69SBart Van Assche     "Reserved [0x8]", "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]",
815*44704f69SBart Van Assche     "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
816*44704f69SBart Van Assche };
817*44704f69SBart Van Assche 
818*44704f69SBart Van Assche const char *
sg_get_desig_code_set_str(int val)819*44704f69SBart Van Assche sg_get_desig_code_set_str(int val)
820*44704f69SBart Van Assche {
821*44704f69SBart Van Assche     if ((val >= 0) && (val < (int)SG_ARRAY_SIZE(desig_code_set_str_arr)))
822*44704f69SBart Van Assche         return desig_code_set_str_arr[val];
823*44704f69SBart Van Assche     else
824*44704f69SBart Van Assche         return NULL;
825*44704f69SBart Van Assche }
826*44704f69SBart Van Assche 
827*44704f69SBart Van Assche static const char * desig_assoc_str_arr[] =
828*44704f69SBart Van Assche {
829*44704f69SBart Van Assche     "Addressed logical unit",
830*44704f69SBart Van Assche     "Target port",      /* that received request; unless SCSI ports VPD */
831*44704f69SBart Van Assche     "Target device that contains addressed lu",
832*44704f69SBart Van Assche     "Reserved [0x3]",
833*44704f69SBart Van Assche };
834*44704f69SBart Van Assche 
835*44704f69SBart Van Assche const char *
sg_get_desig_assoc_str(int val)836*44704f69SBart Van Assche sg_get_desig_assoc_str(int val)
837*44704f69SBart Van Assche {
838*44704f69SBart Van Assche     if ((val >= 0) && (val < (int)SG_ARRAY_SIZE(desig_assoc_str_arr)))
839*44704f69SBart Van Assche         return desig_assoc_str_arr[val];
840*44704f69SBart Van Assche     else
841*44704f69SBart Van Assche         return NULL;
842*44704f69SBart Van Assche }
843*44704f69SBart Van Assche 
844*44704f69SBart Van Assche static const char * desig_type_str_arr[] =
845*44704f69SBart Van Assche {
846*44704f69SBart Van Assche     "Vendor specific [0x0]",
847*44704f69SBart Van Assche     "T10 vendor identification",
848*44704f69SBart Van Assche     "EUI-64 based",
849*44704f69SBart Van Assche     "NAA",
850*44704f69SBart Van Assche     "Relative target port",
851*44704f69SBart Van Assche     "Target port group",        /* spc4r09: _primary_ target port group */
852*44704f69SBart Van Assche     "Logical unit group",
853*44704f69SBart Van Assche     "MD5 logical unit identifier",
854*44704f69SBart Van Assche     "SCSI name string",
855*44704f69SBart Van Assche     "Protocol specific port identifier",        /* spc4r36 */
856*44704f69SBart Van Assche     "UUID identifier",          /* spc5r08 */
857*44704f69SBart Van Assche     "Reserved [0xb]",
858*44704f69SBart Van Assche     "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
859*44704f69SBart Van Assche };
860*44704f69SBart Van Assche 
861*44704f69SBart Van Assche const char *
sg_get_desig_type_str(int val)862*44704f69SBart Van Assche sg_get_desig_type_str(int val)
863*44704f69SBart Van Assche {
864*44704f69SBart Van Assche     if ((val >= 0) && (val < (int)SG_ARRAY_SIZE(desig_type_str_arr)))
865*44704f69SBart Van Assche         return desig_type_str_arr[val];
866*44704f69SBart Van Assche     else
867*44704f69SBart Van Assche         return NULL;
868*44704f69SBart Van Assche }
869*44704f69SBart Van Assche 
870*44704f69SBart Van Assche char *
sg_get_zone_type_str(uint8_t zt,int buff_len,char * buff)871*44704f69SBart Van Assche sg_get_zone_type_str(uint8_t zt, int buff_len, char * buff)
872*44704f69SBart Van Assche {
873*44704f69SBart Van Assche     if ((NULL == buff) || (buff_len < 1))
874*44704f69SBart Van Assche         return NULL;
875*44704f69SBart Van Assche     switch (zt) {
876*44704f69SBart Van Assche     case 1:
877*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "conventional");
878*44704f69SBart Van Assche         break;
879*44704f69SBart Van Assche     case 2:
880*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "sequential write required");
881*44704f69SBart Van Assche         break;
882*44704f69SBart Van Assche     case 3:
883*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "sequential write preferred");
884*44704f69SBart Van Assche         break;
885*44704f69SBart Van Assche     case 4:
886*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "sequential or before required");
887*44704f69SBart Van Assche         break;
888*44704f69SBart Van Assche     case 5:
889*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "gap");
890*44704f69SBart Van Assche         break;
891*44704f69SBart Van Assche     default:
892*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "unknown [0x%x]", zt);
893*44704f69SBart Van Assche         break;
894*44704f69SBart Van Assche     }
895*44704f69SBart Van Assche     return buff;
896*44704f69SBart Van Assche }
897*44704f69SBart Van Assche 
898*44704f69SBart Van Assche 
899*44704f69SBart Van Assche /* Expects a T10 UUID designator (as found in the Device Identification VPD
900*44704f69SBart Van Assche  * page) pointed to by 'dp'. To not produce an error string in 'b', c_set
901*44704f69SBart Van Assche  * should be 1 (binary) and dlen should be 18. Currently T10 only supports
902*44704f69SBart Van Assche  * locally assigned UUIDs. Writes output to string 'b' of no more than blen
903*44704f69SBart Van Assche  * bytes and returns the number of bytes actually written to 'b' but doesn't
904*44704f69SBart Van Assche  * count the trailing null character it always appends (if blen > 0). 'lip'
905*44704f69SBart Van Assche  * is lead-in string (on each line) than may be NULL. skip_prefix avoids
906*44704f69SBart Van Assche  * outputting: '   Locally assigned UUID: ' before the UUID. */
907*44704f69SBart Van Assche int
sg_t10_uuid_desig2str(const uint8_t * dp,int dlen,int c_set,bool do_long,bool skip_prefix,const char * lip,int blen,char * b)908*44704f69SBart Van Assche sg_t10_uuid_desig2str(const uint8_t *dp, int dlen, int c_set, bool do_long,
909*44704f69SBart Van Assche                       bool skip_prefix, const char * lip /* lead-in */,
910*44704f69SBart Van Assche                       int blen, char * b)
911*44704f69SBart Van Assche {
912*44704f69SBart Van Assche     int m;
913*44704f69SBart Van Assche     int n = 0;
914*44704f69SBart Van Assche 
915*44704f69SBart Van Assche     if (NULL == lip)
916*44704f69SBart Van Assche         lip = "";
917*44704f69SBart Van Assche     if (1 != c_set) {
918*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      << expected binary "
919*44704f69SBart Van Assche                       "code_set >>\n", lip);
920*44704f69SBart Van Assche         n += hex2str(dp, dlen, lip, 0, blen - n, b + n);
921*44704f69SBart Van Assche         return n;
922*44704f69SBart Van Assche     }
923*44704f69SBart Van Assche     if ((1 != ((dp[0] >> 4) & 0xf)) || (18 != dlen)) {
924*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      << expected locally "
925*44704f69SBart Van Assche                       "assigned UUID, 16 bytes long >>\n", lip);
926*44704f69SBart Van Assche         n += hex2str(dp, dlen, lip, 0, blen - n, b + n);
927*44704f69SBart Van Assche         return n;
928*44704f69SBart Van Assche     }
929*44704f69SBart Van Assche     if (skip_prefix) {
930*44704f69SBart Van Assche         if (strlen(lip) > 0)
931*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s", lip);
932*44704f69SBart Van Assche     } else
933*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      Locally assigned UUID: ",
934*44704f69SBart Van Assche                       lip);
935*44704f69SBart Van Assche     for (m = 0; m < 16; ++m) {
936*44704f69SBart Van Assche         if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
937*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "-");
938*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)dp[2 + m]);
939*44704f69SBart Van Assche     }
940*44704f69SBart Van Assche     n += sg_scnpr(b + n, blen - n, "\n");
941*44704f69SBart Van Assche     if (do_long) {
942*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      [0x", lip);
943*44704f69SBart Van Assche         for (m = 0; m < 16; ++m)
944*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)dp[2 + m]);
945*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "]\n");
946*44704f69SBart Van Assche     }
947*44704f69SBart Van Assche     return n;
948*44704f69SBart Van Assche }
949*44704f69SBart Van Assche 
950*44704f69SBart Van Assche int
sg_get_designation_descriptor_str(const char * lip,const uint8_t * ddp,int dd_len,bool print_assoc,bool do_long,int blen,char * b)951*44704f69SBart Van Assche sg_get_designation_descriptor_str(const char * lip, const uint8_t * ddp,
952*44704f69SBart Van Assche                                   int dd_len, bool print_assoc, bool do_long,
953*44704f69SBart Van Assche                                   int blen, char * b)
954*44704f69SBart Van Assche {
955*44704f69SBart Van Assche     int m, p_id, piv, c_set, assoc, desig_type, ci_off, c_id, d_id, naa;
956*44704f69SBart Van Assche     int vsi, k, n, dlen;
957*44704f69SBart Van Assche     uint64_t ccc_id, vsei;
958*44704f69SBart Van Assche     const uint8_t * ip;
959*44704f69SBart Van Assche     char e[64];
960*44704f69SBart Van Assche     const char * cp;
961*44704f69SBart Van Assche 
962*44704f69SBart Van Assche     n = 0;
963*44704f69SBart Van Assche     if (NULL == lip)
964*44704f69SBart Van Assche         lip = "";
965*44704f69SBart Van Assche     if (dd_len < 4) {
966*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%sdesignator desc too short: got "
967*44704f69SBart Van Assche                       "length of %d want 4 or more\n", lip, dd_len);
968*44704f69SBart Van Assche         return n;
969*44704f69SBart Van Assche     }
970*44704f69SBart Van Assche     dlen = ddp[3];
971*44704f69SBart Van Assche     if (dlen > (dd_len - 4)) {
972*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%sdesignator too long: says it is %d "
973*44704f69SBart Van Assche                       "bytes, but given %d bytes\n", lip, dlen, dd_len - 4);
974*44704f69SBart Van Assche         return n;
975*44704f69SBart Van Assche     }
976*44704f69SBart Van Assche     ip = ddp + 4;
977*44704f69SBart Van Assche     p_id = ((ddp[0] >> 4) & 0xf);
978*44704f69SBart Van Assche     c_set = (ddp[0] & 0xf);
979*44704f69SBart Van Assche     piv = ((ddp[1] & 0x80) ? 1 : 0);
980*44704f69SBart Van Assche     assoc = ((ddp[1] >> 4) & 0x3);
981*44704f69SBart Van Assche     desig_type = (ddp[1] & 0xf);
982*44704f69SBart Van Assche     if (print_assoc && ((cp = sg_get_desig_assoc_str(assoc))))
983*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s  %s:\n", lip, cp);
984*44704f69SBart Van Assche     n += sg_scnpr(b + n, blen - n, "%s    designator type: ", lip);
985*44704f69SBart Van Assche     cp = sg_get_desig_type_str(desig_type);
986*44704f69SBart Van Assche     if (cp)
987*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s", cp);
988*44704f69SBart Van Assche     n += sg_scnpr(b + n, blen - n, ",  code set: ");
989*44704f69SBart Van Assche     cp = sg_get_desig_code_set_str(c_set);
990*44704f69SBart Van Assche     if (cp)
991*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s", cp);
992*44704f69SBart Van Assche     n += sg_scnpr(b + n, blen - n, "\n");
993*44704f69SBart Van Assche     if (piv && ((1 == assoc) || (2 == assoc)))
994*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s     transport: %s\n", lip,
995*44704f69SBart Van Assche                       sg_get_trans_proto_str(p_id, sizeof(e), e));
996*44704f69SBart Van Assche     switch (desig_type) {
997*44704f69SBart Van Assche     case 0: /* vendor specific */
998*44704f69SBart Van Assche         k = 0;
999*44704f69SBart Van Assche         if ((1 == c_set) || (2 == c_set)) { /* ASCII or UTF-8 */
1000*44704f69SBart Van Assche             for (k = 0; (k < dlen) && my_isprint(ip[k]); ++k)
1001*44704f69SBart Van Assche                 ;
1002*44704f69SBart Van Assche             if (k >= dlen)
1003*44704f69SBart Van Assche                 k = 1;
1004*44704f69SBart Van Assche         }
1005*44704f69SBart Van Assche         if (k)
1006*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      vendor specific: %.*s\n",
1007*44704f69SBart Van Assche                           lip, dlen, ip);
1008*44704f69SBart Van Assche         else {
1009*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      vendor specific:\n", lip);
1010*44704f69SBart Van Assche             n += hex2str(ip, dlen, lip, 0, blen - n, b + n);
1011*44704f69SBart Van Assche         }
1012*44704f69SBart Van Assche         break;
1013*44704f69SBart Van Assche     case 1: /* T10 vendor identification */
1014*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      vendor id: %.8s\n", lip, ip);
1015*44704f69SBart Van Assche         if (dlen > 8) {
1016*44704f69SBart Van Assche             if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
1017*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      vendor specific: "
1018*44704f69SBart Van Assche                               "%.*s\n", lip, dlen - 8, ip + 8);
1019*44704f69SBart Van Assche             } else {
1020*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      vendor specific: 0x",
1021*44704f69SBart Van Assche                               lip);
1022*44704f69SBart Van Assche                 for (m = 8; m < dlen; ++m)
1023*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1024*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "\n");
1025*44704f69SBart Van Assche             }
1026*44704f69SBart Van Assche         }
1027*44704f69SBart Van Assche         break;
1028*44704f69SBart Van Assche     case 2: /* EUI-64 based */
1029*44704f69SBart Van Assche         if (! do_long) {
1030*44704f69SBart Van Assche             if ((8 != dlen) && (12 != dlen) && (16 != dlen)) {
1031*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      << expect 8, 12 and "
1032*44704f69SBart Van Assche                               "16 byte EUI, got %d >>\n", lip, dlen);
1033*44704f69SBart Van Assche                 n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1034*44704f69SBart Van Assche                 break;
1035*44704f69SBart Van Assche             }
1036*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      0x", lip);
1037*44704f69SBart Van Assche             for (m = 0; m < dlen; ++m)
1038*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1039*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "\n");
1040*44704f69SBart Van Assche             break;
1041*44704f69SBart Van Assche         }
1042*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      EUI-64 based %d byte "
1043*44704f69SBart Van Assche                       "identifier\n", lip, dlen);
1044*44704f69SBart Van Assche         if (1 != c_set) {
1045*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      << expected binary "
1046*44704f69SBart Van Assche                           "code_set (1) >>\n", lip);
1047*44704f69SBart Van Assche             n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1048*44704f69SBart Van Assche             break;
1049*44704f69SBart Van Assche         }
1050*44704f69SBart Van Assche         ci_off = 0;
1051*44704f69SBart Van Assche         if (16 == dlen) {       /* first 8 bytes are 'Identifier Extension' */
1052*44704f69SBart Van Assche             uint64_t id_ext = sg_get_unaligned_be64(ip);
1053*44704f69SBart Van Assche 
1054*44704f69SBart Van Assche             ci_off = 8;
1055*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      Identifier extension: 0x%"
1056*44704f69SBart Van Assche                           PRIx64 "\n", lip, id_ext);
1057*44704f69SBart Van Assche         } else if ((8 != dlen) && (12 != dlen)) {
1058*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      << can only decode 8, 12 "
1059*44704f69SBart Van Assche                           "and 16 byte ids >>\n", lip);
1060*44704f69SBart Van Assche             n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1061*44704f69SBart Van Assche             break;
1062*44704f69SBart Van Assche         }
1063*44704f69SBart Van Assche         ccc_id = sg_get_unaligned_be64(ip + ci_off);
1064*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      IEEE identifier: 0x%"
1065*44704f69SBart Van Assche                       PRIx64 "\n", lip, ccc_id);
1066*44704f69SBart Van Assche         if (12 == dlen) {
1067*44704f69SBart Van Assche             d_id = sg_get_unaligned_be32(ip + 8);
1068*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      Directory ID: 0x%x\n",
1069*44704f69SBart Van Assche                           lip, d_id);
1070*44704f69SBart Van Assche         }
1071*44704f69SBart Van Assche         break;
1072*44704f69SBart Van Assche     case 3: /* NAA <n> */
1073*44704f69SBart Van Assche         if (1 != c_set) {
1074*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      << unexpected code set "
1075*44704f69SBart Van Assche                           "%d for NAA >>\n", lip, c_set);
1076*44704f69SBart Van Assche             n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1077*44704f69SBart Van Assche             break;
1078*44704f69SBart Van Assche         }
1079*44704f69SBart Van Assche         naa = (ip[0] >> 4) & 0xff;
1080*44704f69SBart Van Assche         switch (naa) {
1081*44704f69SBart Van Assche         case 2:         /* NAA 2: IEEE Extended */
1082*44704f69SBart Van Assche             if (8 != dlen) {
1083*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      << unexpected NAA 2 "
1084*44704f69SBart Van Assche                               "identifier length: 0x%x >>\n", lip, dlen);
1085*44704f69SBart Van Assche                 n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1086*44704f69SBart Van Assche                 break;
1087*44704f69SBart Van Assche             }
1088*44704f69SBart Van Assche             d_id = (((ip[0] & 0xf) << 8) | ip[1]);
1089*44704f69SBart Van Assche             c_id = sg_get_unaligned_be24(ip + 2);
1090*44704f69SBart Van Assche             vsi = sg_get_unaligned_be24(ip + 5);
1091*44704f69SBart Van Assche             if (do_long) {
1092*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      NAA 2, vendor "
1093*44704f69SBart Van Assche                               "specific identifier A: 0x%x\n", lip, d_id);
1094*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      AOI: 0x%x\n", lip,
1095*44704f69SBart Van Assche                               c_id);
1096*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      vendor specific "
1097*44704f69SBart Van Assche                               "identifier B: 0x%x\n", lip, vsi);
1098*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      [0x", lip);
1099*44704f69SBart Van Assche                 for (m = 0; m < 8; ++m)
1100*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1101*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "]\n");
1102*44704f69SBart Van Assche             }
1103*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      0x", lip);
1104*44704f69SBart Van Assche             for (m = 0; m < 8; ++m)
1105*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1106*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "\n");
1107*44704f69SBart Van Assche             break;
1108*44704f69SBart Van Assche         case 3:         /* NAA 3: Locally assigned */
1109*44704f69SBart Van Assche             if (8 != dlen) {
1110*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      << unexpected NAA 3 "
1111*44704f69SBart Van Assche                               "identifier length: 0x%x >>\n", lip, dlen);
1112*44704f69SBart Van Assche                 n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1113*44704f69SBart Van Assche                 break;
1114*44704f69SBart Van Assche             }
1115*44704f69SBart Van Assche             if (do_long)
1116*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      NAA 3, Locally "
1117*44704f69SBart Van Assche                               "assigned:\n", lip);
1118*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      0x", lip);
1119*44704f69SBart Van Assche             for (m = 0; m < 8; ++m)
1120*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1121*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "\n");
1122*44704f69SBart Van Assche             break;
1123*44704f69SBart Van Assche         case 5:         /* NAA 5: IEEE Registered */
1124*44704f69SBart Van Assche             if (8 != dlen) {
1125*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      << unexpected NAA 5 "
1126*44704f69SBart Van Assche                               "identifier length: 0x%x >>\n", lip, dlen);
1127*44704f69SBart Van Assche                 n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1128*44704f69SBart Van Assche                 break;
1129*44704f69SBart Van Assche             }
1130*44704f69SBart Van Assche             c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
1131*44704f69SBart Van Assche                     (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
1132*44704f69SBart Van Assche             vsei = ip[3] & 0xf;
1133*44704f69SBart Van Assche             for (m = 1; m < 5; ++m) {
1134*44704f69SBart Van Assche                 vsei <<= 8;
1135*44704f69SBart Van Assche                 vsei |= ip[3 + m];
1136*44704f69SBart Van Assche             }
1137*44704f69SBart Van Assche             if (do_long) {
1138*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      NAA 5, AOI: 0x%x\n",
1139*44704f69SBart Van Assche                               lip, c_id);
1140*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      Vendor Specific "
1141*44704f69SBart Van Assche                               "Identifier: 0x%" PRIx64 "\n", lip, vsei);
1142*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      [0x", lip);
1143*44704f69SBart Van Assche                 for (m = 0; m < 8; ++m)
1144*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1145*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "]\n");
1146*44704f69SBart Van Assche             } else {
1147*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      0x", lip);
1148*44704f69SBart Van Assche                 for (m = 0; m < 8; ++m)
1149*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1150*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "\n");
1151*44704f69SBart Van Assche             }
1152*44704f69SBart Van Assche             break;
1153*44704f69SBart Van Assche         case 6:         /* NAA 6: IEEE Registered extended */
1154*44704f69SBart Van Assche             if (16 != dlen) {
1155*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      << unexpected NAA 6 "
1156*44704f69SBart Van Assche                               "identifier length: 0x%x >>\n", lip, dlen);
1157*44704f69SBart Van Assche                 n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1158*44704f69SBart Van Assche                 break;
1159*44704f69SBart Van Assche             }
1160*44704f69SBart Van Assche             c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
1161*44704f69SBart Van Assche                     (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
1162*44704f69SBart Van Assche             vsei = ip[3] & 0xf;
1163*44704f69SBart Van Assche             for (m = 1; m < 5; ++m) {
1164*44704f69SBart Van Assche                 vsei <<= 8;
1165*44704f69SBart Van Assche                 vsei |= ip[3 + m];
1166*44704f69SBart Van Assche             }
1167*44704f69SBart Van Assche             if (do_long) {
1168*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      NAA 6, AOI: 0x%x\n",
1169*44704f69SBart Van Assche                               lip, c_id);
1170*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      Vendor Specific "
1171*44704f69SBart Van Assche                               "Identifier: 0x%" PRIx64 "\n", lip, vsei);
1172*44704f69SBart Van Assche                 vsei = sg_get_unaligned_be64(ip + 8);
1173*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      Vendor Specific "
1174*44704f69SBart Van Assche                               "Identifier Extension: 0x%" PRIx64 "\n", lip,
1175*44704f69SBart Van Assche                               vsei);
1176*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      [0x", lip);
1177*44704f69SBart Van Assche                 for (m = 0; m < 16; ++m)
1178*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1179*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "]\n");
1180*44704f69SBart Van Assche             } else {
1181*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      0x", lip);
1182*44704f69SBart Van Assche                 for (m = 0; m < 16; ++m)
1183*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", (my_uint)ip[m]);
1184*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "\n");
1185*44704f69SBart Van Assche             }
1186*44704f69SBart Van Assche             break;
1187*44704f69SBart Van Assche         default:
1188*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      << unexpected NAA [0x%x] "
1189*44704f69SBart Van Assche                           ">>\n", lip, naa);
1190*44704f69SBart Van Assche             n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1191*44704f69SBart Van Assche             break;
1192*44704f69SBart Van Assche         }
1193*44704f69SBart Van Assche         break;
1194*44704f69SBart Van Assche     case 4: /* Relative target port */
1195*44704f69SBart Van Assche         if ((1 != c_set) || (1 != assoc) || (4 != dlen)) {
1196*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      << expected binary "
1197*44704f69SBart Van Assche                           "code_set, target port association, length 4 >>\n",
1198*44704f69SBart Van Assche                           lip);
1199*44704f69SBart Van Assche             n += hex2str(ip, dlen, "", 1, blen - n, b + n);
1200*44704f69SBart Van Assche             break;
1201*44704f69SBart Van Assche         }
1202*44704f69SBart Van Assche         d_id = sg_get_unaligned_be16(ip + 2);
1203*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      Relative target port: 0x%x\n",
1204*44704f69SBart Van Assche                       lip, d_id);
1205*44704f69SBart Van Assche         break;
1206*44704f69SBart Van Assche     case 5: /* (primary) Target port group */
1207*44704f69SBart Van Assche         if ((1 != c_set) || (1 != assoc) || (4 != dlen)) {
1208*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      << expected binary "
1209*44704f69SBart Van Assche                           "code_set, target port association, length 4 >>\n",
1210*44704f69SBart Van Assche                           lip);
1211*44704f69SBart Van Assche             n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1212*44704f69SBart Van Assche             break;
1213*44704f69SBart Van Assche         }
1214*44704f69SBart Van Assche         d_id = sg_get_unaligned_be16(ip + 2);
1215*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      Target port group: 0x%x\n",
1216*44704f69SBart Van Assche                       lip, d_id);
1217*44704f69SBart Van Assche         break;
1218*44704f69SBart Van Assche     case 6: /* Logical unit group */
1219*44704f69SBart Van Assche         if ((1 != c_set) || (0 != assoc) || (4 != dlen)) {
1220*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      << expected binary "
1221*44704f69SBart Van Assche                           "code_set, logical unit association, length 4 >>\n",
1222*44704f69SBart Van Assche                           lip);
1223*44704f69SBart Van Assche             n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1224*44704f69SBart Van Assche             break;
1225*44704f69SBart Van Assche         }
1226*44704f69SBart Van Assche         d_id = sg_get_unaligned_be16(ip + 2);
1227*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      Logical unit group: 0x%x\n",
1228*44704f69SBart Van Assche                       lip, d_id);
1229*44704f69SBart Van Assche         break;
1230*44704f69SBart Van Assche     case 7: /* MD5 logical unit identifier */
1231*44704f69SBart Van Assche         if ((1 != c_set) || (0 != assoc)) {
1232*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      << expected binary "
1233*44704f69SBart Van Assche                           "code_set, logical unit association >>\n", lip);
1234*44704f69SBart Van Assche             n += hex2str(ip, dlen, "", 1, blen - n, b + n);
1235*44704f69SBart Van Assche             break;
1236*44704f69SBart Van Assche         }
1237*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      MD5 logical unit "
1238*44704f69SBart Van Assche                       "identifier:\n", lip);
1239*44704f69SBart Van Assche         n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1240*44704f69SBart Van Assche         break;
1241*44704f69SBart Van Assche     case 8: /* SCSI name string */
1242*44704f69SBart Van Assche         if (3 != c_set) {       /* accept ASCII as subset of UTF-8 */
1243*44704f69SBart Van Assche             if (2 == c_set) {
1244*44704f69SBart Van Assche                 if (do_long)
1245*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%s      << expected "
1246*44704f69SBart Van Assche                                   "UTF-8, use ASCII >>\n", lip);
1247*44704f69SBart Van Assche             } else {
1248*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s      << expected UTF-8 "
1249*44704f69SBart Van Assche                               "code_set >>\n", lip);
1250*44704f69SBart Van Assche                 n += hex2str(ip, dlen, lip, 0, blen - n, b + n);
1251*44704f69SBart Van Assche                 break;
1252*44704f69SBart Van Assche             }
1253*44704f69SBart Van Assche         }
1254*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      SCSI name string:\n", lip);
1255*44704f69SBart Van Assche         /* does %s print out UTF-8 ok??
1256*44704f69SBart Van Assche          * Seems to depend on the locale. Looks ok here with my
1257*44704f69SBart Van Assche          * locale setting: en_AU.UTF-8
1258*44704f69SBart Van Assche          */
1259*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      %.*s\n", lip, dlen,
1260*44704f69SBart Van Assche                       (const char *)ip);
1261*44704f69SBart Van Assche         break;
1262*44704f69SBart Van Assche     case 9: /* Protocol specific port identifier */
1263*44704f69SBart Van Assche         /* added in spc4r36, PIV must be set, proto_id indicates */
1264*44704f69SBart Van Assche         /* whether UAS (USB) or SOP (PCIe) or ... */
1265*44704f69SBart Van Assche         if (! piv)
1266*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, " %s      >>>> Protocol specific "
1267*44704f69SBart Van Assche                           "port identifier expects protocol\n%s          "
1268*44704f69SBart Van Assche                           "identifier to be valid and it is not\n", lip, lip);
1269*44704f69SBart Van Assche         if (TPROTO_UAS == p_id) {
1270*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      USB device address: "
1271*44704f69SBart Van Assche                           "0x%x\n", lip, 0x7f & ip[0]);
1272*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      USB interface number: "
1273*44704f69SBart Van Assche                           "0x%x\n", lip, ip[2]);
1274*44704f69SBart Van Assche         } else if (TPROTO_SOP == p_id) {
1275*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      PCIe routing ID, bus "
1276*44704f69SBart Van Assche                           "number: 0x%x\n", lip, ip[0]);
1277*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s          function number: "
1278*44704f69SBart Van Assche                           "0x%x\n", lip, ip[1]);
1279*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s          [or device number: "
1280*44704f69SBart Van Assche                           "0x%x, function number: 0x%x]\n", lip,
1281*44704f69SBart Van Assche                           (0x1f & (ip[1] >> 3)), 0x7 & ip[1]);
1282*44704f69SBart Van Assche         } else
1283*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      >>>> unexpected protocol "
1284*44704f69SBart Van Assche                           "identifier: %s\n%s           with Protocol "
1285*44704f69SBart Van Assche                           "specific port identifier\n", lip,
1286*44704f69SBart Van Assche                           sg_get_trans_proto_str(p_id, sizeof(e), e), lip);
1287*44704f69SBart Van Assche         break;
1288*44704f69SBart Van Assche     case 0xa: /* UUID identifier */
1289*44704f69SBart Van Assche         n += sg_t10_uuid_desig2str(ip, dlen, c_set, do_long, false, lip,
1290*44704f69SBart Van Assche                                    blen - n, b + n);
1291*44704f69SBart Van Assche         break;
1292*44704f69SBart Van Assche     default: /* reserved */
1293*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      reserved designator=0x%x\n",
1294*44704f69SBart Van Assche                       lip, desig_type);
1295*44704f69SBart Van Assche         n += hex2str(ip, dlen, lip, 1, blen - n, b + n);
1296*44704f69SBart Van Assche         break;
1297*44704f69SBart Van Assche     }
1298*44704f69SBart Van Assche     return n;
1299*44704f69SBart Van Assche }
1300*44704f69SBart Van Assche 
1301*44704f69SBart Van Assche static int
decode_sks(const char * lip,const uint8_t * descp,int add_d_len,int sense_key,bool * processedp,int blen,char * b)1302*44704f69SBart Van Assche decode_sks(const char * lip, const uint8_t * descp, int add_d_len,
1303*44704f69SBart Van Assche            int sense_key, bool * processedp, int blen, char * b)
1304*44704f69SBart Van Assche {
1305*44704f69SBart Van Assche     int progress, pr, rem, n;
1306*44704f69SBart Van Assche 
1307*44704f69SBart Van Assche     n = 0;
1308*44704f69SBart Van Assche     if (NULL == lip)
1309*44704f69SBart Van Assche         lip = "";
1310*44704f69SBart Van Assche     switch (sense_key) {
1311*44704f69SBart Van Assche     case SPC_SK_ILLEGAL_REQUEST:
1312*44704f69SBart Van Assche         if (add_d_len < 6) {
1313*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Field pointer: ");
1314*44704f69SBart Van Assche             goto too_short;
1315*44704f69SBart Van Assche         }
1316*44704f69SBart Van Assche         /* abbreviate to fit on one line */
1317*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "Field pointer:\n");
1318*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s        Error in %s: byte %d", lip,
1319*44704f69SBart Van Assche                       (descp[4] & 0x40) ? "Command" : "Data parameters",
1320*44704f69SBart Van Assche                       sg_get_unaligned_be16(descp + 5));
1321*44704f69SBart Van Assche         if (descp[4] & 0x08) {
1322*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, " bit %d\n", descp[4] & 0x07);
1323*44704f69SBart Van Assche         } else
1324*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "\n");
1325*44704f69SBart Van Assche         break;
1326*44704f69SBart Van Assche     case SPC_SK_HARDWARE_ERROR:
1327*44704f69SBart Van Assche     case SPC_SK_MEDIUM_ERROR:
1328*44704f69SBart Van Assche     case SPC_SK_RECOVERED_ERROR:
1329*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "Actual retry count: ");
1330*44704f69SBart Van Assche         if (add_d_len < 6)
1331*44704f69SBart Van Assche             goto too_short;
1332*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n,"%u\n",
1333*44704f69SBart Van Assche                       sg_get_unaligned_be16(descp + 5));
1334*44704f69SBart Van Assche         break;
1335*44704f69SBart Van Assche     case SPC_SK_NO_SENSE:
1336*44704f69SBart Van Assche     case SPC_SK_NOT_READY:
1337*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "Progress indication: ");
1338*44704f69SBart Van Assche         if (add_d_len < 6)
1339*44704f69SBart Van Assche             goto too_short;
1340*44704f69SBart Van Assche         progress = sg_get_unaligned_be16(descp + 5);
1341*44704f69SBart Van Assche         pr = (progress * 100) / 65536;
1342*44704f69SBart Van Assche         rem = ((progress * 100) % 65536) / 656;
1343*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%d.%02d%%\n", pr, rem);
1344*44704f69SBart Van Assche         break;
1345*44704f69SBart Van Assche     case SPC_SK_COPY_ABORTED:
1346*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "Segment pointer:\n");
1347*44704f69SBart Van Assche         if (add_d_len < 6)
1348*44704f69SBart Van Assche             goto too_short;
1349*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s        Relative to start of %s, "
1350*44704f69SBart Van Assche                       "byte %d", lip, (descp[4] & 0x20) ?
1351*44704f69SBart Van Assche                          "segment descriptor" : "parameter list",
1352*44704f69SBart Van Assche                       sg_get_unaligned_be16(descp + 5));
1353*44704f69SBart Van Assche         if (descp[4] & 0x08)
1354*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, " bit %d\n", descp[4] & 0x07);
1355*44704f69SBart Van Assche         else
1356*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "\n");
1357*44704f69SBart Van Assche         break;
1358*44704f69SBart Van Assche     case SPC_SK_UNIT_ATTENTION:
1359*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "Unit attention condition queue:\n");
1360*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s        overflow flag is %d\n", lip,
1361*44704f69SBart Van Assche                       !!(descp[4] & 0x1));
1362*44704f69SBart Van Assche         break;
1363*44704f69SBart Van Assche     default:
1364*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "Sense_key: 0x%x unexpected\n",
1365*44704f69SBart Van Assche                       sense_key);
1366*44704f69SBart Van Assche         *processedp = false;
1367*44704f69SBart Van Assche         break;
1368*44704f69SBart Van Assche     }
1369*44704f69SBart Van Assche     return n;
1370*44704f69SBart Van Assche 
1371*44704f69SBart Van Assche too_short:
1372*44704f69SBart Van Assche     n += sg_scnpr(b + n, blen - n, "%s\n", "   >> descriptor too short");
1373*44704f69SBart Van Assche     *processedp = false;
1374*44704f69SBart Van Assche     return n;
1375*44704f69SBart Van Assche }
1376*44704f69SBart Van Assche 
1377*44704f69SBart Van Assche #define TPGS_STATE_OPTIMIZED 0x0
1378*44704f69SBart Van Assche #define TPGS_STATE_NONOPTIMIZED 0x1
1379*44704f69SBart Van Assche #define TPGS_STATE_STANDBY 0x2
1380*44704f69SBart Van Assche #define TPGS_STATE_UNAVAILABLE 0x3
1381*44704f69SBart Van Assche #define TPGS_STATE_OFFLINE 0xe
1382*44704f69SBart Van Assche #define TPGS_STATE_TRANSITIONING 0xf
1383*44704f69SBart Van Assche 
1384*44704f69SBart Van Assche static int
decode_tpgs_state(int st,char * b,int blen)1385*44704f69SBart Van Assche decode_tpgs_state(int st, char * b, int blen)
1386*44704f69SBart Van Assche {
1387*44704f69SBart Van Assche     switch (st) {
1388*44704f69SBart Van Assche     case TPGS_STATE_OPTIMIZED:
1389*44704f69SBart Van Assche         return sg_scnpr(b, blen, "active/optimized");
1390*44704f69SBart Van Assche     case TPGS_STATE_NONOPTIMIZED:
1391*44704f69SBart Van Assche         return sg_scnpr(b, blen, "active/non optimized");
1392*44704f69SBart Van Assche     case TPGS_STATE_STANDBY:
1393*44704f69SBart Van Assche         return sg_scnpr(b, blen, "standby");
1394*44704f69SBart Van Assche     case TPGS_STATE_UNAVAILABLE:
1395*44704f69SBart Van Assche         return sg_scnpr(b, blen, "unavailable");
1396*44704f69SBart Van Assche     case TPGS_STATE_OFFLINE:
1397*44704f69SBart Van Assche         return sg_scnpr(b, blen, "offline");
1398*44704f69SBart Van Assche     case TPGS_STATE_TRANSITIONING:
1399*44704f69SBart Van Assche         return sg_scnpr(b, blen, "transitioning between states");
1400*44704f69SBart Van Assche     default:
1401*44704f69SBart Van Assche         return sg_scnpr(b, blen, "unknown: 0x%x", st);
1402*44704f69SBart Van Assche     }
1403*44704f69SBart Van Assche }
1404*44704f69SBart Van Assche 
1405*44704f69SBart Van Assche static int
uds_referral_descriptor_str(char * b,int blen,const uint8_t * dp,int alen,const char * lip)1406*44704f69SBart Van Assche uds_referral_descriptor_str(char * b, int blen, const uint8_t * dp,
1407*44704f69SBart Van Assche                             int alen, const char * lip)
1408*44704f69SBart Van Assche {
1409*44704f69SBart Van Assche     int n = 0;
1410*44704f69SBart Van Assche     int dlen = alen - 2;
1411*44704f69SBart Van Assche     int k, j, g, f;
1412*44704f69SBart Van Assche     const uint8_t * tp;
1413*44704f69SBart Van Assche     char c[40];
1414*44704f69SBart Van Assche 
1415*44704f69SBart Van Assche     if (NULL == lip)
1416*44704f69SBart Van Assche         lip = "";
1417*44704f69SBart Van Assche     n += sg_scnpr(b + n, blen - n, "%s   Not all referrals: %d\n", lip,
1418*44704f69SBart Van Assche                   !!(dp[2] & 0x1));
1419*44704f69SBart Van Assche     dp += 4;
1420*44704f69SBart Van Assche     for (k = 0, f = 1; (k + 4) < dlen; k += g, dp += g, ++f) {
1421*44704f69SBart Van Assche         int ntpgd = dp[3];
1422*44704f69SBart Van Assche         uint64_t ull;
1423*44704f69SBart Van Assche 
1424*44704f69SBart Van Assche         g = (ntpgd * 4) + 20;
1425*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s    Descriptor %d\n", lip, f);
1426*44704f69SBart Van Assche         if ((k + g) > dlen) {
1427*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s      truncated descriptor, "
1428*44704f69SBart Van Assche                           "stop\n", lip);
1429*44704f69SBart Van Assche             return n;
1430*44704f69SBart Van Assche         }
1431*44704f69SBart Van Assche         ull = sg_get_unaligned_be64(dp + 4);
1432*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      first uds LBA: 0x%" PRIx64
1433*44704f69SBart Van Assche                       "\n", lip, ull);
1434*44704f69SBart Van Assche         ull = sg_get_unaligned_be64(dp + 12);
1435*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s      last uds LBA:  0x%" PRIx64
1436*44704f69SBart Van Assche                       "\n", lip, ull);
1437*44704f69SBart Van Assche         for (j = 0; j < ntpgd; ++j) {
1438*44704f69SBart Van Assche             tp = dp + 20 + (j * 4);
1439*44704f69SBart Van Assche             decode_tpgs_state(tp[0] & 0xf, c, sizeof(c));
1440*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s        tpg: %d  state: %s\n",
1441*44704f69SBart Van Assche                           lip, sg_get_unaligned_be16(tp + 2), c);
1442*44704f69SBart Van Assche         }
1443*44704f69SBart Van Assche     }
1444*44704f69SBart Van Assche     return n;
1445*44704f69SBart Van Assche }
1446*44704f69SBart Van Assche 
1447*44704f69SBart Van Assche static const char * dd_usage_reason_str_arr[] = {
1448*44704f69SBart Van Assche     "Unknown",
1449*44704f69SBart Van Assche     "resend this and further commands to:",
1450*44704f69SBart Van Assche     "resend this command to:",
1451*44704f69SBart Van Assche     "new subsidiary lu added to this administrative lu:",
1452*44704f69SBart Van Assche     "administrative lu associated with a preferred binding:",
1453*44704f69SBart Van Assche    };
1454*44704f69SBart Van Assche 
1455*44704f69SBart Van Assche 
1456*44704f69SBart Van Assche /* Decode descriptor format sense descriptors (assumes sense buffer is
1457*44704f69SBart Van Assche  * in descriptor format). 'leadin' is string prepended to each line written
1458*44704f69SBart Van Assche  * to 'b', NULL treated as "". Returns the number of bytes written to 'b'
1459*44704f69SBart Van Assche  * excluding the trailing '\0'. If problem, returns 0. */
1460*44704f69SBart Van Assche int
sg_get_sense_descriptors_str(const char * lip,const uint8_t * sbp,int sb_len,int blen,char * b)1461*44704f69SBart Van Assche sg_get_sense_descriptors_str(const char * lip, const uint8_t * sbp,
1462*44704f69SBart Van Assche                              int sb_len, int blen, char * b)
1463*44704f69SBart Van Assche {
1464*44704f69SBart Van Assche     int add_sb_len, desc_len, k, j, sense_key;
1465*44704f69SBart Van Assche     int n, progress, pr, rem;
1466*44704f69SBart Van Assche     uint16_t sct_sc;
1467*44704f69SBart Van Assche     bool processed;
1468*44704f69SBart Van Assche     const uint8_t * descp;
1469*44704f69SBart Van Assche     char z[64];
1470*44704f69SBart Van Assche     static const char * dtsp = "   >> descriptor too short";
1471*44704f69SBart Van Assche     static const char * eccp = "Extended copy command";
1472*44704f69SBart Van Assche     static const char * ddp = "destination device";
1473*44704f69SBart Van Assche 
1474*44704f69SBart Van Assche     if ((NULL == b) || (blen <= 0))
1475*44704f69SBart Van Assche         return 0;
1476*44704f69SBart Van Assche     b[0] = '\0';
1477*44704f69SBart Van Assche     if (lip)
1478*44704f69SBart Van Assche         sg_scnpr(z, sizeof(z), "%.60s  ", lip);
1479*44704f69SBart Van Assche     else
1480*44704f69SBart Van Assche         sg_scnpr(z, sizeof(z), "  ");
1481*44704f69SBart Van Assche     if ((sb_len < 8) || (0 == (add_sb_len = sbp[7])))
1482*44704f69SBart Van Assche         return 0;
1483*44704f69SBart Van Assche     add_sb_len = (add_sb_len < (sb_len - 8)) ? add_sb_len : (sb_len - 8);
1484*44704f69SBart Van Assche     sense_key = (sbp[1] & 0xf);
1485*44704f69SBart Van Assche 
1486*44704f69SBart Van Assche     for (descp = (sbp + 8), k = 0, n = 0;
1487*44704f69SBart Van Assche          (k < add_sb_len) && (n < blen);
1488*44704f69SBart Van Assche          k += desc_len, descp += desc_len) {
1489*44704f69SBart Van Assche         int add_d_len = (k < (add_sb_len - 1)) ? descp[1] : -1;
1490*44704f69SBart Van Assche 
1491*44704f69SBart Van Assche         if ((k + add_d_len + 2) > add_sb_len)
1492*44704f69SBart Van Assche             add_d_len = add_sb_len - k - 2;
1493*44704f69SBart Van Assche         desc_len = add_d_len + 2;
1494*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s  Descriptor type: ", lip);
1495*44704f69SBart Van Assche         processed = true;
1496*44704f69SBart Van Assche         switch (descp[0]) {
1497*44704f69SBart Van Assche         case 0:
1498*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Information: ");
1499*44704f69SBart Van Assche             if (add_d_len >= 10) {
1500*44704f69SBart Van Assche                 if (! (0x80 & descp[2]))
1501*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "Valid=0 (-> vendor "
1502*44704f69SBart Van Assche                                   "specific) ");
1503*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "0x");
1504*44704f69SBart Van Assche                 for (j = 0; j < 8; ++j)
1505*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", descp[4 + j]);
1506*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "\n");
1507*44704f69SBart Van Assche             } else {
1508*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1509*44704f69SBart Van Assche                 processed = false;
1510*44704f69SBart Van Assche             }
1511*44704f69SBart Van Assche             break;
1512*44704f69SBart Van Assche         case 1:
1513*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Command specific: ");
1514*44704f69SBart Van Assche             if (add_d_len >= 10) {
1515*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "0x");
1516*44704f69SBart Van Assche                 for (j = 0; j < 8; ++j)
1517*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", descp[4 + j]);
1518*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "\n");
1519*44704f69SBart Van Assche             } else {
1520*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1521*44704f69SBart Van Assche                 processed = false;
1522*44704f69SBart Van Assche             }
1523*44704f69SBart Van Assche             break;
1524*44704f69SBart Van Assche         case 2:         /* Sense Key Specific */
1525*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Sense key specific: ");
1526*44704f69SBart Van Assche             n += decode_sks(lip, descp, add_d_len, sense_key, &processed,
1527*44704f69SBart Van Assche                             blen - n, b + n);
1528*44704f69SBart Van Assche             break;
1529*44704f69SBart Van Assche         case 3:
1530*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Field replaceable unit code: ");
1531*44704f69SBart Van Assche             if (add_d_len >= 2)
1532*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "0x%x\n", descp[3]);
1533*44704f69SBart Van Assche             else {
1534*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1535*44704f69SBart Van Assche                 processed = false;
1536*44704f69SBart Van Assche             }
1537*44704f69SBart Van Assche             break;
1538*44704f69SBart Van Assche         case 4:
1539*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Stream commands: ");
1540*44704f69SBart Van Assche             if (add_d_len >= 2) {
1541*44704f69SBart Van Assche                 if (descp[3] & 0x80)
1542*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "FILEMARK");
1543*44704f69SBart Van Assche                 if (descp[3] & 0x40)
1544*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "End Of Medium (EOM)");
1545*44704f69SBart Van Assche                 if (descp[3] & 0x20)
1546*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "Incorrect Length "
1547*44704f69SBart Van Assche                                   "Indicator (ILI)");
1548*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "\n");
1549*44704f69SBart Van Assche             } else {
1550*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1551*44704f69SBart Van Assche                 processed = false;
1552*44704f69SBart Van Assche             }
1553*44704f69SBart Van Assche             break;
1554*44704f69SBart Van Assche         case 5:
1555*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Block commands: ");
1556*44704f69SBart Van Assche             if (add_d_len >= 2)
1557*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "Incorrect Length Indicator "
1558*44704f69SBart Van Assche                               "(ILI) %s\n",
1559*44704f69SBart Van Assche                               (descp[3] & 0x20) ? "set" : "clear");
1560*44704f69SBart Van Assche             else {
1561*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1562*44704f69SBart Van Assche                 processed = false;
1563*44704f69SBart Van Assche             }
1564*44704f69SBart Van Assche             break;
1565*44704f69SBart Van Assche         case 6:
1566*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "OSD object identification\n");
1567*44704f69SBart Van Assche             processed = false;
1568*44704f69SBart Van Assche             break;
1569*44704f69SBart Van Assche         case 7:
1570*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "OSD response integrity check "
1571*44704f69SBart Van Assche                           "value\n");
1572*44704f69SBart Van Assche             processed = false;
1573*44704f69SBart Van Assche             break;
1574*44704f69SBart Van Assche         case 8:
1575*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "OSD attribute identification\n");
1576*44704f69SBart Van Assche             processed = false;
1577*44704f69SBart Van Assche             break;
1578*44704f69SBart Van Assche         case 9:         /* this is defined in SAT (SAT-2) */
1579*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "ATA Status Return: ");
1580*44704f69SBart Van Assche             if (add_d_len >= 12) {
1581*44704f69SBart Van Assche                 int extend, count;
1582*44704f69SBart Van Assche 
1583*44704f69SBart Van Assche                 extend = descp[2] & 1;
1584*44704f69SBart Van Assche                 count = descp[5] + (extend ? (descp[4] << 8) : 0);
1585*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "extend=%d error=0x%x \n%s"
1586*44704f69SBart Van Assche                               "        count=0x%x ", extend, descp[3], lip,
1587*44704f69SBart Van Assche                               count);
1588*44704f69SBart Van Assche                 if (extend)
1589*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n,
1590*44704f69SBart Van Assche                                   "lba=0x%02x%02x%02x%02x%02x%02x ",
1591*44704f69SBart Van Assche                                    descp[10], descp[8], descp[6], descp[11],
1592*44704f69SBart Van Assche                                    descp[9], descp[7]);
1593*44704f69SBart Van Assche                 else
1594*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "lba=0x%02x%02x%02x ",
1595*44704f69SBart Van Assche                                   descp[11], descp[9], descp[7]);
1596*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "device=0x%x status=0x%x\n",
1597*44704f69SBart Van Assche                               descp[12], descp[13]);
1598*44704f69SBart Van Assche             } else {
1599*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1600*44704f69SBart Van Assche                 processed = false;
1601*44704f69SBart Van Assche             }
1602*44704f69SBart Van Assche             break;
1603*44704f69SBart Van Assche         case 0xa:
1604*44704f69SBart Van Assche            /* Added in SPC-4 rev 17, became 'Another ...' in rev 34 */
1605*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Another progress indication: ");
1606*44704f69SBart Van Assche             if (add_d_len < 6) {
1607*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1608*44704f69SBart Van Assche                 processed = false;
1609*44704f69SBart Van Assche                 break;
1610*44704f69SBart Van Assche             }
1611*44704f69SBart Van Assche             progress = sg_get_unaligned_be16(descp + 6);
1612*44704f69SBart Van Assche             pr = (progress * 100) / 65536;
1613*44704f69SBart Van Assche             rem = ((progress * 100) % 65536) / 656;
1614*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%d.02%d%%\n", pr, rem);
1615*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s        [sense_key=0x%x "
1616*44704f69SBart Van Assche                           "asc,ascq=0x%x,0x%x]\n", lip, descp[2], descp[3],
1617*44704f69SBart Van Assche                           descp[4]);
1618*44704f69SBart Van Assche             break;
1619*44704f69SBart Van Assche         case 0xb:       /* Added in SPC-4 rev 23, defined in SBC-3 rev 22 */
1620*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "User data segment referral: ");
1621*44704f69SBart Van Assche             if (add_d_len < 2) {
1622*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1623*44704f69SBart Van Assche                 processed = false;
1624*44704f69SBart Van Assche                 break;
1625*44704f69SBart Van Assche             }
1626*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "\n");
1627*44704f69SBart Van Assche             n += uds_referral_descriptor_str(b + n, blen - n, descp,
1628*44704f69SBart Van Assche                                              add_d_len, lip);
1629*44704f69SBart Van Assche             break;
1630*44704f69SBart Van Assche         case 0xc:       /* Added in SPC-4 rev 28 */
1631*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Forwarded sense data\n");
1632*44704f69SBart Van Assche             if (add_d_len < 2) {
1633*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1634*44704f69SBart Van Assche                 processed = false;
1635*44704f69SBart Van Assche                 break;
1636*44704f69SBart Van Assche             }
1637*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s    FSDT: %s\n", lip,
1638*44704f69SBart Van Assche                           (descp[2] & 0x80) ? "set" : "clear");
1639*44704f69SBart Van Assche             j = descp[2] & 0xf;
1640*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s    Sense data source: ", lip);
1641*44704f69SBart Van Assche             switch (j) {
1642*44704f69SBart Van Assche             case 0:
1643*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s source device\n", eccp);
1644*44704f69SBart Van Assche                 break;
1645*44704f69SBart Van Assche             case 1:
1646*44704f69SBart Van Assche             case 2:
1647*44704f69SBart Van Assche             case 3:
1648*44704f69SBart Van Assche             case 4:
1649*44704f69SBart Van Assche             case 5:
1650*44704f69SBart Van Assche             case 6:
1651*44704f69SBart Van Assche             case 7:
1652*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s %s %d\n", eccp, ddp, j - 1);
1653*44704f69SBart Van Assche                 break;
1654*44704f69SBart Van Assche             default:
1655*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "unknown [%d]\n", j);
1656*44704f69SBart Van Assche             }
1657*44704f69SBart Van Assche             {
1658*44704f69SBart Van Assche                 char c[480];
1659*44704f69SBart Van Assche 
1660*44704f69SBart Van Assche                 sg_get_scsi_status_str(descp[3], sizeof(c) - 1, c);
1661*44704f69SBart Van Assche                 c[sizeof(c) - 1] = '\0';
1662*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s    Forwarded status: %s\n",
1663*44704f69SBart Van Assche                               lip, c);
1664*44704f69SBart Van Assche                 if (add_d_len > 2) {
1665*44704f69SBart Van Assche                     /* recursing; hope not to get carried away */
1666*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%s vvvvvvvvvvvvvvvv\n",
1667*44704f69SBart Van Assche                                   lip);
1668*44704f69SBart Van Assche                     sg_get_sense_str(lip, descp + 4, add_d_len - 2, false,
1669*44704f69SBart Van Assche                                      sizeof(c), c);
1670*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%s", c);
1671*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%s ^^^^^^^^^^^^^^^^\n",
1672*44704f69SBart Van Assche                                   lip);
1673*44704f69SBart Van Assche                 }
1674*44704f69SBart Van Assche             }
1675*44704f69SBart Van Assche             break;
1676*44704f69SBart Van Assche         case 0xd:       /* Added in SBC-3 rev 36d */
1677*44704f69SBart Van Assche             /* this descriptor combines descriptors 0, 1, 2 and 3 */
1678*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Direct-access block device\n");
1679*44704f69SBart Van Assche             if (add_d_len < 28) {
1680*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1681*44704f69SBart Van Assche                 processed = false;
1682*44704f69SBart Van Assche                 break;
1683*44704f69SBart Van Assche             }
1684*44704f69SBart Van Assche             if (0x20 & descp[2])
1685*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s    ILI (incorrect length "
1686*44704f69SBart Van Assche                               "indication) set\n", lip);
1687*44704f69SBart Van Assche             if (0x80 & descp[4]) {
1688*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s    Sense key specific: ",
1689*44704f69SBart Van Assche                               lip);
1690*44704f69SBart Van Assche                 n += decode_sks(lip, descp, add_d_len, sense_key, &processed,
1691*44704f69SBart Van Assche                                 blen - n, b + n);
1692*44704f69SBart Van Assche             }
1693*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s    Field replaceable unit "
1694*44704f69SBart Van Assche                           "code: 0x%x\n", lip, descp[7]);
1695*44704f69SBart Van Assche             if (0x80 & descp[2]) {
1696*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s    Information: 0x", lip);
1697*44704f69SBart Van Assche                 for (j = 0; j < 8; ++j)
1698*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", descp[8 + j]);
1699*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "\n");
1700*44704f69SBart Van Assche             }
1701*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s    Command specific: 0x", lip);
1702*44704f69SBart Van Assche             for (j = 0; j < 8; ++j)
1703*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%02x", descp[16 + j]);
1704*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "\n");
1705*44704f69SBart Van Assche             break;
1706*44704f69SBart Van Assche         case 0xe:       /* Added in SPC-5 rev 6 (for Bind/Unbind) */
1707*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Device designation\n");
1708*44704f69SBart Van Assche             j = (int)SG_ARRAY_SIZE(dd_usage_reason_str_arr);
1709*44704f69SBart Van Assche             if (descp[3] < j)
1710*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s    Usage reason: %s\n",
1711*44704f69SBart Van Assche                               lip, dd_usage_reason_str_arr[descp[3]]);
1712*44704f69SBart Van Assche             else
1713*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s    Usage reason: "
1714*44704f69SBart Van Assche                               "reserved[%d]\n", lip, descp[3]);
1715*44704f69SBart Van Assche             n += sg_get_designation_descriptor_str(z, descp + 4, descp[1] - 2,
1716*44704f69SBart Van Assche                                                    true, false, blen - n,
1717*44704f69SBart Van Assche                                                    b + n);
1718*44704f69SBart Van Assche             break;
1719*44704f69SBart Van Assche         case 0xf:       /* Added in SPC-5 rev 10 (for Write buffer) */
1720*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "Microcode activation ");
1721*44704f69SBart Van Assche             if (add_d_len < 6) {
1722*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1723*44704f69SBart Van Assche                 processed = false;
1724*44704f69SBart Van Assche                 break;
1725*44704f69SBart Van Assche             }
1726*44704f69SBart Van Assche             progress = sg_get_unaligned_be16(descp + 6);
1727*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "time: ");
1728*44704f69SBart Van Assche             if (0 == progress)
1729*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "unknown\n");
1730*44704f69SBart Van Assche             else
1731*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%d seconds\n", progress);
1732*44704f69SBart Van Assche             break;
1733*44704f69SBart Van Assche         case 0xde:       /* NVME Status Field; vendor (sg3_utils) specific */
1734*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "NVMe Status: ");
1735*44704f69SBart Van Assche             if (add_d_len < 6) {
1736*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s\n", dtsp);
1737*44704f69SBart Van Assche                 processed = false;
1738*44704f69SBart Van Assche                 break;
1739*44704f69SBart Van Assche             }
1740*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "DNR=%d, M=%d, ",
1741*44704f69SBart Van Assche                           (int)!!(0x80 & descp[5]), (int)!!(0x40 & descp[5]));
1742*44704f69SBart Van Assche             sct_sc = sg_get_unaligned_be16(descp + 6);
1743*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "SCT_SC=0x%x\n", sct_sc);
1744*44704f69SBart Van Assche             if (sct_sc > 0) {
1745*44704f69SBart Van Assche                 char d[80];
1746*44704f69SBart Van Assche 
1747*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "    %s\n",
1748*44704f69SBart Van Assche                         sg_get_nvme_cmd_status_str(sct_sc, sizeof(d), d));
1749*44704f69SBart Van Assche             }
1750*44704f69SBart Van Assche             break;
1751*44704f69SBart Van Assche         default:
1752*44704f69SBart Van Assche             if (descp[0] >= 0x80)
1753*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "Vendor specific [0x%x]\n",
1754*44704f69SBart Van Assche                               descp[0]);
1755*44704f69SBart Van Assche             else
1756*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "Unknown [0x%x]\n", descp[0]);
1757*44704f69SBart Van Assche             processed = false;
1758*44704f69SBart Van Assche             break;
1759*44704f69SBart Van Assche         }
1760*44704f69SBart Van Assche         if (! processed) {
1761*44704f69SBart Van Assche             if (add_d_len > 0) {
1762*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%s    ", lip);
1763*44704f69SBart Van Assche                 for (j = 0; j < add_d_len; ++j) {
1764*44704f69SBart Van Assche                     if ((j > 0) && (0 == (j % 24)))
1765*44704f69SBart Van Assche                         n += sg_scnpr(b + n, blen - n, "\n%s    ", lip);
1766*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x ", descp[j + 2]);
1767*44704f69SBart Van Assche                 }
1768*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "\n");
1769*44704f69SBart Van Assche             }
1770*44704f69SBart Van Assche         }
1771*44704f69SBart Van Assche         if (add_d_len < 0)
1772*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "%s    short descriptor\n", lip);
1773*44704f69SBart Van Assche     }
1774*44704f69SBart Van Assche     return n;
1775*44704f69SBart Van Assche }
1776*44704f69SBart Van Assche 
1777*44704f69SBart Van Assche /* Decode SAT ATA PASS-THROUGH fixed format sense. Shows "+" after 'count'
1778*44704f69SBart Van Assche  * and/or 'lba' values to indicate that not all data in those fields is shown.
1779*44704f69SBart Van Assche  * That extra field information may be available in the ATA pass-through
1780*44704f69SBart Van Assche  * results log page parameter with the corresponding 'log_index'. */
1781*44704f69SBart Van Assche static int
sg_get_sense_sat_pt_fixed_str(const char * lip,const uint8_t * sp,int slen,int blen,char * b)1782*44704f69SBart Van Assche sg_get_sense_sat_pt_fixed_str(const char * lip, const uint8_t * sp,
1783*44704f69SBart Van Assche                               int slen, int blen, char * b)
1784*44704f69SBart Van Assche {
1785*44704f69SBart Van Assche     int n = 0;
1786*44704f69SBart Van Assche     bool extend, count_upper_nz, lba_upper_nz;
1787*44704f69SBart Van Assche 
1788*44704f69SBart Van Assche     if ((blen < 1) || (slen < 12))
1789*44704f69SBart Van Assche         return n;
1790*44704f69SBart Van Assche     if (NULL == lip)
1791*44704f69SBart Van Assche         lip = "";
1792*44704f69SBart Van Assche     if (SPC_SK_RECOVERED_ERROR != (0xf & sp[2]))
1793*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "%s  >> expected Sense key: Recovered "
1794*44704f69SBart Van Assche                       "Error ??\n", lip);
1795*44704f69SBart Van Assche     /* Fixed sense command-specific information field starts at sp + 8 */
1796*44704f69SBart Van Assche     extend = !!(0x80 & sp[8]);
1797*44704f69SBart Van Assche     count_upper_nz = !!(0x40 & sp[8]);
1798*44704f69SBart Van Assche     lba_upper_nz = !!(0x20 & sp[8]);
1799*44704f69SBart Van Assche     /* Fixed sense information field starts at sp + 3 */
1800*44704f69SBart Van Assche     n += sg_scnpr(b + n, blen - n, "%s  error=0x%x, status=0x%x, "
1801*44704f69SBart Van Assche                   "device=0x%x, count(7:0)=0x%x%c\n", lip, sp[3], sp[4],
1802*44704f69SBart Van Assche                   sp[5], sp[6], (count_upper_nz ? '+' : ' '));
1803*44704f69SBart Van Assche     n += sg_scnpr(b + n, blen - n, "%s  extend=%d, log_index=0x%x, "
1804*44704f69SBart Van Assche                   "lba_high,mid,low(7:0)=0x%x,0x%x,0x%x%c\n", lip,
1805*44704f69SBart Van Assche                   (int)extend, (0xf & sp[8]), sp[11], sp[10], sp[9],
1806*44704f69SBart Van Assche                   (lba_upper_nz ? '+' : ' '));
1807*44704f69SBart Van Assche     return n;
1808*44704f69SBart Van Assche }
1809*44704f69SBart Van Assche 
1810*44704f69SBart Van Assche /* Fetch sense information */
1811*44704f69SBart Van Assche int
sg_get_sense_str(const char * lip,const uint8_t * sbp,int sb_len,bool raw_sinfo,int cblen,char * cbp)1812*44704f69SBart Van Assche sg_get_sense_str(const char * lip, const uint8_t * sbp, int sb_len,
1813*44704f69SBart Van Assche                  bool raw_sinfo, int cblen, char * cbp)
1814*44704f69SBart Van Assche {
1815*44704f69SBart Van Assche     bool descriptor_format = false;
1816*44704f69SBart Van Assche     bool sdat_ovfl = false;
1817*44704f69SBart Van Assche     bool valid_info_fld;
1818*44704f69SBart Van Assche     int len, progress, n, r, pr, rem, blen;
1819*44704f69SBart Van Assche     unsigned int info;
1820*44704f69SBart Van Assche     uint8_t resp_code;
1821*44704f69SBart Van Assche     const char * ebp = NULL;
1822*44704f69SBart Van Assche     char ebuff[64];
1823*44704f69SBart Van Assche     char b[256];
1824*44704f69SBart Van Assche     struct sg_scsi_sense_hdr ssh;
1825*44704f69SBart Van Assche 
1826*44704f69SBart Van Assche     if ((NULL == cbp) || (cblen <= 0))
1827*44704f69SBart Van Assche         return 0;
1828*44704f69SBart Van Assche     else if (1 == cblen) {
1829*44704f69SBart Van Assche         cbp[0] = '\0';
1830*44704f69SBart Van Assche         return 0;
1831*44704f69SBart Van Assche     }
1832*44704f69SBart Van Assche     blen = sizeof(b);
1833*44704f69SBart Van Assche     n = 0;
1834*44704f69SBart Van Assche     if (NULL == lip)
1835*44704f69SBart Van Assche         lip = "";
1836*44704f69SBart Van Assche     if ((NULL == sbp) || (sb_len < 1)) {
1837*44704f69SBart Van Assche         n += sg_scnpr(cbp, cblen, "%s >>> sense buffer empty\n", lip);
1838*44704f69SBart Van Assche         return n;
1839*44704f69SBart Van Assche     }
1840*44704f69SBart Van Assche     resp_code = 0x7f & sbp[0];
1841*44704f69SBart Van Assche     valid_info_fld = !!(sbp[0] & 0x80);
1842*44704f69SBart Van Assche     len = sb_len;
1843*44704f69SBart Van Assche     if (sg_scsi_normalize_sense(sbp, sb_len, &ssh)) {
1844*44704f69SBart Van Assche         switch (ssh.response_code) {
1845*44704f69SBart Van Assche         case 0x70:      /* fixed, current */
1846*44704f69SBart Van Assche             ebp = "Fixed format, current";
1847*44704f69SBart Van Assche             len = (sb_len > 7) ? (sbp[7] + 8) : sb_len;
1848*44704f69SBart Van Assche             len = (len > sb_len) ? sb_len : len;
1849*44704f69SBart Van Assche             sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false;
1850*44704f69SBart Van Assche             break;
1851*44704f69SBart Van Assche         case 0x71:      /* fixed, deferred */
1852*44704f69SBart Van Assche             /* error related to a previous command */
1853*44704f69SBart Van Assche             ebp = "Fixed format, <<<deferred>>>";
1854*44704f69SBart Van Assche             len = (sb_len > 7) ? (sbp[7] + 8) : sb_len;
1855*44704f69SBart Van Assche             len = (len > sb_len) ? sb_len : len;
1856*44704f69SBart Van Assche             sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false;
1857*44704f69SBart Van Assche             break;
1858*44704f69SBart Van Assche         case 0x72:      /* descriptor, current */
1859*44704f69SBart Van Assche             descriptor_format = true;
1860*44704f69SBart Van Assche             ebp = "Descriptor format, current";
1861*44704f69SBart Van Assche             sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false;
1862*44704f69SBart Van Assche             break;
1863*44704f69SBart Van Assche         case 0x73:      /* descriptor, deferred */
1864*44704f69SBart Van Assche             descriptor_format = true;
1865*44704f69SBart Van Assche             ebp = "Descriptor format, <<<deferred>>>";
1866*44704f69SBart Van Assche             sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false;
1867*44704f69SBart Van Assche             break;
1868*44704f69SBart Van Assche         case 0x0:
1869*44704f69SBart Van Assche             ebp = "Response code: 0x0 (?)";
1870*44704f69SBart Van Assche             break;
1871*44704f69SBart Van Assche         default:
1872*44704f69SBart Van Assche             sg_scnpr(ebuff, sizeof(ebuff), "Unknown response code: 0x%x",
1873*44704f69SBart Van Assche                      ssh.response_code);
1874*44704f69SBart Van Assche             ebp = ebuff;
1875*44704f69SBart Van Assche             break;
1876*44704f69SBart Van Assche         }
1877*44704f69SBart Van Assche         n += sg_scnpr(cbp + n, cblen - n, "%s%s; Sense key: %s\n", lip, ebp,
1878*44704f69SBart Van Assche                       sg_lib_sense_key_desc[ssh.sense_key]);
1879*44704f69SBart Van Assche         if (sdat_ovfl)
1880*44704f69SBart Van Assche             n += sg_scnpr(cbp + n, cblen - n, "%s<<<Sense data overflow "
1881*44704f69SBart Van Assche                           "(SDAT_OVFL)>>>\n", lip);
1882*44704f69SBart Van Assche         if (descriptor_format) {
1883*44704f69SBart Van Assche             n += sg_scnpr(cbp + n, cblen - n, "%s%s\n", lip,
1884*44704f69SBart Van Assche                           sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b));
1885*44704f69SBart Van Assche             n += sg_get_sense_descriptors_str(lip, sbp, len,
1886*44704f69SBart Van Assche                                               cblen - n, cbp + n);
1887*44704f69SBart Van Assche         } else if ((len > 12) && (0 == ssh.asc) &&
1888*44704f69SBart Van Assche                    (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
1889*44704f69SBart Van Assche             /* SAT ATA PASS-THROUGH fixed format */
1890*44704f69SBart Van Assche             n += sg_scnpr(cbp + n, cblen - n, "%s%s\n", lip,
1891*44704f69SBart Van Assche                           sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b));
1892*44704f69SBart Van Assche             n += sg_get_sense_sat_pt_fixed_str(lip, sbp, len,
1893*44704f69SBart Van Assche                                                cblen - n, cbp + n);
1894*44704f69SBart Van Assche         } else if (len > 2) {   /* fixed format */
1895*44704f69SBart Van Assche             if (len > 12)
1896*44704f69SBart Van Assche                 n += sg_scnpr(cbp + n, cblen - n, "%s%s\n", lip,
1897*44704f69SBart Van Assche                          sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b));
1898*44704f69SBart Van Assche             r = 0;
1899*44704f69SBart Van Assche             if (strlen(lip) > 0)
1900*44704f69SBart Van Assche                 r += sg_scnpr(b + r, blen - r, "%s", lip);
1901*44704f69SBart Van Assche             if (len > 6) {
1902*44704f69SBart Van Assche                 info = sg_get_unaligned_be32(sbp + 3);
1903*44704f69SBart Van Assche                 if (valid_info_fld)
1904*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "  Info fld=0x%x [%u] ",
1905*44704f69SBart Van Assche                                   info, info);
1906*44704f69SBart Van Assche                 else if (info > 0)
1907*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "  Valid=0, Info fld=0x%x "
1908*44704f69SBart Van Assche                                   "[%u] ", info, info);
1909*44704f69SBart Van Assche             } else
1910*44704f69SBart Van Assche                 info = 0;
1911*44704f69SBart Van Assche             if (sbp[2] & 0xe0) {
1912*44704f69SBart Van Assche                 if (sbp[2] & 0x80)
1913*44704f69SBart Van Assche                    r += sg_scnpr(b + r, blen - r, " FMK");
1914*44704f69SBart Van Assche                             /* current command has read a filemark */
1915*44704f69SBart Van Assche                 if (sbp[2] & 0x40)
1916*44704f69SBart Van Assche                    r += sg_scnpr(b + r, blen - r, " EOM");
1917*44704f69SBart Van Assche                             /* end-of-medium condition exists */
1918*44704f69SBart Van Assche                 if (sbp[2] & 0x20)
1919*44704f69SBart Van Assche                    r += sg_scnpr(b + r, blen - r, " ILI");
1920*44704f69SBart Van Assche                             /* incorrect block length requested */
1921*44704f69SBart Van Assche                 r += sg_scnpr(b + r, blen - r, "\n");
1922*44704f69SBart Van Assche             } else if (valid_info_fld || (info > 0))
1923*44704f69SBart Van Assche                 r += sg_scnpr(b + r, blen - r, "\n");
1924*44704f69SBart Van Assche             if ((len >= 14) && sbp[14])
1925*44704f69SBart Van Assche                 r += sg_scnpr(b + r, blen - r, "%s  Field replaceable unit "
1926*44704f69SBart Van Assche                               "code: %d\n", lip, sbp[14]);
1927*44704f69SBart Van Assche             if ((len >= 18) && (sbp[15] & 0x80)) {
1928*44704f69SBart Van Assche                 /* sense key specific decoding */
1929*44704f69SBart Van Assche                 switch (ssh.sense_key) {
1930*44704f69SBart Van Assche                 case SPC_SK_ILLEGAL_REQUEST:
1931*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "%s  Sense Key Specific: "
1932*44704f69SBart Van Assche                                   "Error in %s: byte %d", lip,
1933*44704f69SBart Van Assche                                   ((sbp[15] & 0x40) ?
1934*44704f69SBart Van Assche                                          "Command" : "Data parameters"),
1935*44704f69SBart Van Assche                                   sg_get_unaligned_be16(sbp + 16));
1936*44704f69SBart Van Assche                     if (sbp[15] & 0x08)
1937*44704f69SBart Van Assche                         r += sg_scnpr(b + r, blen - r, " bit %d\n",
1938*44704f69SBart Van Assche                                       sbp[15] & 0x07);
1939*44704f69SBart Van Assche                     else
1940*44704f69SBart Van Assche                         r += sg_scnpr(b + r, blen - r, "\n");
1941*44704f69SBart Van Assche                     break;
1942*44704f69SBart Van Assche                 case SPC_SK_NO_SENSE:
1943*44704f69SBart Van Assche                 case SPC_SK_NOT_READY:
1944*44704f69SBart Van Assche                     progress = sg_get_unaligned_be16(sbp + 16);
1945*44704f69SBart Van Assche                     pr = (progress * 100) / 65536;
1946*44704f69SBart Van Assche                     rem = ((progress * 100) % 65536) / 656;
1947*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "%s  Progress indication: "
1948*44704f69SBart Van Assche                                   "%d.%02d%%\n", lip, pr, rem);
1949*44704f69SBart Van Assche                     break;
1950*44704f69SBart Van Assche                 case SPC_SK_HARDWARE_ERROR:
1951*44704f69SBart Van Assche                 case SPC_SK_MEDIUM_ERROR:
1952*44704f69SBart Van Assche                 case SPC_SK_RECOVERED_ERROR:
1953*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "%s  Actual retry count: "
1954*44704f69SBart Van Assche                                   "0x%02x%02x\n", lip, sbp[16], sbp[17]);
1955*44704f69SBart Van Assche                     break;
1956*44704f69SBart Van Assche                 case SPC_SK_COPY_ABORTED:
1957*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "%s  Segment pointer: ",
1958*44704f69SBart Van Assche                                   lip);
1959*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "Relative to start of %s, "
1960*44704f69SBart Van Assche                                   "byte %d", ((sbp[15] & 0x20) ?
1961*44704f69SBart Van Assche                                      "segment descriptor" : "parameter list"),
1962*44704f69SBart Van Assche                                   sg_get_unaligned_be16(sbp + 16));
1963*44704f69SBart Van Assche                     if (sbp[15] & 0x08)
1964*44704f69SBart Van Assche                         r += sg_scnpr(b + r, blen - r, " bit %d\n",
1965*44704f69SBart Van Assche                                       sbp[15] & 0x07);
1966*44704f69SBart Van Assche                     else
1967*44704f69SBart Van Assche                         r += sg_scnpr(b + r, blen - r, "\n");
1968*44704f69SBart Van Assche                     break;
1969*44704f69SBart Van Assche                 case SPC_SK_UNIT_ATTENTION:
1970*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "%s  Unit attention "
1971*44704f69SBart Van Assche                                   "condition queue: ", lip);
1972*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "overflow flag is %d\n",
1973*44704f69SBart Van Assche                                   !!(sbp[15] & 0x1));
1974*44704f69SBart Van Assche                     break;
1975*44704f69SBart Van Assche                 default:
1976*44704f69SBart Van Assche                     r += sg_scnpr(b + r, blen - r, "%s  Sense_key: 0x%x "
1977*44704f69SBart Van Assche                                   "unexpected\n", lip, ssh.sense_key);
1978*44704f69SBart Van Assche                     break;
1979*44704f69SBart Van Assche                 }
1980*44704f69SBart Van Assche             }
1981*44704f69SBart Van Assche             if (r > 0)
1982*44704f69SBart Van Assche                 n += sg_scnpr(cbp + n, cblen - n, "%s", b);
1983*44704f69SBart Van Assche         } else
1984*44704f69SBart Van Assche             n += sg_scnpr(cbp + n, cblen - n, "%s fixed descriptor length "
1985*44704f69SBart Van Assche                           "too short, len=%d\n", lip, len);
1986*44704f69SBart Van Assche     } else {    /* unable to normalise sense buffer, something irregular */
1987*44704f69SBart Van Assche         if (sb_len < 4) {       /* Too short */
1988*44704f69SBart Van Assche             n += sg_scnpr(cbp + n, cblen - n, "%ssense buffer too short (4 "
1989*44704f69SBart Van Assche                           "byte minimum)\n", lip);
1990*44704f69SBart Van Assche             goto check_raw;
1991*44704f69SBart Van Assche         }
1992*44704f69SBart Van Assche         if (0x7f == resp_code) {        /* Vendor specific */
1993*44704f69SBart Van Assche             n += sg_scnpr(cbp + n, cblen - n, "%sVendor specific sense "
1994*44704f69SBart Van Assche                           "buffer, in hex:\n", lip);
1995*44704f69SBart Van Assche             n += hex2str(sbp, sb_len, lip, -1, cblen - n, cbp + n);
1996*44704f69SBart Van Assche             return n;   /* no need to check raw, just output in hex */
1997*44704f69SBart Van Assche         }
1998*44704f69SBart Van Assche         /* non-extended SCSI-1 sense data ?? */
1999*44704f69SBart Van Assche         r = 0;
2000*44704f69SBart Van Assche         if (strlen(lip) > 0)
2001*44704f69SBart Van Assche             r += sg_scnpr(b + r, blen - r, "%s", lip);
2002*44704f69SBart Van Assche         r += sg_scnpr(b + r, blen - r, "Probably uninitialized data.\n%s  "
2003*44704f69SBart Van Assche                       "Try to view as SCSI-1 non-extended sense:\n", lip);
2004*44704f69SBart Van Assche         r += sg_scnpr(b + r, blen - r, "  AdValid=%d  Error class=%d  Error "
2005*44704f69SBart Van Assche                       "code=%d\n", valid_info_fld, ((sbp[0] >> 4) & 0x7),
2006*44704f69SBart Van Assche                       (sbp[0] & 0xf));
2007*44704f69SBart Van Assche         if (valid_info_fld)
2008*44704f69SBart Van Assche             sg_scnpr(b + r, blen - r, "%s  lba=0x%x\n", lip,
2009*44704f69SBart Van Assche                      sg_get_unaligned_be24(sbp + 1) & 0x1fffff);
2010*44704f69SBart Van Assche         n += sg_scnpr(cbp + n, cblen - n, "%s\n", b);
2011*44704f69SBart Van Assche     }
2012*44704f69SBart Van Assche check_raw:
2013*44704f69SBart Van Assche     if (raw_sinfo) {
2014*44704f69SBart Van Assche         int calculated_len;
2015*44704f69SBart Van Assche         char z[64];
2016*44704f69SBart Van Assche 
2017*44704f69SBart Van Assche         n += sg_scnpr(cbp + n, cblen - n, "%s Raw sense data (in hex), "
2018*44704f69SBart Van Assche                       "sb_len=%d", lip, sb_len);
2019*44704f69SBart Van Assche         if (n >= (cblen - 1))
2020*44704f69SBart Van Assche             return n;
2021*44704f69SBart Van Assche         if ((sb_len > 7) && (sbp[0] >= 0x70) && (sbp[0] < 0x74)) {
2022*44704f69SBart Van Assche             calculated_len = sbp[7] + 8;
2023*44704f69SBart Van Assche             n += sg_scnpr(cbp + n, cblen - n, ", calculated_len=%d\n",
2024*44704f69SBart Van Assche                           calculated_len);
2025*44704f69SBart Van Assche         } else {
2026*44704f69SBart Van Assche             calculated_len = sb_len;
2027*44704f69SBart Van Assche             n += sg_scnpr(cbp + n, cblen - n, "\n");
2028*44704f69SBart Van Assche         }
2029*44704f69SBart Van Assche         if (n >= (cblen - 1))
2030*44704f69SBart Van Assche             return n;
2031*44704f69SBart Van Assche 
2032*44704f69SBart Van Assche         sg_scnpr(z, sizeof(z), "%.50s        ", lip);
2033*44704f69SBart Van Assche         n += hex2str(sbp, calculated_len, z,  -1, cblen - n, cbp + n);
2034*44704f69SBart Van Assche     }
2035*44704f69SBart Van Assche     return n;
2036*44704f69SBart Van Assche }
2037*44704f69SBart Van Assche 
2038*44704f69SBart Van Assche /* Print sense information */
2039*44704f69SBart Van Assche void
sg_print_sense(const char * leadin,const uint8_t * sbp,int sb_len,bool raw_sinfo)2040*44704f69SBart Van Assche sg_print_sense(const char * leadin, const uint8_t * sbp, int sb_len,
2041*44704f69SBart Van Assche                bool raw_sinfo)
2042*44704f69SBart Van Assche {
2043*44704f69SBart Van Assche     uint32_t pg_sz = sg_get_page_size();
2044*44704f69SBart Van Assche     char *cp;
2045*44704f69SBart Van Assche     uint8_t *free_cp;
2046*44704f69SBart Van Assche 
2047*44704f69SBart Van Assche     cp = (char *)sg_memalign(pg_sz, pg_sz, &free_cp, false);
2048*44704f69SBart Van Assche     if (NULL == cp)
2049*44704f69SBart Van Assche         return;
2050*44704f69SBart Van Assche     sg_get_sense_str(leadin, sbp, sb_len, raw_sinfo, pg_sz, cp);
2051*44704f69SBart Van Assche     pr2ws("%s", cp);
2052*44704f69SBart Van Assche     free(free_cp);
2053*44704f69SBart Van Assche }
2054*44704f69SBart Van Assche 
2055*44704f69SBart Van Assche /* This examines exit_status and if an error message is known it is output
2056*44704f69SBart Van Assche  * as a string to 'b' and true is returned. If 'longer' is true and extra
2057*44704f69SBart Van Assche  * information is available then it is added to the output. If no error
2058*44704f69SBart Van Assche  * message is available a null character is output and false is returned.
2059*44704f69SBart Van Assche  * If exit_status is zero (no error) and 'longer' is true then the string
2060*44704f69SBart Van Assche  * 'No errors' is output; if 'longer' is false then a null character is
2061*44704f69SBart Van Assche  * output; in both cases true is returned. If exit_status is negative then
2062*44704f69SBart Van Assche  * a null character is output and false is returned. All messages are a
2063*44704f69SBart Van Assche  * single line (less than 80 characters) with no trailing LF. The output
2064*44704f69SBart Van Assche  * string including the trailing null character is no longer than b_len.
2065*44704f69SBart Van Assche  * exit_status represents the Unix exit status available after a utility
2066*44704f69SBart Van Assche  * finishes executing (for whatever reason). */
sg_exit2str(int exit_status,bool longer,int b_len,char * b)2067*44704f69SBart Van Assche bool sg_exit2str(int exit_status, bool longer, int b_len, char *b)
2068*44704f69SBart Van Assche {
2069*44704f69SBart Van Assche     const struct sg_value_2names_t * ess = sg_exit_str_arr;
2070*44704f69SBart Van Assche 
2071*44704f69SBart Van Assche     if ((b_len < 1) || (NULL == b))
2072*44704f69SBart Van Assche         return false;
2073*44704f69SBart Van Assche     /* if there is a valid buffer, initialize it to a valid empty string */
2074*44704f69SBart Van Assche     b[0] = '\0';
2075*44704f69SBart Van Assche     if (exit_status < 0)
2076*44704f69SBart Van Assche         return false;
2077*44704f69SBart Van Assche     else if ((0 == exit_status) || (SG_LIB_OK_FALSE == exit_status)) {
2078*44704f69SBart Van Assche         if (longer)
2079*44704f69SBart Van Assche             goto fini;
2080*44704f69SBart Van Assche         return true;
2081*44704f69SBart Van Assche     }
2082*44704f69SBart Van Assche 
2083*44704f69SBart Van Assche     if ((exit_status > SG_LIB_OS_BASE_ERR) &&       /* 51 to 96 inclusive */
2084*44704f69SBart Van Assche         (exit_status < SG_LIB_CAT_MALFORMED)) {
2085*44704f69SBart Van Assche         snprintf(b, b_len, "%s%s", (longer ? "OS error: " : ""),
2086*44704f69SBart Van Assche                  safe_strerror(exit_status - SG_LIB_OS_BASE_ERR));
2087*44704f69SBart Van Assche         return true;
2088*44704f69SBart Van Assche     } else if ((exit_status > 128) && (exit_status < 255)) {
2089*44704f69SBart Van Assche         snprintf(b, b_len, "Utility stopped/aborted by signal number: %d",
2090*44704f69SBart Van Assche                  exit_status - 128);
2091*44704f69SBart Van Assche         return true;
2092*44704f69SBart Van Assche     }
2093*44704f69SBart Van Assche fini:
2094*44704f69SBart Van Assche     for ( ; ess->name; ++ess) {
2095*44704f69SBart Van Assche         if (exit_status == ess->value)
2096*44704f69SBart Van Assche             break;
2097*44704f69SBart Van Assche     }
2098*44704f69SBart Van Assche     if (ess->name) {
2099*44704f69SBart Van Assche         if (longer && ess->name2)
2100*44704f69SBart Van Assche             snprintf(b, b_len, "%s, %s", ess->name, ess->name2);
2101*44704f69SBart Van Assche         else
2102*44704f69SBart Van Assche             snprintf(b, b_len, "%s", ess->name);
2103*44704f69SBart Van Assche         return true;
2104*44704f69SBart Van Assche     }
2105*44704f69SBart Van Assche     return false;
2106*44704f69SBart Van Assche }
2107*44704f69SBart Van Assche 
2108*44704f69SBart Van Assche static bool
sg_if_can2fp(const char * leadin,int exit_status,FILE * fp)2109*44704f69SBart Van Assche sg_if_can2fp(const char * leadin, int exit_status, FILE * fp)
2110*44704f69SBart Van Assche {
2111*44704f69SBart Van Assche     char b[256];
2112*44704f69SBart Van Assche     const char * s = leadin ? leadin : "";
2113*44704f69SBart Van Assche 
2114*44704f69SBart Van Assche     if ((0 == exit_status) || (SG_LIB_OK_FALSE == exit_status))
2115*44704f69SBart Van Assche         return true;    /* don't print anything */
2116*44704f69SBart Van Assche     else if (sg_exit2str(exit_status, false, sizeof(b), b)) {
2117*44704f69SBart Van Assche         fprintf(fp, "%s%s\n", s, b);
2118*44704f69SBart Van Assche         return true;
2119*44704f69SBart Van Assche     } else
2120*44704f69SBart Van Assche         return false;
2121*44704f69SBart Van Assche }
2122*44704f69SBart Van Assche 
2123*44704f69SBart Van Assche /* This examines exit_status and if an error message is known it is output
2124*44704f69SBart Van Assche  * to stdout/stderr and true is returned. If no error message is
2125*44704f69SBart Van Assche  * available nothing is output and false is returned. If exit_status is
2126*44704f69SBart Van Assche  * zero (no error) nothing is output and true is returned. If exit_status
2127*44704f69SBart Van Assche  * is negative then nothing is output and false is returned. If leadin is
2128*44704f69SBart Van Assche  * non-NULL then it is printed before the error message. All messages are
2129*44704f69SBart Van Assche  * a single line with a trailing LF. */
2130*44704f69SBart Van Assche bool
sg_if_can2stdout(const char * leadin,int exit_status)2131*44704f69SBart Van Assche sg_if_can2stdout(const char * leadin, int exit_status)
2132*44704f69SBart Van Assche {
2133*44704f69SBart Van Assche     return sg_if_can2fp(leadin, exit_status, stdout);
2134*44704f69SBart Van Assche }
2135*44704f69SBart Van Assche 
2136*44704f69SBart Van Assche /* See sg_if_can2stdout() comments */
2137*44704f69SBart Van Assche bool
sg_if_can2stderr(const char * leadin,int exit_status)2138*44704f69SBart Van Assche sg_if_can2stderr(const char * leadin, int exit_status)
2139*44704f69SBart Van Assche {
2140*44704f69SBart Van Assche     return sg_if_can2fp(leadin, exit_status,
2141*44704f69SBart Van Assche                         sg_warnings_strm ? sg_warnings_strm : stderr);
2142*44704f69SBart Van Assche }
2143*44704f69SBart Van Assche 
2144*44704f69SBart Van Assche /* If os_err_num is within bounds then the returned value is 'os_err_num +
2145*44704f69SBart Van Assche  * SG_LIB_OS_BASE_ERR' otherwise SG_LIB_OS_BASE_ERR is returned. If
2146*44704f69SBart Van Assche  * os_err_num is 0 then 0 is returned. */
2147*44704f69SBart Van Assche int
sg_convert_errno(int os_err_num)2148*44704f69SBart Van Assche sg_convert_errno(int os_err_num)
2149*44704f69SBart Van Assche {
2150*44704f69SBart Van Assche     if (os_err_num <= 0) {
2151*44704f69SBart Van Assche         if (os_err_num < 0)
2152*44704f69SBart Van Assche             return SG_LIB_OS_BASE_ERR;
2153*44704f69SBart Van Assche         return os_err_num;      /* os_err_num of 0 maps to 0 */
2154*44704f69SBart Van Assche     }
2155*44704f69SBart Van Assche     if (os_err_num < (SG_LIB_CAT_MALFORMED - SG_LIB_OS_BASE_ERR))
2156*44704f69SBart Van Assche         return SG_LIB_OS_BASE_ERR + os_err_num;
2157*44704f69SBart Van Assche     return SG_LIB_OS_BASE_ERR;
2158*44704f69SBart Van Assche }
2159*44704f69SBart Van Assche 
2160*44704f69SBart Van Assche static const char * const bad_sense_cat = "Bad sense category";
2161*44704f69SBart Van Assche 
2162*44704f69SBart Van Assche /* Yield string associated with sense category. Returns 'b' (or pointer
2163*44704f69SBart Van Assche  * to "Bad sense category" if 'b' is NULL). If sense_cat unknown then
2164*44704f69SBart Van Assche  * yield "Sense category: <sense_cat_val>" string. The original 'sense
2165*44704f69SBart Van Assche  * category' concept has been expanded to most detected errors and is
2166*44704f69SBart Van Assche  * returned by these utilities as their exit status value (an (unsigned)
2167*44704f69SBart Van Assche  * 8 bit value where 0 means good (i.e. no errors)).  Uses sg_exit2str()
2168*44704f69SBart Van Assche  * function. */
2169*44704f69SBart Van Assche const char *
sg_get_category_sense_str(int sense_cat,int b_len,char * b,int verbose)2170*44704f69SBart Van Assche sg_get_category_sense_str(int sense_cat, int b_len, char * b, int verbose)
2171*44704f69SBart Van Assche {
2172*44704f69SBart Van Assche     if (NULL == b)
2173*44704f69SBart Van Assche         return bad_sense_cat;
2174*44704f69SBart Van Assche     if (b_len <= 0)
2175*44704f69SBart Van Assche         return b;
2176*44704f69SBart Van Assche     if (! sg_exit2str(sense_cat, (verbose > 0), b_len, b)) {
2177*44704f69SBart Van Assche         int n = sg_scnpr(b, b_len, "Sense category: %d", sense_cat);
2178*44704f69SBart Van Assche 
2179*44704f69SBart Van Assche         if ((0 == verbose) && (n < (b_len - 1)))
2180*44704f69SBart Van Assche             sg_scnpr(b + n, b_len - n, ", try '-v' option for more "
2181*44704f69SBart Van Assche                      "information");
2182*44704f69SBart Van Assche     }
2183*44704f69SBart Van Assche     return b;   /* Note that a valid C string is returned in all cases */
2184*44704f69SBart Van Assche }
2185*44704f69SBart Van Assche 
2186*44704f69SBart Van Assche /* See description in sg_lib.h header file */
2187*44704f69SBart Van Assche bool
sg_scsi_normalize_sense(const uint8_t * sbp,int sb_len,struct sg_scsi_sense_hdr * sshp)2188*44704f69SBart Van Assche sg_scsi_normalize_sense(const uint8_t * sbp, int sb_len,
2189*44704f69SBart Van Assche                         struct sg_scsi_sense_hdr * sshp)
2190*44704f69SBart Van Assche {
2191*44704f69SBart Van Assche     uint8_t resp_code;
2192*44704f69SBart Van Assche     if (sshp)
2193*44704f69SBart Van Assche         memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr));
2194*44704f69SBart Van Assche     if ((NULL == sbp) || (sb_len < 1))
2195*44704f69SBart Van Assche         return false;
2196*44704f69SBart Van Assche     resp_code = 0x7f & sbp[0];
2197*44704f69SBart Van Assche     if ((resp_code < 0x70) || (resp_code > 0x73))
2198*44704f69SBart Van Assche         return false;
2199*44704f69SBart Van Assche     if (sshp) {
2200*44704f69SBart Van Assche         sshp->response_code = resp_code;
2201*44704f69SBart Van Assche         if (sshp->response_code >= 0x72) {  /* descriptor format */
2202*44704f69SBart Van Assche             if (sb_len > 1)
2203*44704f69SBart Van Assche                 sshp->sense_key = (0xf & sbp[1]);
2204*44704f69SBart Van Assche             if (sb_len > 2)
2205*44704f69SBart Van Assche                 sshp->asc = sbp[2];
2206*44704f69SBart Van Assche             if (sb_len > 3)
2207*44704f69SBart Van Assche                 sshp->ascq = sbp[3];
2208*44704f69SBart Van Assche             if (sb_len > 7)
2209*44704f69SBart Van Assche                 sshp->additional_length = sbp[7];
2210*44704f69SBart Van Assche             sshp->byte4 = sbp[4];       /* bit 7: SDAT_OVFL bit */
2211*44704f69SBart Van Assche             /* sbp[5] and sbp[6] reserved for descriptor format */
2212*44704f69SBart Van Assche         } else {                              /* fixed format */
2213*44704f69SBart Van Assche             if (sb_len > 2)
2214*44704f69SBart Van Assche                 sshp->sense_key = (0xf & sbp[2]);
2215*44704f69SBart Van Assche             if (sb_len > 7) {
2216*44704f69SBart Van Assche                 sb_len = (sb_len < (sbp[7] + 8)) ? sb_len : (sbp[7] + 8);
2217*44704f69SBart Van Assche                 if (sb_len > 12)
2218*44704f69SBart Van Assche                     sshp->asc = sbp[12];
2219*44704f69SBart Van Assche                 if (sb_len > 13)
2220*44704f69SBart Van Assche                     sshp->ascq = sbp[13];
2221*44704f69SBart Van Assche             }
2222*44704f69SBart Van Assche             if (sb_len > 6) {   /* lower 3 bytes of INFO field */
2223*44704f69SBart Van Assche                 sshp->byte4 = sbp[4];
2224*44704f69SBart Van Assche                 sshp->byte5 = sbp[5];
2225*44704f69SBart Van Assche                 sshp->byte6 = sbp[6];
2226*44704f69SBart Van Assche             }
2227*44704f69SBart Van Assche         }
2228*44704f69SBart Van Assche     }
2229*44704f69SBart Van Assche     return true;
2230*44704f69SBart Van Assche }
2231*44704f69SBart Van Assche 
2232*44704f69SBart Van Assche /* Returns a SG_LIB_CAT_* value. If cannot decode sense buffer (sbp) or a
2233*44704f69SBart Van Assche  * less common sense key then return SG_LIB_CAT_SENSE .*/
2234*44704f69SBart Van Assche int
sg_err_category_sense(const uint8_t * sbp,int sb_len)2235*44704f69SBart Van Assche sg_err_category_sense(const uint8_t * sbp, int sb_len)
2236*44704f69SBart Van Assche {
2237*44704f69SBart Van Assche     struct sg_scsi_sense_hdr ssh;
2238*44704f69SBart Van Assche 
2239*44704f69SBart Van Assche     if ((sbp && (sb_len > 2)) &&
2240*44704f69SBart Van Assche         (sg_scsi_normalize_sense(sbp, sb_len, &ssh))) {
2241*44704f69SBart Van Assche         switch (ssh.sense_key) {        /* 0 to 0x1f */
2242*44704f69SBart Van Assche         case SPC_SK_NO_SENSE:
2243*44704f69SBart Van Assche             return SG_LIB_CAT_NO_SENSE;
2244*44704f69SBart Van Assche         case SPC_SK_RECOVERED_ERROR:
2245*44704f69SBart Van Assche             return SG_LIB_CAT_RECOVERED;
2246*44704f69SBart Van Assche         case SPC_SK_NOT_READY:
2247*44704f69SBart Van Assche             if ((0x04 == ssh.asc) && (0x0b == ssh.ascq))
2248*44704f69SBart Van Assche                 return SG_LIB_CAT_STANDBY;
2249*44704f69SBart Van Assche             if ((0x04 == ssh.asc) && (0x0c == ssh.ascq))
2250*44704f69SBart Van Assche                 return SG_LIB_CAT_UNAVAILABLE;
2251*44704f69SBart Van Assche             return SG_LIB_CAT_NOT_READY;
2252*44704f69SBart Van Assche         case SPC_SK_MEDIUM_ERROR:
2253*44704f69SBart Van Assche         case SPC_SK_HARDWARE_ERROR:
2254*44704f69SBart Van Assche         case SPC_SK_BLANK_CHECK:
2255*44704f69SBart Van Assche             return SG_LIB_CAT_MEDIUM_HARD;
2256*44704f69SBart Van Assche         case SPC_SK_UNIT_ATTENTION:
2257*44704f69SBart Van Assche             return SG_LIB_CAT_UNIT_ATTENTION;
2258*44704f69SBart Van Assche             /* used to return SG_LIB_CAT_MEDIA_CHANGED when ssh.asc==0x28 */
2259*44704f69SBart Van Assche         case SPC_SK_ILLEGAL_REQUEST:
2260*44704f69SBart Van Assche             if ((0x20 == ssh.asc) && (0x0 == ssh.ascq))
2261*44704f69SBart Van Assche                 return SG_LIB_CAT_INVALID_OP;
2262*44704f69SBart Van Assche             else if ((0x21 == ssh.asc) && (0x0 == ssh.ascq))
2263*44704f69SBart Van Assche                 return SG_LIB_LBA_OUT_OF_RANGE;
2264*44704f69SBart Van Assche             else
2265*44704f69SBart Van Assche                 return SG_LIB_CAT_ILLEGAL_REQ;
2266*44704f69SBart Van Assche             break;
2267*44704f69SBart Van Assche         case SPC_SK_ABORTED_COMMAND:
2268*44704f69SBart Van Assche             if (0x10 == ssh.asc)
2269*44704f69SBart Van Assche                 return SG_LIB_CAT_PROTECTION;
2270*44704f69SBart Van Assche             else
2271*44704f69SBart Van Assche                 return SG_LIB_CAT_ABORTED_COMMAND;
2272*44704f69SBart Van Assche         case SPC_SK_MISCOMPARE:
2273*44704f69SBart Van Assche             return SG_LIB_CAT_MISCOMPARE;
2274*44704f69SBart Van Assche         case SPC_SK_DATA_PROTECT:
2275*44704f69SBart Van Assche             return SG_LIB_CAT_DATA_PROTECT;
2276*44704f69SBart Van Assche         case SPC_SK_COPY_ABORTED:
2277*44704f69SBart Van Assche             return SG_LIB_CAT_COPY_ABORTED;
2278*44704f69SBart Van Assche         case SPC_SK_COMPLETED:
2279*44704f69SBart Van Assche         case SPC_SK_VOLUME_OVERFLOW:
2280*44704f69SBart Van Assche             return SG_LIB_CAT_SENSE;
2281*44704f69SBart Van Assche         default:
2282*44704f69SBart Van Assche             ;   /* reserved and vendor specific sense keys fall through */
2283*44704f69SBart Van Assche         }
2284*44704f69SBart Van Assche     }
2285*44704f69SBart Van Assche     return SG_LIB_CAT_SENSE;
2286*44704f69SBart Van Assche }
2287*44704f69SBart Van Assche 
2288*44704f69SBart Van Assche /* Beware: gives wrong answer for variable length command (opcode=0x7f) */
2289*44704f69SBart Van Assche int
sg_get_command_size(uint8_t opcode)2290*44704f69SBart Van Assche sg_get_command_size(uint8_t opcode)
2291*44704f69SBart Van Assche {
2292*44704f69SBart Van Assche     switch ((opcode >> 5) & 0x7) {
2293*44704f69SBart Van Assche     case 0:
2294*44704f69SBart Van Assche         return 6;
2295*44704f69SBart Van Assche     case 3: case 5:
2296*44704f69SBart Van Assche         return 12;
2297*44704f69SBart Van Assche     case 4:
2298*44704f69SBart Van Assche         return 16;
2299*44704f69SBart Van Assche     default:        /* 1, 2, 6, 7 */
2300*44704f69SBart Van Assche         return 10;
2301*44704f69SBart Van Assche     }
2302*44704f69SBart Van Assche }
2303*44704f69SBart Van Assche 
2304*44704f69SBart Van Assche void
sg_get_command_name(const uint8_t * cdbp,int peri_type,int buff_len,char * buff)2305*44704f69SBart Van Assche sg_get_command_name(const uint8_t * cdbp, int peri_type, int buff_len,
2306*44704f69SBart Van Assche                     char * buff)
2307*44704f69SBart Van Assche {
2308*44704f69SBart Van Assche     int service_action;
2309*44704f69SBart Van Assche 
2310*44704f69SBart Van Assche     if ((NULL == buff) || (buff_len < 1))
2311*44704f69SBart Van Assche         return;
2312*44704f69SBart Van Assche     else if (1 == buff_len) {
2313*44704f69SBart Van Assche         buff[0] = '\0';
2314*44704f69SBart Van Assche         return;
2315*44704f69SBart Van Assche     }
2316*44704f69SBart Van Assche     if (NULL == cdbp) {
2317*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "%s", "<null> command pointer");
2318*44704f69SBart Van Assche         return;
2319*44704f69SBart Van Assche     }
2320*44704f69SBart Van Assche     service_action = (SG_VARIABLE_LENGTH_CMD == cdbp[0]) ?
2321*44704f69SBart Van Assche                      sg_get_unaligned_be16(cdbp + 8) : (cdbp[1] & 0x1f);
2322*44704f69SBart Van Assche     sg_get_opcode_sa_name(cdbp[0], service_action, peri_type, buff_len, buff);
2323*44704f69SBart Van Assche }
2324*44704f69SBart Van Assche 
2325*44704f69SBart Van Assche struct op_code2sa_t {
2326*44704f69SBart Van Assche     int op_code;
2327*44704f69SBart Van Assche     int pdt_s;
2328*44704f69SBart Van Assche     struct sg_lib_value_name_t * arr;
2329*44704f69SBart Van Assche     const char * prefix;
2330*44704f69SBart Van Assche };
2331*44704f69SBart Van Assche 
2332*44704f69SBart Van Assche static struct op_code2sa_t op_code2sa_arr[] = {
2333*44704f69SBart Van Assche     {SG_VARIABLE_LENGTH_CMD, PDT_ALL, sg_lib_variable_length_arr, NULL},
2334*44704f69SBart Van Assche     {SG_MAINTENANCE_IN, PDT_ALL, sg_lib_maint_in_arr, NULL},
2335*44704f69SBart Van Assche     {SG_MAINTENANCE_OUT, PDT_ALL, sg_lib_maint_out_arr, NULL},
2336*44704f69SBart Van Assche     {SG_SERVICE_ACTION_IN_12, PDT_ALL, sg_lib_serv_in12_arr, NULL},
2337*44704f69SBart Van Assche     {SG_SERVICE_ACTION_OUT_12, PDT_ALL, sg_lib_serv_out12_arr, NULL},
2338*44704f69SBart Van Assche     {SG_SERVICE_ACTION_IN_16, PDT_ALL, sg_lib_serv_in16_arr, NULL},
2339*44704f69SBart Van Assche     {SG_SERVICE_ACTION_OUT_16, PDT_ALL, sg_lib_serv_out16_arr, NULL},
2340*44704f69SBart Van Assche     {SG_SERVICE_ACTION_BIDI, PDT_ALL, sg_lib_serv_bidi_arr, NULL},
2341*44704f69SBart Van Assche     {SG_PERSISTENT_RESERVE_IN, PDT_ALL, sg_lib_pr_in_arr,
2342*44704f69SBart Van Assche      "Persistent reserve in"},
2343*44704f69SBart Van Assche     {SG_PERSISTENT_RESERVE_OUT, PDT_ALL, sg_lib_pr_out_arr,
2344*44704f69SBart Van Assche      "Persistent reserve out"},
2345*44704f69SBart Van Assche     {SG_3PARTY_COPY_OUT, PDT_ALL, sg_lib_xcopy_sa_arr, NULL},
2346*44704f69SBart Van Assche     {SG_3PARTY_COPY_IN, PDT_ALL, sg_lib_rec_copy_sa_arr, NULL},
2347*44704f69SBart Van Assche     {SG_READ_BUFFER, PDT_ALL, sg_lib_read_buff_arr, "Read buffer(10)"},
2348*44704f69SBart Van Assche     {SG_READ_BUFFER_16, PDT_ALL, sg_lib_read_buff_arr, "Read buffer(16)"},
2349*44704f69SBart Van Assche     {SG_READ_ATTRIBUTE, PDT_ALL, sg_lib_read_attr_arr, "Read attribute"},
2350*44704f69SBart Van Assche     {SG_READ_POSITION, PDT_TAPE, sg_lib_read_pos_arr, "Read position"},
2351*44704f69SBart Van Assche     {SG_SANITIZE, PDT_DISK_ZBC, sg_lib_sanitize_sa_arr, "Sanitize"},
2352*44704f69SBart Van Assche     {SG_WRITE_BUFFER, PDT_ALL, sg_lib_write_buff_arr, "Write buffer"},
2353*44704f69SBart Van Assche     {SG_ZONING_IN, PDT_DISK_ZBC, sg_lib_zoning_in_arr, NULL},
2354*44704f69SBart Van Assche     {SG_ZONING_OUT, PDT_DISK_ZBC, sg_lib_zoning_out_arr, NULL},
2355*44704f69SBart Van Assche     {0xffff, -1, NULL, NULL},
2356*44704f69SBart Van Assche };
2357*44704f69SBart Van Assche 
2358*44704f69SBart Van Assche void
sg_get_opcode_sa_name(uint8_t cmd_byte0,int service_action,int peri_type,int buff_len,char * buff)2359*44704f69SBart Van Assche sg_get_opcode_sa_name(uint8_t cmd_byte0, int service_action,
2360*44704f69SBart Van Assche                       int peri_type, int buff_len, char * buff)
2361*44704f69SBart Van Assche {
2362*44704f69SBart Van Assche     int d_pdt;
2363*44704f69SBart Van Assche     const struct sg_lib_value_name_t * vnp;
2364*44704f69SBart Van Assche     const struct op_code2sa_t * osp;
2365*44704f69SBart Van Assche     char b[80];
2366*44704f69SBart Van Assche 
2367*44704f69SBart Van Assche     if ((NULL == buff) || (buff_len < 1))
2368*44704f69SBart Van Assche         return;
2369*44704f69SBart Van Assche     else if (1 == buff_len) {
2370*44704f69SBart Van Assche         buff[0] = '\0';
2371*44704f69SBart Van Assche         return;
2372*44704f69SBart Van Assche     }
2373*44704f69SBart Van Assche 
2374*44704f69SBart Van Assche     if (peri_type < 0)
2375*44704f69SBart Van Assche         peri_type = 0;
2376*44704f69SBart Van Assche     d_pdt = sg_lib_pdt_decay(peri_type);
2377*44704f69SBart Van Assche     for (osp = op_code2sa_arr; osp->arr; ++osp) {
2378*44704f69SBart Van Assche         if ((int)cmd_byte0 == osp->op_code) {
2379*44704f69SBart Van Assche             if (sg_pdt_s_eq(osp->pdt_s, d_pdt)) {
2380*44704f69SBart Van Assche                 vnp = get_value_name(osp->arr, service_action, peri_type);
2381*44704f69SBart Van Assche                 if (vnp) {
2382*44704f69SBart Van Assche                     if (osp->prefix)
2383*44704f69SBart Van Assche                         sg_scnpr(buff, buff_len, "%s, %s", osp->prefix,
2384*44704f69SBart Van Assche                                  vnp->name);
2385*44704f69SBart Van Assche                     else
2386*44704f69SBart Van Assche                         sg_scnpr(buff, buff_len, "%s", vnp->name);
2387*44704f69SBart Van Assche                 } else {
2388*44704f69SBart Van Assche                     sg_get_opcode_name(cmd_byte0, peri_type, sizeof(b), b);
2389*44704f69SBart Van Assche                     sg_scnpr(buff, buff_len, "%s service action=0x%x", b,
2390*44704f69SBart Van Assche                              service_action);
2391*44704f69SBart Van Assche                 }
2392*44704f69SBart Van Assche             } else
2393*44704f69SBart Van Assche                 sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff);
2394*44704f69SBart Van Assche             return;
2395*44704f69SBart Van Assche         }
2396*44704f69SBart Van Assche     }
2397*44704f69SBart Van Assche     sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff);
2398*44704f69SBart Van Assche }
2399*44704f69SBart Van Assche 
2400*44704f69SBart Van Assche void
sg_get_opcode_name(uint8_t cmd_byte0,int peri_type,int buff_len,char * buff)2401*44704f69SBart Van Assche sg_get_opcode_name(uint8_t cmd_byte0, int peri_type, int buff_len,
2402*44704f69SBart Van Assche                    char * buff)
2403*44704f69SBart Van Assche {
2404*44704f69SBart Van Assche     const struct sg_lib_value_name_t * vnp;
2405*44704f69SBart Van Assche     int grp;
2406*44704f69SBart Van Assche 
2407*44704f69SBart Van Assche     if ((NULL == buff) || (buff_len < 1))
2408*44704f69SBart Van Assche         return;
2409*44704f69SBart Van Assche     else if (1 == buff_len) {
2410*44704f69SBart Van Assche         buff[0] = '\0';
2411*44704f69SBart Van Assche         return;
2412*44704f69SBart Van Assche     }
2413*44704f69SBart Van Assche     if (SG_VARIABLE_LENGTH_CMD == cmd_byte0) {
2414*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "%s", "Variable length");
2415*44704f69SBart Van Assche         return;
2416*44704f69SBart Van Assche     }
2417*44704f69SBart Van Assche     grp = (cmd_byte0 >> 5) & 0x7;
2418*44704f69SBart Van Assche     switch (grp) {
2419*44704f69SBart Van Assche     case 0:
2420*44704f69SBart Van Assche     case 1:
2421*44704f69SBart Van Assche     case 2:
2422*44704f69SBart Van Assche     case 4:
2423*44704f69SBart Van Assche     case 5:
2424*44704f69SBart Van Assche         vnp = get_value_name(sg_lib_normal_opcodes, cmd_byte0, peri_type);
2425*44704f69SBart Van Assche         if (vnp)
2426*44704f69SBart Van Assche             sg_scnpr(buff, buff_len, "%s", vnp->name);
2427*44704f69SBart Van Assche         else
2428*44704f69SBart Van Assche             sg_scnpr(buff, buff_len, "Opcode=0x%x", (int)cmd_byte0);
2429*44704f69SBart Van Assche         break;
2430*44704f69SBart Van Assche     case 3:
2431*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "Reserved [0x%x]", (int)cmd_byte0);
2432*44704f69SBart Van Assche         break;
2433*44704f69SBart Van Assche     case 6:
2434*44704f69SBart Van Assche     case 7:
2435*44704f69SBart Van Assche         sg_scnpr(buff, buff_len, "Vendor specific [0x%x]", (int)cmd_byte0);
2436*44704f69SBart Van Assche         break;
2437*44704f69SBart Van Assche     }
2438*44704f69SBart Van Assche }
2439*44704f69SBart Van Assche 
2440*44704f69SBart Van Assche /* Fetch NVMe command name given first byte (byte offset 0 in 64 byte
2441*44704f69SBart Van Assche  * command) of command. Gets Admin NVMe command name if 'admin' is true
2442*44704f69SBart Van Assche  * (e.g. opcode=0x6 -> Identify), otherwise gets NVM command set name
2443*44704f69SBart Van Assche  * (e.g. opcode=0 -> Flush). Returns 'buff'. */
2444*44704f69SBart Van Assche char *
sg_get_nvme_opcode_name(uint8_t cmd_byte0,bool admin,int buff_len,char * buff)2445*44704f69SBart Van Assche sg_get_nvme_opcode_name(uint8_t cmd_byte0, bool admin, int buff_len,
2446*44704f69SBart Van Assche                         char * buff)
2447*44704f69SBart Van Assche {
2448*44704f69SBart Van Assche     const struct sg_lib_simple_value_name_t * vnp = admin ?
2449*44704f69SBart Van Assche                         sg_lib_nvme_admin_cmd_arr : sg_lib_nvme_nvm_cmd_arr;
2450*44704f69SBart Van Assche 
2451*44704f69SBart Van Assche     if ((NULL == buff) || (buff_len < 1))
2452*44704f69SBart Van Assche         return buff;
2453*44704f69SBart Van Assche     else if (1 == buff_len) {
2454*44704f69SBart Van Assche         buff[0] = '\0';
2455*44704f69SBart Van Assche         return buff;
2456*44704f69SBart Van Assche     }
2457*44704f69SBart Van Assche     for ( ; vnp->name; ++vnp) {
2458*44704f69SBart Van Assche         if (cmd_byte0 == (uint8_t)vnp->value) {
2459*44704f69SBart Van Assche             snprintf(buff, buff_len, "%s", vnp->name);
2460*44704f69SBart Van Assche             return buff;
2461*44704f69SBart Van Assche         }
2462*44704f69SBart Van Assche     }
2463*44704f69SBart Van Assche     if (admin) {
2464*44704f69SBart Van Assche         if (cmd_byte0 >= 0xc0)
2465*44704f69SBart Van Assche             snprintf(buff, buff_len, "Vendor specific opcode: 0x%x",
2466*44704f69SBart Van Assche                      cmd_byte0);
2467*44704f69SBart Van Assche         else if (cmd_byte0 >= 0x80)
2468*44704f69SBart Van Assche             snprintf(buff, buff_len, "Command set specific opcode: 0x%x",
2469*44704f69SBart Van Assche                      cmd_byte0);
2470*44704f69SBart Van Assche         else
2471*44704f69SBart Van Assche             snprintf(buff, buff_len, "Unknown opcode: 0x%x", cmd_byte0);
2472*44704f69SBart Van Assche     } else {    /* NVM (non-Admin) command set */
2473*44704f69SBart Van Assche         if (cmd_byte0 >= 0x80)
2474*44704f69SBart Van Assche             snprintf(buff, buff_len, "Vendor specific opcode: 0x%x",
2475*44704f69SBart Van Assche                      cmd_byte0);
2476*44704f69SBart Van Assche         else
2477*44704f69SBart Van Assche             snprintf(buff, buff_len, "Unknown opcode: 0x%x", cmd_byte0);
2478*44704f69SBart Van Assche     }
2479*44704f69SBart Van Assche     return buff;
2480*44704f69SBart Van Assche }
2481*44704f69SBart Van Assche 
2482*44704f69SBart Van Assche /* Iterates to next designation descriptor in the device identification
2483*44704f69SBart Van Assche  * VPD page. The 'initial_desig_desc' should point to start of first
2484*44704f69SBart Van Assche  * descriptor with 'page_len' being the number of valid bytes in that
2485*44704f69SBart Van Assche  * and following descriptors. To start, 'off' should point to a negative
2486*44704f69SBart Van Assche  * value, thereafter it should point to the value yielded by the previous
2487*44704f69SBart Van Assche  * call. If 0 returned then 'initial_desig_desc + *off' should be a valid
2488*44704f69SBart Van Assche  * descriptor; returns -1 if normal end condition and -2 for an abnormal
2489*44704f69SBart Van Assche  * termination. Matches association, designator_type and/or code_set when
2490*44704f69SBart Van Assche  * any of those values are greater than or equal to zero. */
2491*44704f69SBart Van Assche int
sg_vpd_dev_id_iter(const uint8_t * initial_desig_desc,int page_len,int * off,int m_assoc,int m_desig_type,int m_code_set)2492*44704f69SBart Van Assche sg_vpd_dev_id_iter(const uint8_t * initial_desig_desc, int page_len,
2493*44704f69SBart Van Assche                    int * off, int m_assoc, int m_desig_type, int m_code_set)
2494*44704f69SBart Van Assche {
2495*44704f69SBart Van Assche     bool fltr = ((m_assoc >= 0) || (m_desig_type >= 0) || (m_code_set >= 0));
2496*44704f69SBart Van Assche     int k = *off;
2497*44704f69SBart Van Assche     const uint8_t * bp = initial_desig_desc;
2498*44704f69SBart Van Assche 
2499*44704f69SBart Van Assche     while ((k + 3) < page_len) {
2500*44704f69SBart Van Assche         k = (k < 0) ? 0 : (k + bp[k + 3] + 4);
2501*44704f69SBart Van Assche         if ((k + 4) > page_len)
2502*44704f69SBart Van Assche             break;
2503*44704f69SBart Van Assche         if (fltr) {
2504*44704f69SBart Van Assche             if (m_code_set >= 0) {
2505*44704f69SBart Van Assche                 if ((bp[k] & 0xf) != m_code_set)
2506*44704f69SBart Van Assche                     continue;
2507*44704f69SBart Van Assche             }
2508*44704f69SBart Van Assche             if (m_assoc >= 0) {
2509*44704f69SBart Van Assche                 if (((bp[k + 1] >> 4) & 0x3) != m_assoc)
2510*44704f69SBart Van Assche                     continue;
2511*44704f69SBart Van Assche             }
2512*44704f69SBart Van Assche             if (m_desig_type >= 0) {
2513*44704f69SBart Van Assche                 if ((bp[k + 1] & 0xf) != m_desig_type)
2514*44704f69SBart Van Assche                     continue;
2515*44704f69SBart Van Assche             }
2516*44704f69SBart Van Assche         }
2517*44704f69SBart Van Assche         *off = k;
2518*44704f69SBart Van Assche         return 0;
2519*44704f69SBart Van Assche     }
2520*44704f69SBart Van Assche     return (k == page_len) ? -1 : -2;
2521*44704f69SBart Van Assche }
2522*44704f69SBart Van Assche 
2523*44704f69SBart Van Assche static const char * sg_sfs_spc_reserved = "SPC Reserved";
2524*44704f69SBart Van Assche static const char * sg_sfs_sbc_reserved = "SBC Reserved";
2525*44704f69SBart Van Assche static const char * sg_sfs_ssc_reserved = "SSC Reserved";
2526*44704f69SBart Van Assche static const char * sg_sfs_zbc_reserved = "ZBC Reserved";
2527*44704f69SBart Van Assche static const char * sg_sfs_reserved = "Reserved";
2528*44704f69SBart Van Assche 
2529*44704f69SBart Van Assche /* Yield SCSI Feature Set (sfs) string. When 'peri_type' is < -1 (or > 31)
2530*44704f69SBart Van Assche  * returns pointer to string (same as 'buff') associated with 'sfs_code'.
2531*44704f69SBart Van Assche  * When 'peri_type' is between -1 (for SPC) and 31 (inclusive) then a match
2532*44704f69SBart Van Assche  * on both 'sfs_code' and 'peri_type' is required. If 'foundp' is not NULL
2533*44704f69SBart Van Assche  * then where it points is set to true if a match is found else it is set to
2534*44704f69SBart Van Assche  * false. If 'buff' is not NULL then in the case of a match a descriptive
2535*44704f69SBart Van Assche  * string is written to 'buff' while if there is not a not then a string
2536*44704f69SBart Van Assche  * ending in "Reserved" is written (and may be prefixed with SPC, SBC, SSC
2537*44704f69SBart Van Assche  * or ZBC). Returns 'buff' (i.e. a pointer value) even if it is NULL.
2538*44704f69SBart Van Assche  * Example:
2539*44704f69SBart Van Assche  *    char b[64];
2540*44704f69SBart Van Assche  *    ...
2541*44704f69SBart Van Assche  *    printf("%s\n", sg_get_sfs_str(sfs_code, -2, sizeof(b), b, NULL, 0));
2542*44704f69SBart Van Assche  */
2543*44704f69SBart Van Assche const char *
sg_get_sfs_str(uint16_t sfs_code,int peri_type,int buff_len,char * buff,bool * foundp,int verbose)2544*44704f69SBart Van Assche sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len, char * buff,
2545*44704f69SBart Van Assche                bool * foundp, int verbose)
2546*44704f69SBart Van Assche {
2547*44704f69SBart Van Assche     const struct sg_lib_value_name_t * vnp = NULL;
2548*44704f69SBart Van Assche     int n = 0;
2549*44704f69SBart Van Assche     int my_pdt;
2550*44704f69SBart Van Assche 
2551*44704f69SBart Van Assche     if ((NULL == buff) || (buff_len < 1)) {
2552*44704f69SBart Van Assche         if (foundp)
2553*44704f69SBart Van Assche             *foundp = false;
2554*44704f69SBart Van Assche         return NULL;
2555*44704f69SBart Van Assche     } else if (1 == buff_len) {
2556*44704f69SBart Van Assche         buff[0] = '\0';
2557*44704f69SBart Van Assche         if (foundp)
2558*44704f69SBart Van Assche             *foundp = false;
2559*44704f69SBart Van Assche         return NULL;
2560*44704f69SBart Van Assche     }
2561*44704f69SBart Van Assche     my_pdt = ((peri_type < -1) || (peri_type > PDT_MAX)) ? -2 : peri_type;
2562*44704f69SBart Van Assche     vnp = get_value_name(sg_lib_scsi_feature_sets, sfs_code, my_pdt);
2563*44704f69SBart Van Assche     if (vnp && (-2 != my_pdt)) {
2564*44704f69SBart Van Assche         if (! sg_pdt_s_eq(my_pdt, vnp->peri_dev_type))
2565*44704f69SBart Van Assche             vnp = NULL;      /* shouldn't really happen */
2566*44704f69SBart Van Assche     }
2567*44704f69SBart Van Assche     if (foundp)
2568*44704f69SBart Van Assche         *foundp = vnp ? true : false;
2569*44704f69SBart Van Assche     if (sfs_code < 0x100) {             /* SPC Feature Sets */
2570*44704f69SBart Van Assche         if (vnp) {
2571*44704f69SBart Van Assche             if (verbose)
2572*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "SPC %s", vnp->name);
2573*44704f69SBart Van Assche             else
2574*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "%s", vnp->name);
2575*44704f69SBart Van Assche         } else
2576*44704f69SBart Van Assche             n += sg_scnpr(buff, buff_len, "%s", sg_sfs_spc_reserved);
2577*44704f69SBart Van Assche     } else if (sfs_code < 0x200) {      /* SBC Feature Sets */
2578*44704f69SBart Van Assche         if (vnp) {
2579*44704f69SBart Van Assche             if (verbose)
2580*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "SBC %s", vnp->name);
2581*44704f69SBart Van Assche             else
2582*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "%s", vnp->name);
2583*44704f69SBart Van Assche         } else
2584*44704f69SBart Van Assche             n += sg_scnpr(buff, buff_len, "%s", sg_sfs_sbc_reserved);
2585*44704f69SBart Van Assche     } else if (sfs_code < 0x300) {      /* SSC Feature Sets */
2586*44704f69SBart Van Assche         if (vnp) {
2587*44704f69SBart Van Assche             if (verbose)
2588*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "SSC %s", vnp->name);
2589*44704f69SBart Van Assche             else
2590*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "%s", vnp->name);
2591*44704f69SBart Van Assche         } else
2592*44704f69SBart Van Assche             n += sg_scnpr(buff, buff_len, "%s", sg_sfs_ssc_reserved);
2593*44704f69SBart Van Assche     } else if (sfs_code < 0x400) {      /* ZBC Feature Sets */
2594*44704f69SBart Van Assche         if (vnp) {
2595*44704f69SBart Van Assche             if (verbose)
2596*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "ZBC %s", vnp->name);
2597*44704f69SBart Van Assche             else
2598*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "%s", vnp->name);
2599*44704f69SBart Van Assche         } else
2600*44704f69SBart Van Assche             n += sg_scnpr(buff, buff_len, "%s", sg_sfs_zbc_reserved);
2601*44704f69SBart Van Assche     } else {                            /* Other SCSI Feature Sets */
2602*44704f69SBart Van Assche         if (vnp) {
2603*44704f69SBart Van Assche             if (verbose)
2604*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "[unrecognized PDT] %s",
2605*44704f69SBart Van Assche                               vnp->name);
2606*44704f69SBart Van Assche             else
2607*44704f69SBart Van Assche                 n += sg_scnpr(buff, buff_len, "%s", vnp->name);
2608*44704f69SBart Van Assche         } else
2609*44704f69SBart Van Assche             n += sg_scnpr(buff, buff_len, "%s", sg_sfs_reserved);
2610*44704f69SBart Van Assche 
2611*44704f69SBart Van Assche     }
2612*44704f69SBart Van Assche     if (verbose > 4)
2613*44704f69SBart Van Assche         pr2ws("%s: length of returned string (n) %d\n", __func__, n);
2614*44704f69SBart Van Assche     return buff;
2615*44704f69SBart Van Assche }
2616*44704f69SBart Van Assche 
2617*44704f69SBart Van Assche /* This is a heuristic that takes into account the command bytes and length
2618*44704f69SBart Van Assche  * to decide whether the presented unstructured sequence of bytes could be
2619*44704f69SBart Van Assche  * a SCSI command. If so it returns true otherwise false. Vendor specific
2620*44704f69SBart Van Assche  * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
2621*44704f69SBart Van Assche  * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
2622*44704f69SBart Van Assche  * only SCSI commands considered above 16 bytes of length are the Variable
2623*44704f69SBart Van Assche  * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e).
2624*44704f69SBart Van Assche  * Both have an inbuilt length field which can be cross checked with clen.
2625*44704f69SBart Van Assche  * No NVMe commands (64 bytes long plus some extra added by some OSes) have
2626*44704f69SBart Van Assche  * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
2627*44704f69SBart Van Assche  * structures that are sent across the wire. The FIS register structure is
2628*44704f69SBart Van Assche  * used to move a command from a SATA host to device, but the ATA 'command'
2629*44704f69SBart Van Assche  * is not the first byte. So it is harder to say what will happen if a
2630*44704f69SBart Van Assche  * FIS structure is presented as a SCSI command, hopefully there is a low
2631*44704f69SBart Van Assche  * probability this function will yield true in that case. */
2632*44704f69SBart Van Assche bool
sg_is_scsi_cdb(const uint8_t * cdbp,int clen)2633*44704f69SBart Van Assche sg_is_scsi_cdb(const uint8_t * cdbp, int clen)
2634*44704f69SBart Van Assche {
2635*44704f69SBart Van Assche     uint8_t opcode;
2636*44704f69SBart Van Assche     uint8_t top3bits;
2637*44704f69SBart Van Assche 
2638*44704f69SBart Van Assche     if (clen < 6)
2639*44704f69SBart Van Assche         return false;
2640*44704f69SBart Van Assche     opcode = cdbp[0];
2641*44704f69SBart Van Assche     top3bits = opcode >> 5;
2642*44704f69SBart Van Assche     if (0x3 == top3bits) {
2643*44704f69SBart Van Assche         int ilen, sa;
2644*44704f69SBart Van Assche 
2645*44704f69SBart Van Assche         if ((clen < 12) || (clen % 4))
2646*44704f69SBart Van Assche             return false;       /* must be modulo 4 and 12 or more bytes */
2647*44704f69SBart Van Assche         switch (opcode) {
2648*44704f69SBart Van Assche         case 0x7e:      /* Extended cdb (XCDB) */
2649*44704f69SBart Van Assche             ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
2650*44704f69SBart Van Assche             return (ilen == clen);
2651*44704f69SBart Van Assche         case 0x7f:      /* Variable Length cdb */
2652*44704f69SBart Van Assche             ilen = 8 + cdbp[7];
2653*44704f69SBart Van Assche             sa = sg_get_unaligned_be16(cdbp + 8);
2654*44704f69SBart Van Assche             /* service action (sa) 0x0 is reserved */
2655*44704f69SBart Van Assche             return ((ilen == clen) && sa);
2656*44704f69SBart Van Assche         default:
2657*44704f69SBart Van Assche             return false;
2658*44704f69SBart Van Assche         }
2659*44704f69SBart Van Assche     } else if (clen <= 16) {
2660*44704f69SBart Van Assche         switch (clen) {
2661*44704f69SBart Van Assche         case 6:
2662*44704f69SBart Van Assche             if (top3bits > 0x5)         /* vendor */
2663*44704f69SBart Van Assche                 return true;
2664*44704f69SBart Van Assche             return (0x0 == top3bits);   /* 6 byte cdb */
2665*44704f69SBart Van Assche         case 10:
2666*44704f69SBart Van Assche             if (top3bits > 0x5)         /* vendor */
2667*44704f69SBart Van Assche                 return true;
2668*44704f69SBart Van Assche             return ((0x1 == top3bits) || (0x2 == top3bits)); /* 10 byte cdb */
2669*44704f69SBart Van Assche         case 16:
2670*44704f69SBart Van Assche             if (top3bits > 0x5)         /* vendor */
2671*44704f69SBart Van Assche                 return true;
2672*44704f69SBart Van Assche             return (0x4 == top3bits);   /* 16 byte cdb */
2673*44704f69SBart Van Assche         case 12:
2674*44704f69SBart Van Assche             if (top3bits > 0x5)         /* vendor */
2675*44704f69SBart Van Assche                 return true;
2676*44704f69SBart Van Assche             return (0x5 == top3bits);   /* 12 byte cdb */
2677*44704f69SBart Van Assche         default:
2678*44704f69SBart Van Assche             return false;
2679*44704f69SBart Van Assche         }
2680*44704f69SBart Van Assche     }
2681*44704f69SBart Van Assche     /* NVMe probably falls out here, clen > 16 and (opcode < 0x60 or
2682*44704f69SBart Van Assche      * opcode > 0x7f). */
2683*44704f69SBart Van Assche     return false;
2684*44704f69SBart Van Assche }
2685*44704f69SBart Van Assche 
2686*44704f69SBart Van Assche /* Yield string associated with NVMe command status value in sct_sc. It
2687*44704f69SBart Van Assche  * expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25
2688*44704f69SBart Van Assche  * are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC).
2689*44704f69SBart Van Assche  * Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found
2690*44704f69SBart Van Assche  * a string of the form "Reserved [0x<sct_sc_in_hex>]" is generated.
2691*44704f69SBart Van Assche  * Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/
2692*44704f69SBart Van Assche char *
sg_get_nvme_cmd_status_str(uint16_t sct_sc,int b_len,char * b)2693*44704f69SBart Van Assche sg_get_nvme_cmd_status_str(uint16_t sct_sc, int b_len, char * b)
2694*44704f69SBart Van Assche {
2695*44704f69SBart Van Assche     int k;
2696*44704f69SBart Van Assche     uint16_t s = 0x3ff & sct_sc;
2697*44704f69SBart Van Assche     const struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr;
2698*44704f69SBart Van Assche 
2699*44704f69SBart Van Assche     if ((b_len <= 0) || (NULL == b))
2700*44704f69SBart Van Assche         return b;
2701*44704f69SBart Van Assche     else if (1 == b_len) {
2702*44704f69SBart Van Assche         b[0] = '\0';
2703*44704f69SBart Van Assche         return b;
2704*44704f69SBart Van Assche     }
2705*44704f69SBart Van Assche     for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) {
2706*44704f69SBart Van Assche         if (s == (uint16_t)vp->value) {
2707*44704f69SBart Van Assche             strncpy(b, vp->name, b_len);
2708*44704f69SBart Van Assche             b[b_len - 1] = '\0';
2709*44704f69SBart Van Assche             return b;
2710*44704f69SBart Van Assche         }
2711*44704f69SBart Van Assche     }
2712*44704f69SBart Van Assche     if (k >= 1000)
2713*44704f69SBart Van Assche         pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n",
2714*44704f69SBart Van Assche                         __func__);
2715*44704f69SBart Van Assche     snprintf(b, b_len, "Reserved [0x%x]", sct_sc);
2716*44704f69SBart Van Assche     return b;
2717*44704f69SBart Van Assche }
2718*44704f69SBart Van Assche 
2719*44704f69SBart Van Assche /* Attempts to map NVMe status value ((SCT << 8) | SC) to SCSI status,
2720*44704f69SBart Van Assche  * sense_key, asc and ascq tuple. If successful returns true and writes to
2721*44704f69SBart Van Assche  * non-NULL pointer arguments; otherwise returns false. */
2722*44704f69SBart Van Assche bool
sg_nvme_status2scsi(uint16_t sct_sc,uint8_t * status_p,uint8_t * sk_p,uint8_t * asc_p,uint8_t * ascq_p)2723*44704f69SBart Van Assche sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p,
2724*44704f69SBart Van Assche                     uint8_t * asc_p, uint8_t * ascq_p)
2725*44704f69SBart Van Assche {
2726*44704f69SBart Van Assche     int k, ind;
2727*44704f69SBart Van Assche     uint16_t s = 0x3ff & sct_sc;
2728*44704f69SBart Van Assche     struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr;
2729*44704f69SBart Van Assche     struct sg_lib_4tuple_u8 * mp = sg_lib_scsi_status_sense_arr;
2730*44704f69SBart Van Assche 
2731*44704f69SBart Van Assche     for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) {
2732*44704f69SBart Van Assche         if (s == (uint16_t)vp->value)
2733*44704f69SBart Van Assche             break;
2734*44704f69SBart Van Assche     }
2735*44704f69SBart Van Assche     if (k >= 1000) {
2736*44704f69SBart Van Assche         pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n",
2737*44704f69SBart Van Assche               __func__);
2738*44704f69SBart Van Assche         return false;
2739*44704f69SBart Van Assche     }
2740*44704f69SBart Van Assche     if (NULL == vp->name)
2741*44704f69SBart Van Assche         return false;
2742*44704f69SBart Van Assche     ind = vp->peri_dev_type;
2743*44704f69SBart Van Assche 
2744*44704f69SBart Van Assche 
2745*44704f69SBart Van Assche     for (k = 0; (0xff != mp->t2) && k < 1000; ++k, ++mp)
2746*44704f69SBart Van Assche         ;       /* count entries for valid index range */
2747*44704f69SBart Van Assche     if (k >= 1000) {
2748*44704f69SBart Van Assche         pr2ws("%s: where is sentinel for sg_lib_scsi_status_sense_arr ??\n",
2749*44704f69SBart Van Assche               __func__);
2750*44704f69SBart Van Assche         return false;
2751*44704f69SBart Van Assche     } else if (ind >= k)
2752*44704f69SBart Van Assche         return false;
2753*44704f69SBart Van Assche 
2754*44704f69SBart Van Assche     mp = sg_lib_scsi_status_sense_arr + ind;
2755*44704f69SBart Van Assche     if (status_p)
2756*44704f69SBart Van Assche         *status_p = mp->t1;
2757*44704f69SBart Van Assche     if (sk_p)
2758*44704f69SBart Van Assche         *sk_p = mp->t2;
2759*44704f69SBart Van Assche     if (asc_p)
2760*44704f69SBart Van Assche         *asc_p = mp->t3;
2761*44704f69SBart Van Assche     if (ascq_p)
2762*44704f69SBart Van Assche         *ascq_p = mp->t4;
2763*44704f69SBart Van Assche     return true;
2764*44704f69SBart Van Assche }
2765*44704f69SBart Van Assche 
2766*44704f69SBart Van Assche /* Add vendor (sg3_utils) specific sense descriptor for the NVMe Status
2767*44704f69SBart Van Assche  * field. Assumes descriptor (i.e. not fixed) sense. Assumes sbp has room. */
2768*44704f69SBart Van Assche void
sg_nvme_desc2sense(uint8_t * sbp,bool dnr,bool more,uint16_t sct_sc)2769*44704f69SBart Van Assche sg_nvme_desc2sense(uint8_t * sbp, bool dnr, bool more, uint16_t sct_sc)
2770*44704f69SBart Van Assche {
2771*44704f69SBart Van Assche     int len = sbp[7] + 8;
2772*44704f69SBart Van Assche 
2773*44704f69SBart Van Assche     sbp[len] = 0xde;            /* vendor specific descriptor type */
2774*44704f69SBart Van Assche     sbp[len + 1] = 6;           /* descriptor is 8 bytes long */
2775*44704f69SBart Van Assche     memset(sbp + len + 2, 0, 6);
2776*44704f69SBart Van Assche     if (dnr)
2777*44704f69SBart Van Assche         sbp[len + 5] = 0x80;
2778*44704f69SBart Van Assche     if (more)
2779*44704f69SBart Van Assche         sbp[len + 5] |= 0x40;
2780*44704f69SBart Van Assche     sg_put_unaligned_be16(sct_sc, sbp + len + 6);
2781*44704f69SBart Van Assche     sbp[7] += 8;
2782*44704f69SBart Van Assche }
2783*44704f69SBart Van Assche 
2784*44704f69SBart Van Assche /* Build minimum sense buffer, either descriptor type (desc=true) or fixed
2785*44704f69SBart Van Assche  * type (desc=false). Assume sbp has enough room (8 or 14 bytes
2786*44704f69SBart Van Assche  * respectively). sbp should have room for 32 or 18 bytes respectively */
2787*44704f69SBart Van Assche void
sg_build_sense_buffer(bool desc,uint8_t * sbp,uint8_t skey,uint8_t asc,uint8_t ascq)2788*44704f69SBart Van Assche sg_build_sense_buffer(bool desc, uint8_t *sbp, uint8_t skey, uint8_t asc,
2789*44704f69SBart Van Assche                       uint8_t ascq)
2790*44704f69SBart Van Assche {
2791*44704f69SBart Van Assche     if (desc) {
2792*44704f69SBart Van Assche         sbp[0] = 0x72;  /* descriptor, current */
2793*44704f69SBart Van Assche         sbp[1] = skey;
2794*44704f69SBart Van Assche         sbp[2] = asc;
2795*44704f69SBart Van Assche         sbp[3] = ascq;
2796*44704f69SBart Van Assche         sbp[7] = 0;
2797*44704f69SBart Van Assche     } else {
2798*44704f69SBart Van Assche         sbp[0] = 0x70;  /* fixed, current */
2799*44704f69SBart Van Assche         sbp[2] = skey;
2800*44704f69SBart Van Assche         sbp[7] = 0xa;   /* Assumes length is 18 bytes */
2801*44704f69SBart Van Assche         sbp[12] = asc;
2802*44704f69SBart Van Assche         sbp[13] = ascq;
2803*44704f69SBart Van Assche     }
2804*44704f69SBart Van Assche }
2805*44704f69SBart Van Assche 
2806*44704f69SBart Van Assche /* safe_strerror() contributed by Clayton Weaver <cgweav at email dot com>
2807*44704f69SBart Van Assche  * Allows for situation in which strerror() is given a wild value (or the
2808*44704f69SBart Van Assche  * C library is incomplete) and returns NULL. Still not thread safe.
2809*44704f69SBart Van Assche  */
2810*44704f69SBart Van Assche 
2811*44704f69SBart Van Assche static char safe_errbuf[64] = {'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ',
2812*44704f69SBart Van Assche                                'e', 'r', 'r', 'n', 'o', ':', ' ', 0};
2813*44704f69SBart Van Assche 
2814*44704f69SBart Van Assche char *
safe_strerror(int errnum)2815*44704f69SBart Van Assche safe_strerror(int errnum)
2816*44704f69SBart Van Assche {
2817*44704f69SBart Van Assche     char * errstr;
2818*44704f69SBart Van Assche 
2819*44704f69SBart Van Assche     if (errnum < 0)
2820*44704f69SBart Van Assche         errnum = -errnum;
2821*44704f69SBart Van Assche     errstr = strerror(errnum);
2822*44704f69SBart Van Assche     if (NULL == errstr) {
2823*44704f69SBart Van Assche         size_t len = strlen(safe_errbuf);
2824*44704f69SBart Van Assche 
2825*44704f69SBart Van Assche         sg_scnpr(safe_errbuf + len, sizeof(safe_errbuf) - len, "%i", errnum);
2826*44704f69SBart Van Assche         return safe_errbuf;
2827*44704f69SBart Van Assche     }
2828*44704f69SBart Van Assche     return errstr;
2829*44704f69SBart Van Assche }
2830*44704f69SBart Van Assche 
2831*44704f69SBart Van Assche static int
trimTrailingSpaces(char * b)2832*44704f69SBart Van Assche trimTrailingSpaces(char * b)
2833*44704f69SBart Van Assche {
2834*44704f69SBart Van Assche     int n = strlen(b);
2835*44704f69SBart Van Assche 
2836*44704f69SBart Van Assche     while ((n > 0) && (' ' == b[n - 1]))
2837*44704f69SBart Van Assche         b[--n] = '\0';
2838*44704f69SBart Van Assche     return n;
2839*44704f69SBart Van Assche }
2840*44704f69SBart Van Assche 
2841*44704f69SBart Van Assche /* Read binary starting at 'str' for 'len' bytes and output as ASCII
2842*44704f69SBart Van Assche  * hexadecinal into file pointer (fp). 16 bytes per line are output with an
2843*44704f69SBart Van Assche  * additional space between 8th and 9th byte on each line (for readability).
2844*44704f69SBart Van Assche  * 'no_ascii' selects one of 3 output format types:
2845*44704f69SBart Van Assche  *     > 0     each line has address then up to 16 ASCII-hex bytes
2846*44704f69SBart Van Assche  *     = 0     in addition, the bytes are listed in ASCII to the right
2847*44704f69SBart Van Assche  *     < 0     only the ASCII-hex bytes are listed (i.e. without address) */
2848*44704f69SBart Van Assche void
dStrHexFp(const char * str,int len,int no_ascii,FILE * fp)2849*44704f69SBart Van Assche dStrHexFp(const char* str, int len, int no_ascii, FILE * fp)
2850*44704f69SBart Van Assche {
2851*44704f69SBart Van Assche     const char * p = str;
2852*44704f69SBart Van Assche     const char * formatstr;
2853*44704f69SBart Van Assche     uint8_t c;
2854*44704f69SBart Van Assche     char buff[82];
2855*44704f69SBart Van Assche     int a = 0;
2856*44704f69SBart Van Assche     int bpstart = 5;
2857*44704f69SBart Van Assche     const int cpstart = 60;
2858*44704f69SBart Van Assche     int cpos = cpstart;
2859*44704f69SBart Van Assche     int bpos = bpstart;
2860*44704f69SBart Van Assche     int i, k, blen;
2861*44704f69SBart Van Assche 
2862*44704f69SBart Van Assche     if (len <= 0)
2863*44704f69SBart Van Assche         return;
2864*44704f69SBart Van Assche     blen = (int)sizeof(buff);
2865*44704f69SBart Van Assche     if (0 == no_ascii)  /* address at left and ASCII at right */
2866*44704f69SBart Van Assche         formatstr = "%.76s\n";
2867*44704f69SBart Van Assche     else                        /* previously when > 0 str was "%.58s\n" */
2868*44704f69SBart Van Assche         formatstr = "%s\n";     /* when < 0 str was: "%.48s\n" */
2869*44704f69SBart Van Assche     memset(buff, ' ', 80);
2870*44704f69SBart Van Assche     buff[80] = '\0';
2871*44704f69SBart Van Assche     if (no_ascii < 0) {
2872*44704f69SBart Van Assche         bpstart = 0;
2873*44704f69SBart Van Assche         bpos = bpstart;
2874*44704f69SBart Van Assche         for (k = 0; k < len; k++) {
2875*44704f69SBart Van Assche             c = *p++;
2876*44704f69SBart Van Assche             if (bpos == (bpstart + (8 * 3)))
2877*44704f69SBart Van Assche                 bpos++;
2878*44704f69SBart Van Assche             sg_scnpr(&buff[bpos], blen - bpos, "%.2x", (int)(uint8_t)c);
2879*44704f69SBart Van Assche             buff[bpos + 2] = ' ';
2880*44704f69SBart Van Assche             if ((k > 0) && (0 == ((k + 1) % 16))) {
2881*44704f69SBart Van Assche                 trimTrailingSpaces(buff);
2882*44704f69SBart Van Assche                 fprintf(fp, formatstr, buff);
2883*44704f69SBart Van Assche                 bpos = bpstart;
2884*44704f69SBart Van Assche                 memset(buff, ' ', 80);
2885*44704f69SBart Van Assche             } else
2886*44704f69SBart Van Assche                 bpos += 3;
2887*44704f69SBart Van Assche         }
2888*44704f69SBart Van Assche         if (bpos > bpstart) {
2889*44704f69SBart Van Assche             buff[bpos + 2] = '\0';
2890*44704f69SBart Van Assche             trimTrailingSpaces(buff);
2891*44704f69SBart Van Assche             fprintf(fp, "%s\n", buff);
2892*44704f69SBart Van Assche         }
2893*44704f69SBart Van Assche         return;
2894*44704f69SBart Van Assche     }
2895*44704f69SBart Van Assche     /* no_ascii>=0, start each line with address (offset) */
2896*44704f69SBart Van Assche     k = sg_scnpr(buff + 1, blen - 1, "%.2x", a);
2897*44704f69SBart Van Assche     buff[k + 1] = ' ';
2898*44704f69SBart Van Assche 
2899*44704f69SBart Van Assche     for (i = 0; i < len; i++) {
2900*44704f69SBart Van Assche         c = *p++;
2901*44704f69SBart Van Assche         bpos += 3;
2902*44704f69SBart Van Assche         if (bpos == (bpstart + (9 * 3)))
2903*44704f69SBart Van Assche             bpos++;
2904*44704f69SBart Van Assche         sg_scnpr(&buff[bpos], blen - bpos, "%.2x", (int)(uint8_t)c);
2905*44704f69SBart Van Assche         buff[bpos + 2] = ' ';
2906*44704f69SBart Van Assche         if (no_ascii)
2907*44704f69SBart Van Assche             buff[cpos++] = ' ';
2908*44704f69SBart Van Assche         else {
2909*44704f69SBart Van Assche             if (! my_isprint(c))
2910*44704f69SBart Van Assche                 c = '.';
2911*44704f69SBart Van Assche             buff[cpos++] = c;
2912*44704f69SBart Van Assche         }
2913*44704f69SBart Van Assche         if (cpos > (cpstart + 15)) {
2914*44704f69SBart Van Assche             if (no_ascii)
2915*44704f69SBart Van Assche                 trimTrailingSpaces(buff);
2916*44704f69SBart Van Assche             fprintf(fp, formatstr, buff);
2917*44704f69SBart Van Assche             bpos = bpstart;
2918*44704f69SBart Van Assche             cpos = cpstart;
2919*44704f69SBart Van Assche             a += 16;
2920*44704f69SBart Van Assche             memset(buff, ' ', 80);
2921*44704f69SBart Van Assche             k = sg_scnpr(buff + 1, blen - 1, "%.2x", a);
2922*44704f69SBart Van Assche             buff[k + 1] = ' ';
2923*44704f69SBart Van Assche         }
2924*44704f69SBart Van Assche     }
2925*44704f69SBart Van Assche     if (cpos > cpstart) {
2926*44704f69SBart Van Assche         buff[cpos] = '\0';
2927*44704f69SBart Van Assche         if (no_ascii)
2928*44704f69SBart Van Assche             trimTrailingSpaces(buff);
2929*44704f69SBart Van Assche         fprintf(fp, "%s\n", buff);
2930*44704f69SBart Van Assche     }
2931*44704f69SBart Van Assche }
2932*44704f69SBart Van Assche 
2933*44704f69SBart Van Assche void
dStrHex(const char * str,int len,int no_ascii)2934*44704f69SBart Van Assche dStrHex(const char* str, int len, int no_ascii)
2935*44704f69SBart Van Assche {
2936*44704f69SBart Van Assche     dStrHexFp(str, len, no_ascii, stdout);
2937*44704f69SBart Van Assche }
2938*44704f69SBart Van Assche 
2939*44704f69SBart Van Assche void
dStrHexErr(const char * str,int len,int no_ascii)2940*44704f69SBart Van Assche dStrHexErr(const char* str, int len, int no_ascii)
2941*44704f69SBart Van Assche {
2942*44704f69SBart Van Assche     dStrHexFp(str, len, no_ascii,
2943*44704f69SBart Van Assche               (sg_warnings_strm ? sg_warnings_strm : stderr));
2944*44704f69SBart Van Assche }
2945*44704f69SBart Van Assche 
2946*44704f69SBart Van Assche #define DSHS_LINE_BLEN 160      /* maximum characters per line */
2947*44704f69SBart Van Assche #define DSHS_BPL 16             /* bytes per line */
2948*44704f69SBart Van Assche 
2949*44704f69SBart Van Assche /* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space separated)
2950*44704f69SBart Van Assche  * to 'b' not to exceed 'b_len' characters. Each line starts with 'leadin'
2951*44704f69SBart Van Assche  * (NULL for no leadin) and there are 16 bytes per line with an extra space
2952*44704f69SBart Van Assche  * between the 8th and 9th bytes. 'oformat' is 0 for repeat in printable ASCII
2953*44704f69SBart Van Assche  * ('.' for non printable chars) to right of each line; 1 don't (so just
2954*44704f69SBart Van Assche  * output ASCII hex). If 'oformat' is 2 output same as 1 but any LFs are
2955*44704f69SBart Van Assche  * replaced by space (and trailing spaces are trimmed). Note that an address
2956*44704f69SBart Van Assche  * is not printed on each line preceding the hex data. Returns number of bytes
2957*44704f69SBart Van Assche  * written to 'b' excluding the trailing '\0'. The only difference between
2958*44704f69SBart Van Assche  * dStrHexStr() and hex2str() is the type of the first argument. */
2959*44704f69SBart Van Assche int
dStrHexStr(const char * str,int len,const char * leadin,int oformat,int b_len,char * b)2960*44704f69SBart Van Assche dStrHexStr(const char * str, int len, const char * leadin, int oformat,
2961*44704f69SBart Van Assche            int b_len, char * b)
2962*44704f69SBart Van Assche {
2963*44704f69SBart Van Assche     bool want_ascii = (0 == oformat);
2964*44704f69SBart Van Assche     int bpstart, bpos, k, n, prior_ascii_len;
2965*44704f69SBart Van Assche     char buff[DSHS_LINE_BLEN + 2];      /* allow for trailing null */
2966*44704f69SBart Van Assche     char a[DSHS_BPL + 1];               /* printable ASCII bytes or '.' */
2967*44704f69SBart Van Assche     const char * p = str;
2968*44704f69SBart Van Assche     const char * lf_or = (oformat > 1) ? "  " : "\n";
2969*44704f69SBart Van Assche 
2970*44704f69SBart Van Assche     if (len <= 0) {
2971*44704f69SBart Van Assche         if (b_len > 0)
2972*44704f69SBart Van Assche             b[0] = '\0';
2973*44704f69SBart Van Assche         return 0;
2974*44704f69SBart Van Assche     }
2975*44704f69SBart Van Assche     if (b_len <= 0)
2976*44704f69SBart Van Assche         return 0;
2977*44704f69SBart Van Assche     if (want_ascii) {
2978*44704f69SBart Van Assche         memset(a, ' ', DSHS_BPL);
2979*44704f69SBart Van Assche         a[DSHS_BPL] = '\0';
2980*44704f69SBart Van Assche     }
2981*44704f69SBart Van Assche     n = 0;
2982*44704f69SBart Van Assche     bpstart = 0;
2983*44704f69SBart Van Assche     if (leadin) {
2984*44704f69SBart Van Assche         if (oformat > 1)
2985*44704f69SBart Van Assche             n += sg_scnpr(b + n, b_len - n, "%s", leadin);
2986*44704f69SBart Van Assche         else {
2987*44704f69SBart Van Assche             bpstart = strlen(leadin);
2988*44704f69SBart Van Assche             /* Cap leadin at (DSHS_LINE_BLEN - 70) characters */
2989*44704f69SBart Van Assche             if (bpstart > (DSHS_LINE_BLEN - 70))
2990*44704f69SBart Van Assche                 bpstart = DSHS_LINE_BLEN - 70;
2991*44704f69SBart Van Assche         }
2992*44704f69SBart Van Assche     }
2993*44704f69SBart Van Assche     bpos = bpstart;
2994*44704f69SBart Van Assche     prior_ascii_len = bpstart + (DSHS_BPL * 3) + 1;
2995*44704f69SBart Van Assche     memset(buff, ' ', DSHS_LINE_BLEN);
2996*44704f69SBart Van Assche     buff[DSHS_LINE_BLEN] = '\0';
2997*44704f69SBart Van Assche     if (bpstart > 0)
2998*44704f69SBart Van Assche         memcpy(buff, leadin, bpstart);
2999*44704f69SBart Van Assche     for (k = 0; k < len; k++) {
3000*44704f69SBart Van Assche         uint8_t c = *p++;
3001*44704f69SBart Van Assche 
3002*44704f69SBart Van Assche         if (bpos == (bpstart + ((DSHS_BPL / 2) * 3)))
3003*44704f69SBart Van Assche             bpos++;     /* for extra space in middle of each line's hex */
3004*44704f69SBart Van Assche         sg_scnpr(buff + bpos, (int)sizeof(buff) - bpos, "%.2x",
3005*44704f69SBart Van Assche                  (int)(uint8_t)c);
3006*44704f69SBart Van Assche         buff[bpos + 2] = ' ';
3007*44704f69SBart Van Assche         if (want_ascii)
3008*44704f69SBart Van Assche             a[k % DSHS_BPL] = my_isprint(c) ? c : '.';
3009*44704f69SBart Van Assche         if ((k > 0) && (0 == ((k + 1) % DSHS_BPL))) {
3010*44704f69SBart Van Assche             trimTrailingSpaces(buff);
3011*44704f69SBart Van Assche             if (want_ascii) {
3012*44704f69SBart Van Assche                 n += sg_scnpr(b + n, b_len - n, "%-*s   %s\n",
3013*44704f69SBart Van Assche                               prior_ascii_len, buff, a);
3014*44704f69SBart Van Assche                 memset(a, ' ', DSHS_BPL);
3015*44704f69SBart Van Assche             } else
3016*44704f69SBart Van Assche                 n += sg_scnpr(b + n, b_len - n, "%s%s", buff, lf_or);
3017*44704f69SBart Van Assche             if (n >= (b_len - 1))
3018*44704f69SBart Van Assche                 goto fini;
3019*44704f69SBart Van Assche             memset(buff, ' ', DSHS_LINE_BLEN);
3020*44704f69SBart Van Assche             bpos = bpstart;
3021*44704f69SBart Van Assche             if (bpstart > 0)
3022*44704f69SBart Van Assche                 memcpy(buff, leadin, bpstart);
3023*44704f69SBart Van Assche         } else
3024*44704f69SBart Van Assche             bpos += 3;
3025*44704f69SBart Van Assche     }
3026*44704f69SBart Van Assche     if (bpos > bpstart) {
3027*44704f69SBart Van Assche         trimTrailingSpaces(buff);
3028*44704f69SBart Van Assche         if (want_ascii)
3029*44704f69SBart Van Assche             n += sg_scnpr(b + n, b_len - n, "%-*s   %s\n", prior_ascii_len,
3030*44704f69SBart Van Assche                           buff, a);
3031*44704f69SBart Van Assche         else
3032*44704f69SBart Van Assche             n += sg_scnpr(b + n, b_len - n, "%s%s", buff, lf_or);
3033*44704f69SBart Van Assche     }
3034*44704f69SBart Van Assche fini:
3035*44704f69SBart Van Assche     if (oformat > 1)
3036*44704f69SBart Van Assche         n = trimTrailingSpaces(b);
3037*44704f69SBart Van Assche     return n;
3038*44704f69SBart Van Assche }
3039*44704f69SBart Van Assche 
3040*44704f69SBart Van Assche void
hex2stdout(const uint8_t * b_str,int len,int no_ascii)3041*44704f69SBart Van Assche hex2stdout(const uint8_t * b_str, int len, int no_ascii)
3042*44704f69SBart Van Assche {
3043*44704f69SBart Van Assche     dStrHex((const char *)b_str, len, no_ascii);
3044*44704f69SBart Van Assche }
3045*44704f69SBart Van Assche 
3046*44704f69SBart Van Assche void
hex2stderr(const uint8_t * b_str,int len,int no_ascii)3047*44704f69SBart Van Assche hex2stderr(const uint8_t * b_str, int len, int no_ascii)
3048*44704f69SBart Van Assche {
3049*44704f69SBart Van Assche     dStrHexErr((const char *)b_str, len, no_ascii);
3050*44704f69SBart Van Assche }
3051*44704f69SBart Van Assche 
3052*44704f69SBart Van Assche int
hex2str(const uint8_t * b_str,int len,const char * leadin,int oformat,int b_len,char * b)3053*44704f69SBart Van Assche hex2str(const uint8_t * b_str, int len, const char * leadin, int oformat,
3054*44704f69SBart Van Assche         int b_len, char * b)
3055*44704f69SBart Van Assche {
3056*44704f69SBart Van Assche     return dStrHexStr((const char *)b_str, len, leadin, oformat, b_len, b);
3057*44704f69SBart Van Assche }
3058*44704f69SBart Van Assche 
3059*44704f69SBart Van Assche void
hex2fp(const uint8_t * b_str,int len,const char * leadin,int oformat,FILE * fp)3060*44704f69SBart Van Assche hex2fp(const uint8_t * b_str, int len, const char * leadin, int oformat,
3061*44704f69SBart Van Assche        FILE * fp)
3062*44704f69SBart Van Assche {
3063*44704f69SBart Van Assche     int k, num;
3064*44704f69SBart Van Assche     char b[800];        /* allow for 4 lines of 16 bytes (in hex) each */
3065*44704f69SBart Van Assche 
3066*44704f69SBart Van Assche     if (leadin && (strlen(leadin) > 118)) {
3067*44704f69SBart Van Assche         fprintf(fp, ">>> leadin parameter is too large\n");
3068*44704f69SBart Van Assche         return;
3069*44704f69SBart Van Assche     }
3070*44704f69SBart Van Assche     for (k = 0; k < len; k += num) {
3071*44704f69SBart Van Assche         num = ((k + 64) < len) ? 64 : (len - k);
3072*44704f69SBart Van Assche         hex2str(b_str + k, num, leadin, oformat, sizeof(b), b);
3073*44704f69SBart Van Assche         fprintf(fp, "%s", b);
3074*44704f69SBart Van Assche     }
3075*44704f69SBart Van Assche }
3076*44704f69SBart Van Assche 
3077*44704f69SBart Van Assche /* Returns true when executed on big endian machine; else returns false.
3078*44704f69SBart Van Assche  * Useful for displaying ATA identify words (which need swapping on a
3079*44704f69SBart Van Assche  * big endian machine). */
3080*44704f69SBart Van Assche bool
sg_is_big_endian()3081*44704f69SBart Van Assche sg_is_big_endian()
3082*44704f69SBart Van Assche {
3083*44704f69SBart Van Assche     union u_t {
3084*44704f69SBart Van Assche         uint16_t s;
3085*44704f69SBart Van Assche         uint8_t c[sizeof(uint16_t)];
3086*44704f69SBart Van Assche     } u;
3087*44704f69SBart Van Assche 
3088*44704f69SBart Van Assche     u.s = 0x0102;
3089*44704f69SBart Van Assche     return (u.c[0] == 0x01);     /* The lowest address contains
3090*44704f69SBart Van Assche                                     the most significant byte */
3091*44704f69SBart Van Assche }
3092*44704f69SBart Van Assche 
3093*44704f69SBart Van Assche bool
sg_all_zeros(const uint8_t * bp,int b_len)3094*44704f69SBart Van Assche sg_all_zeros(const uint8_t * bp, int b_len)
3095*44704f69SBart Van Assche {
3096*44704f69SBart Van Assche     if ((NULL == bp) || (b_len <= 0))
3097*44704f69SBart Van Assche         return false;
3098*44704f69SBart Van Assche     for (--b_len; b_len >= 0; --b_len) {
3099*44704f69SBart Van Assche         if (0x0 != bp[b_len])
3100*44704f69SBart Van Assche             return false;
3101*44704f69SBart Van Assche     }
3102*44704f69SBart Van Assche     return true;
3103*44704f69SBart Van Assche }
3104*44704f69SBart Van Assche 
3105*44704f69SBart Van Assche bool
sg_all_ffs(const uint8_t * bp,int b_len)3106*44704f69SBart Van Assche sg_all_ffs(const uint8_t * bp, int b_len)
3107*44704f69SBart Van Assche {
3108*44704f69SBart Van Assche     if ((NULL == bp) || (b_len <= 0))
3109*44704f69SBart Van Assche         return false;
3110*44704f69SBart Van Assche     for (--b_len; b_len >= 0; --b_len) {
3111*44704f69SBart Van Assche         if (0xff != bp[b_len])
3112*44704f69SBart Van Assche             return false;
3113*44704f69SBart Van Assche     }
3114*44704f69SBart Van Assche     return true;
3115*44704f69SBart Van Assche }
3116*44704f69SBart Van Assche 
3117*44704f69SBart Van Assche static uint16_t
swapb_uint16(uint16_t u)3118*44704f69SBart Van Assche swapb_uint16(uint16_t u)
3119*44704f69SBart Van Assche {
3120*44704f69SBart Van Assche     uint16_t r;
3121*44704f69SBart Van Assche 
3122*44704f69SBart Van Assche     r = (u >> 8) & 0xff;
3123*44704f69SBart Van Assche     r |= ((u & 0xff) << 8);
3124*44704f69SBart Van Assche     return r;
3125*44704f69SBart Van Assche }
3126*44704f69SBart Van Assche 
3127*44704f69SBart Van Assche /* Note the ASCII-hex output goes to stdout. [Most other output from functions
3128*44704f69SBart Van Assche  * in this file go to sg_warnings_strm (default stderr).]
3129*44704f69SBart Van Assche  * 'no_ascii' allows for 3 output types:
3130*44704f69SBart Van Assche  *     > 0     each line has address then up to 8 ASCII-hex 16 bit words
3131*44704f69SBart Van Assche  *     = 0     in addition, the ASCI bytes pairs are listed to the right
3132*44704f69SBart Van Assche  *     = -1    only the ASCII-hex words are listed (i.e. without address)
3133*44704f69SBart Van Assche  *     = -2    only the ASCII-hex words, formatted for "hdparm --Istdin"
3134*44704f69SBart Van Assche  *     < -2    same as -1
3135*44704f69SBart Van Assche  * If 'swapb' is true then bytes in each word swapped. Needs to be set
3136*44704f69SBart Van Assche  * for ATA IDENTIFY DEVICE response on big-endian machines. */
3137*44704f69SBart Van Assche void
dWordHex(const uint16_t * words,int num,int no_ascii,bool swapb)3138*44704f69SBart Van Assche dWordHex(const uint16_t* words, int num, int no_ascii, bool swapb)
3139*44704f69SBart Van Assche {
3140*44704f69SBart Van Assche     const uint16_t * p = words;
3141*44704f69SBart Van Assche     uint16_t c;
3142*44704f69SBart Van Assche     char buff[82];
3143*44704f69SBart Van Assche     uint8_t upp, low;
3144*44704f69SBart Van Assche     int a = 0;
3145*44704f69SBart Van Assche     const int bpstart = 3;
3146*44704f69SBart Van Assche     const int cpstart = 52;
3147*44704f69SBart Van Assche     int cpos = cpstart;
3148*44704f69SBart Van Assche     int bpos = bpstart;
3149*44704f69SBart Van Assche     int i, k, blen;
3150*44704f69SBart Van Assche 
3151*44704f69SBart Van Assche     if (num <= 0)
3152*44704f69SBart Van Assche         return;
3153*44704f69SBart Van Assche     blen = (int)sizeof(buff);
3154*44704f69SBart Van Assche     memset(buff, ' ', 80);
3155*44704f69SBart Van Assche     buff[80] = '\0';
3156*44704f69SBart Van Assche     if (no_ascii < 0) {
3157*44704f69SBart Van Assche         for (k = 0; k < num; k++) {
3158*44704f69SBart Van Assche             c = *p++;
3159*44704f69SBart Van Assche             if (swapb)
3160*44704f69SBart Van Assche                 c = swapb_uint16(c);
3161*44704f69SBart Van Assche             bpos += 5;
3162*44704f69SBart Van Assche             sg_scnpr(buff + bpos, blen - bpos, "%.4x", (my_uint)c);
3163*44704f69SBart Van Assche             buff[bpos + 4] = ' ';
3164*44704f69SBart Van Assche             if ((k > 0) && (0 == ((k + 1) % 8))) {
3165*44704f69SBart Van Assche                 if (-2 == no_ascii)
3166*44704f69SBart Van Assche                     printf("%.39s\n", buff +8);
3167*44704f69SBart Van Assche                 else
3168*44704f69SBart Van Assche                     printf("%.47s\n", buff);
3169*44704f69SBart Van Assche                 bpos = bpstart;
3170*44704f69SBart Van Assche                 memset(buff, ' ', 80);
3171*44704f69SBart Van Assche             }
3172*44704f69SBart Van Assche         }
3173*44704f69SBart Van Assche         if (bpos > bpstart) {
3174*44704f69SBart Van Assche             if (-2 == no_ascii)
3175*44704f69SBart Van Assche                 printf("%.39s\n", buff +8);
3176*44704f69SBart Van Assche             else
3177*44704f69SBart Van Assche                 printf("%.47s\n", buff);
3178*44704f69SBart Van Assche         }
3179*44704f69SBart Van Assche         return;
3180*44704f69SBart Van Assche     }
3181*44704f69SBart Van Assche     /* no_ascii>=0, start each line with address (offset) */
3182*44704f69SBart Van Assche     k = sg_scnpr(buff + 1, blen - 1, "%.2x", a);
3183*44704f69SBart Van Assche     buff[k + 1] = ' ';
3184*44704f69SBart Van Assche 
3185*44704f69SBart Van Assche     for (i = 0; i < num; i++) {
3186*44704f69SBart Van Assche         c = *p++;
3187*44704f69SBart Van Assche         if (swapb)
3188*44704f69SBart Van Assche             c = swapb_uint16(c);
3189*44704f69SBart Van Assche         bpos += 5;
3190*44704f69SBart Van Assche         sg_scnpr(buff + bpos, blen - bpos, "%.4x", (my_uint)c);
3191*44704f69SBart Van Assche         buff[bpos + 4] = ' ';
3192*44704f69SBart Van Assche         if (no_ascii) {
3193*44704f69SBart Van Assche             buff[cpos++] = ' ';
3194*44704f69SBart Van Assche             buff[cpos++] = ' ';
3195*44704f69SBart Van Assche             buff[cpos++] = ' ';
3196*44704f69SBart Van Assche         } else {
3197*44704f69SBart Van Assche             upp = (c >> 8) & 0xff;
3198*44704f69SBart Van Assche             low = c & 0xff;
3199*44704f69SBart Van Assche             if (! my_isprint(upp))
3200*44704f69SBart Van Assche                 upp = '.';
3201*44704f69SBart Van Assche             buff[cpos++] = upp;
3202*44704f69SBart Van Assche             if (! my_isprint(low))
3203*44704f69SBart Van Assche                 low = '.';
3204*44704f69SBart Van Assche             buff[cpos++] = low;
3205*44704f69SBart Van Assche             buff[cpos++] = ' ';
3206*44704f69SBart Van Assche         }
3207*44704f69SBart Van Assche         if (cpos > (cpstart + 23)) {
3208*44704f69SBart Van Assche             printf("%.76s\n", buff);
3209*44704f69SBart Van Assche             bpos = bpstart;
3210*44704f69SBart Van Assche             cpos = cpstart;
3211*44704f69SBart Van Assche             a += 8;
3212*44704f69SBart Van Assche             memset(buff, ' ', 80);
3213*44704f69SBart Van Assche             k = sg_scnpr(buff + 1, blen - 1, "%.2x", a);
3214*44704f69SBart Van Assche             buff[k + 1] = ' ';
3215*44704f69SBart Van Assche         }
3216*44704f69SBart Van Assche     }
3217*44704f69SBart Van Assche     if (cpos > cpstart)
3218*44704f69SBart Van Assche         printf("%.76s\n", buff);
3219*44704f69SBart Van Assche }
3220*44704f69SBart Van Assche 
3221*44704f69SBart Van Assche /* If the number in 'buf' can not be decoded or the multiplier is unknown
3222*44704f69SBart Van Assche  * then -1 is returned. Accepts a hex prefix (0x or 0X) or a decimal
3223*44704f69SBart Van Assche  * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)).
3224*44704f69SBart Van Assche  * Main (SI) multipliers supported: K, M, G. Ignore leading spaces and
3225*44704f69SBart Van Assche  * tabs; accept comma, hyphen, space, tab and hash as terminator.
3226*44704f69SBart Van Assche  * Handles zero and positive values up to 2**31-1 .
3227*44704f69SBart Van Assche  * Experimental: left argument (must in with hexadecimal digit) added
3228*44704f69SBart Van Assche  * to, or multiplied, by right argument. No embedded spaces.
3229*44704f69SBart Van Assche  * Examples: '3+1k' (evaluates to 1027) and '0x34+1m'. */
3230*44704f69SBart Van Assche int
sg_get_num(const char * buf)3231*44704f69SBart Van Assche sg_get_num(const char * buf)
3232*44704f69SBart Van Assche {
3233*44704f69SBart Van Assche     bool is_hex = false;
3234*44704f69SBart Van Assche     int res, num, n, len;
3235*44704f69SBart Van Assche     unsigned int unum;
3236*44704f69SBart Van Assche     char * cp;
3237*44704f69SBart Van Assche     const char * b;
3238*44704f69SBart Van Assche     const char * b2p;
3239*44704f69SBart Van Assche     char c = 'c';
3240*44704f69SBart Van Assche     char c2 = '\0';     /* keep static checker happy */
3241*44704f69SBart Van Assche     char c3 = '\0';     /* keep static checker happy */
3242*44704f69SBart Van Assche     char lb[16];
3243*44704f69SBart Van Assche 
3244*44704f69SBart Van Assche     if ((NULL == buf) || ('\0' == buf[0]))
3245*44704f69SBart Van Assche         return -1;
3246*44704f69SBart Van Assche     len = strlen(buf);
3247*44704f69SBart Van Assche     n = strspn(buf, " \t");
3248*44704f69SBart Van Assche     if (n > 0) {
3249*44704f69SBart Van Assche         if (n == len)
3250*44704f69SBart Van Assche             return -1;
3251*44704f69SBart Van Assche         buf += n;
3252*44704f69SBart Van Assche         len -= n;
3253*44704f69SBart Van Assche     }
3254*44704f69SBart Van Assche     /* following hack to keep C++ happy */
3255*44704f69SBart Van Assche     cp = strpbrk((char *)buf, " \t,#-");
3256*44704f69SBart Van Assche     if (cp) {
3257*44704f69SBart Van Assche         len = cp - buf;
3258*44704f69SBart Van Assche         n = (int)sizeof(lb) - 1;
3259*44704f69SBart Van Assche         len = (len < n) ? len : n;
3260*44704f69SBart Van Assche         memcpy(lb, buf, len);
3261*44704f69SBart Van Assche         lb[len] = '\0';
3262*44704f69SBart Van Assche         b = lb;
3263*44704f69SBart Van Assche     } else
3264*44704f69SBart Van Assche         b = buf;
3265*44704f69SBart Van Assche 
3266*44704f69SBart Van Assche     b2p = b;
3267*44704f69SBart Van Assche     if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) {
3268*44704f69SBart Van Assche         res = sscanf(b + 2, "%x%c", &unum, &c);
3269*44704f69SBart Van Assche         num = unum;
3270*44704f69SBart Van Assche         is_hex = true;
3271*44704f69SBart Van Assche         b2p = b + 2;
3272*44704f69SBart Van Assche     } else if ('H' == toupper((int)b[len - 1])) {
3273*44704f69SBart Van Assche         res = sscanf(b, "%x", &unum);
3274*44704f69SBart Van Assche         num = unum;
3275*44704f69SBart Van Assche     } else
3276*44704f69SBart Van Assche         res = sscanf(b, "%d%c%c%c", &num, &c, &c2, &c3);
3277*44704f69SBart Van Assche 
3278*44704f69SBart Van Assche     if (res < 1)
3279*44704f69SBart Van Assche         return -1;
3280*44704f69SBart Van Assche     else if (1 == res)
3281*44704f69SBart Van Assche         return num;
3282*44704f69SBart Van Assche     else {
3283*44704f69SBart Van Assche         c = toupper((int)c);
3284*44704f69SBart Van Assche         if (is_hex) {
3285*44704f69SBart Van Assche             if (! ((c == '+') || (c == 'X')))
3286*44704f69SBart Van Assche                 return -1;
3287*44704f69SBart Van Assche         }
3288*44704f69SBart Van Assche         if (res > 2)
3289*44704f69SBart Van Assche             c2 = toupper((int)c2);
3290*44704f69SBart Van Assche         if (res > 3)
3291*44704f69SBart Van Assche             c3 = toupper((int)c3);
3292*44704f69SBart Van Assche 
3293*44704f69SBart Van Assche         switch (c) {
3294*44704f69SBart Van Assche         case 'C':
3295*44704f69SBart Van Assche             return num;
3296*44704f69SBart Van Assche         case 'W':
3297*44704f69SBart Van Assche             return num * 2;
3298*44704f69SBart Van Assche         case 'B':
3299*44704f69SBart Van Assche             return num * 512;
3300*44704f69SBart Van Assche         case 'K':
3301*44704f69SBart Van Assche             if (2 == res)
3302*44704f69SBart Van Assche                 return num * 1024;
3303*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3304*44704f69SBart Van Assche                 return num * 1000;
3305*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3306*44704f69SBart Van Assche                 return num * 1024;
3307*44704f69SBart Van Assche             return -1;
3308*44704f69SBart Van Assche         case 'M':
3309*44704f69SBart Van Assche             if (2 == res)
3310*44704f69SBart Van Assche                 return num * 1048576;
3311*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3312*44704f69SBart Van Assche                 return num * 1000000;
3313*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3314*44704f69SBart Van Assche                 return num * 1048576;
3315*44704f69SBart Van Assche             return -1;
3316*44704f69SBart Van Assche         case 'G':
3317*44704f69SBart Van Assche             if (2 == res)
3318*44704f69SBart Van Assche                 return num * 1073741824;
3319*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3320*44704f69SBart Van Assche                 return num * 1000000000;
3321*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3322*44704f69SBart Van Assche                 return num * 1073741824;
3323*44704f69SBart Van Assche             return -1;
3324*44704f69SBart Van Assche         case 'X':       /* experimental: multiplication */
3325*44704f69SBart Van Assche             /* left argument must end with hexadecimal digit */
3326*44704f69SBart Van Assche             cp = (char *)strchr(b2p, 'x');
3327*44704f69SBart Van Assche             if (NULL == cp)
3328*44704f69SBart Van Assche                 cp = (char *)strchr(b2p, 'X');
3329*44704f69SBart Van Assche             if (cp) {
3330*44704f69SBart Van Assche                 n = sg_get_num(cp + 1);
3331*44704f69SBart Van Assche                 if (-1 != n)
3332*44704f69SBart Van Assche                     return num * n;
3333*44704f69SBart Van Assche             }
3334*44704f69SBart Van Assche             return -1;
3335*44704f69SBart Van Assche         case '+':       /* experimental: addition */
3336*44704f69SBart Van Assche             /* left argument must end with hexadecimal digit */
3337*44704f69SBart Van Assche             cp = (char *)strchr(b2p, '+');
3338*44704f69SBart Van Assche             if (cp) {
3339*44704f69SBart Van Assche                 n = sg_get_num(cp + 1);
3340*44704f69SBart Van Assche                 if (-1 != n)
3341*44704f69SBart Van Assche                     return num + n;
3342*44704f69SBart Van Assche             }
3343*44704f69SBart Van Assche             return -1;
3344*44704f69SBart Van Assche         default:
3345*44704f69SBart Van Assche             pr2ws("unrecognized multiplier\n");
3346*44704f69SBart Van Assche             return -1;
3347*44704f69SBart Van Assche         }
3348*44704f69SBart Van Assche     }
3349*44704f69SBart Van Assche }
3350*44704f69SBart Van Assche 
3351*44704f69SBart Van Assche /* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
3352*44704f69SBart Van Assche  * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
3353*44704f69SBart Van Assche  * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"),
3354*44704f69SBart Van Assche  * a whitespace or newline as terminator. */
3355*44704f69SBart Van Assche int
sg_get_num_nomult(const char * buf)3356*44704f69SBart Van Assche sg_get_num_nomult(const char * buf)
3357*44704f69SBart Van Assche {
3358*44704f69SBart Van Assche     int res, len, num;
3359*44704f69SBart Van Assche     unsigned int unum;
3360*44704f69SBart Van Assche     char * commap;
3361*44704f69SBart Van Assche 
3362*44704f69SBart Van Assche     if ((NULL == buf) || ('\0' == buf[0]))
3363*44704f69SBart Van Assche         return -1;
3364*44704f69SBart Van Assche     len = strlen(buf);
3365*44704f69SBart Van Assche     commap = (char *)strchr(buf + 1, ',');
3366*44704f69SBart Van Assche     if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
3367*44704f69SBart Van Assche         res = sscanf(buf + 2, "%x", &unum);
3368*44704f69SBart Van Assche         num = unum;
3369*44704f69SBart Van Assche     } else if (commap && ('H' == toupper((int)*(commap - 1)))) {
3370*44704f69SBart Van Assche         res = sscanf(buf, "%x", &unum);
3371*44704f69SBart Van Assche         num = unum;
3372*44704f69SBart Van Assche     } else if ((NULL == commap) && ('H' == toupper((int)buf[len - 1]))) {
3373*44704f69SBart Van Assche         res = sscanf(buf, "%x", &unum);
3374*44704f69SBart Van Assche         num = unum;
3375*44704f69SBart Van Assche     } else
3376*44704f69SBart Van Assche         res = sscanf(buf, "%d", &num);
3377*44704f69SBart Van Assche     if (1 == res)
3378*44704f69SBart Van Assche         return num;
3379*44704f69SBart Van Assche     else
3380*44704f69SBart Van Assche         return -1;
3381*44704f69SBart Van Assche }
3382*44704f69SBart Van Assche 
3383*44704f69SBart Van Assche /* If the number in 'buf' can not be decoded or the multiplier is unknown
3384*44704f69SBart Van Assche  * then -1LL is returned. Accepts a hex prefix (0x or 0X), hex suffix
3385*44704f69SBart Van Assche  * (h or H), or a decimal multiplier suffix (as per GNU's dd (since 2002:
3386*44704f69SBart Van Assche  * SI and IEC 60027-2)).  Main (SI) multipliers supported: K, M, G, T, P
3387*44704f69SBart Van Assche  * and E. Ignore leading spaces and tabs; accept comma, hyphen, space, tab
3388*44704f69SBart Van Assche  * and hash as terminator. Handles zero and positive values up to 2**63-1 .
3389*44704f69SBart Van Assche  * Experimental: left argument (must in with hexadecimal digit) added
3390*44704f69SBart Van Assche  * to, or multiplied by right argument. No embedded spaces.
3391*44704f69SBart Van Assche  * Examples: '3+1k' (evaluates to 1027) and '0x34+1m'. */
3392*44704f69SBart Van Assche int64_t
sg_get_llnum(const char * buf)3393*44704f69SBart Van Assche sg_get_llnum(const char * buf)
3394*44704f69SBart Van Assche {
3395*44704f69SBart Van Assche     bool is_hex = false;
3396*44704f69SBart Van Assche     int res, len, n;
3397*44704f69SBart Van Assche     int64_t num, ll;
3398*44704f69SBart Van Assche     uint64_t unum;
3399*44704f69SBart Van Assche     char * cp;
3400*44704f69SBart Van Assche     const char * b;
3401*44704f69SBart Van Assche     const char * b2p;
3402*44704f69SBart Van Assche     char c = 'c';
3403*44704f69SBart Van Assche     char c2 = '\0';     /* keep static checker happy */
3404*44704f69SBart Van Assche     char c3 = '\0';     /* keep static checker happy */
3405*44704f69SBart Van Assche     char lb[32];
3406*44704f69SBart Van Assche 
3407*44704f69SBart Van Assche     if ((NULL == buf) || ('\0' == buf[0]))
3408*44704f69SBart Van Assche         return -1LL;
3409*44704f69SBart Van Assche     len = strlen(buf);
3410*44704f69SBart Van Assche     n = strspn(buf, " \t");
3411*44704f69SBart Van Assche     if (n > 0) {
3412*44704f69SBart Van Assche         if (n == len)
3413*44704f69SBart Van Assche             return -1LL;
3414*44704f69SBart Van Assche         buf += n;
3415*44704f69SBart Van Assche         len -= n;
3416*44704f69SBart Van Assche     }
3417*44704f69SBart Van Assche     /* following cast hack to keep C++ happy */
3418*44704f69SBart Van Assche     cp = strpbrk((char *)buf, " \t,#-");
3419*44704f69SBart Van Assche     if (cp) {
3420*44704f69SBart Van Assche         len = cp - buf;
3421*44704f69SBart Van Assche         n = (int)sizeof(lb) - 1;
3422*44704f69SBart Van Assche         len = (len < n) ? len : n;
3423*44704f69SBart Van Assche         memcpy(lb, buf, len);
3424*44704f69SBart Van Assche         lb[len] = '\0';
3425*44704f69SBart Van Assche         b = lb;
3426*44704f69SBart Van Assche     } else
3427*44704f69SBart Van Assche         b = buf;
3428*44704f69SBart Van Assche 
3429*44704f69SBart Van Assche     b2p = b;
3430*44704f69SBart Van Assche     if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) {
3431*44704f69SBart Van Assche         res = sscanf(b + 2, "%" SCNx64 "%c", &unum, &c);
3432*44704f69SBart Van Assche         num = unum;
3433*44704f69SBart Van Assche         is_hex = true;
3434*44704f69SBart Van Assche         b2p = b + 2;
3435*44704f69SBart Van Assche     } else if ('H' == toupper((int)b[len - 1])) {
3436*44704f69SBart Van Assche         res = sscanf(b, "%" SCNx64 , &unum);
3437*44704f69SBart Van Assche         num = unum;
3438*44704f69SBart Van Assche     } else
3439*44704f69SBart Van Assche         res = sscanf(b, "%" SCNd64 "%c%c%c", &num, &c, &c2, &c3);
3440*44704f69SBart Van Assche 
3441*44704f69SBart Van Assche     if (res < 1)
3442*44704f69SBart Van Assche         return -1LL;
3443*44704f69SBart Van Assche     else if (1 == res)
3444*44704f69SBart Van Assche         return num;
3445*44704f69SBart Van Assche     else {
3446*44704f69SBart Van Assche         c = toupper((int)c);
3447*44704f69SBart Van Assche         if (is_hex) {
3448*44704f69SBart Van Assche             if (! ((c == '+') || (c == 'X')))
3449*44704f69SBart Van Assche                 return -1;
3450*44704f69SBart Van Assche         }
3451*44704f69SBart Van Assche         if (res > 2)
3452*44704f69SBart Van Assche             c2 = toupper((int)c2);
3453*44704f69SBart Van Assche         if (res > 3)
3454*44704f69SBart Van Assche             c3 = toupper((int)c3);
3455*44704f69SBart Van Assche 
3456*44704f69SBart Van Assche         switch (c) {
3457*44704f69SBart Van Assche         case 'C':
3458*44704f69SBart Van Assche             return num;
3459*44704f69SBart Van Assche         case 'W':
3460*44704f69SBart Van Assche             return num * 2;
3461*44704f69SBart Van Assche         case 'B':
3462*44704f69SBart Van Assche             return num * 512;
3463*44704f69SBart Van Assche         case 'K':       /* kilo or kibi */
3464*44704f69SBart Van Assche             if (2 == res)
3465*44704f69SBart Van Assche                 return num * 1024;
3466*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3467*44704f69SBart Van Assche                 return num * 1000;
3468*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3469*44704f69SBart Van Assche                 return num * 1024;      /* KiB */
3470*44704f69SBart Van Assche             return -1LL;
3471*44704f69SBart Van Assche         case 'M':       /* mega or mebi */
3472*44704f69SBart Van Assche             if (2 == res)
3473*44704f69SBart Van Assche                 return num * 1048576;   /* M */
3474*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3475*44704f69SBart Van Assche                 return num * 1000000;   /* MB */
3476*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3477*44704f69SBart Van Assche                 return num * 1048576;   /* MiB */
3478*44704f69SBart Van Assche             return -1LL;
3479*44704f69SBart Van Assche         case 'G':       /* giga or gibi */
3480*44704f69SBart Van Assche             if (2 == res)
3481*44704f69SBart Van Assche                 return num * 1073741824;        /* G */
3482*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3483*44704f69SBart Van Assche                 return num * 1000000000;        /* GB */
3484*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3485*44704f69SBart Van Assche                 return num * 1073741824;        /* GiB */
3486*44704f69SBart Van Assche             return -1LL;
3487*44704f69SBart Van Assche         case 'T':       /* tera or tebi */
3488*44704f69SBart Van Assche             if (2 == res)
3489*44704f69SBart Van Assche                 return num * 1099511627776LL;   /* T */
3490*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3491*44704f69SBart Van Assche                 return num * 1000000000000LL;   /* TB */
3492*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3493*44704f69SBart Van Assche                 return num * 1099511627776LL;   /* TiB */
3494*44704f69SBart Van Assche             return -1LL;
3495*44704f69SBart Van Assche         case 'P':       /* peta or pebi */
3496*44704f69SBart Van Assche             if (2 == res)
3497*44704f69SBart Van Assche                 return num * 1099511627776LL * 1024;
3498*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3499*44704f69SBart Van Assche                 return num * 1000000000000LL * 1000;
3500*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3501*44704f69SBart Van Assche                 return num * 1099511627776LL * 1024;
3502*44704f69SBart Van Assche             return -1LL;
3503*44704f69SBart Van Assche         case 'E':       /* exa or exbi */
3504*44704f69SBart Van Assche             if (2 == res)
3505*44704f69SBart Van Assche                 return num * 1099511627776LL * 1024 * 1024;
3506*44704f69SBart Van Assche             if (('B' == c2) || ('D' == c2))
3507*44704f69SBart Van Assche                 return num * 1000000000000LL * 1000 * 1000;
3508*44704f69SBart Van Assche             if (('I' == c2) && (4 == res) && ('B' == c3))
3509*44704f69SBart Van Assche                 return num * 1099511627776LL * 1024 * 1024;
3510*44704f69SBart Van Assche             return -1LL;
3511*44704f69SBart Van Assche         case 'X':       /* experimental: decimal (left arg) multiplication */
3512*44704f69SBart Van Assche             cp = (char *)strchr(b2p, 'x');
3513*44704f69SBart Van Assche             if (NULL == cp)
3514*44704f69SBart Van Assche                 cp = (char *)strchr(b2p, 'X');
3515*44704f69SBart Van Assche             if (cp) {
3516*44704f69SBart Van Assche                 ll = sg_get_llnum(cp + 1);
3517*44704f69SBart Van Assche                 if (-1LL != ll)
3518*44704f69SBart Van Assche                     return num * ll;
3519*44704f69SBart Van Assche             }
3520*44704f69SBart Van Assche             return -1LL;
3521*44704f69SBart Van Assche         case '+':       /* experimental: decimal (left arg) addition */
3522*44704f69SBart Van Assche             cp = (char *)strchr(b2p, '+');
3523*44704f69SBart Van Assche             if (cp) {
3524*44704f69SBart Van Assche                 ll = sg_get_llnum(cp + 1);
3525*44704f69SBart Van Assche                 if (-1LL != ll)
3526*44704f69SBart Van Assche                     return num + ll;
3527*44704f69SBart Van Assche             }
3528*44704f69SBart Van Assche             return -1LL;
3529*44704f69SBart Van Assche         default:
3530*44704f69SBart Van Assche             pr2ws("unrecognized multiplier\n");
3531*44704f69SBart Van Assche             return -1LL;
3532*44704f69SBart Van Assche         }
3533*44704f69SBart Van Assche     }
3534*44704f69SBart Van Assche }
3535*44704f69SBart Van Assche 
3536*44704f69SBart Van Assche /* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
3537*44704f69SBart Van Assche  * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
3538*44704f69SBart Van Assche  * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"),
3539*44704f69SBart Van Assche  * a whitespace or newline as terminator. Only decimal numbers can represent
3540*44704f69SBart Van Assche  * negative numbers and '-1' must be treated separately. */
3541*44704f69SBart Van Assche int64_t
sg_get_llnum_nomult(const char * buf)3542*44704f69SBart Van Assche sg_get_llnum_nomult(const char * buf)
3543*44704f69SBart Van Assche {
3544*44704f69SBart Van Assche     int res, len;
3545*44704f69SBart Van Assche     int64_t num;
3546*44704f69SBart Van Assche     uint64_t unum;
3547*44704f69SBart Van Assche 
3548*44704f69SBart Van Assche     if ((NULL == buf) || ('\0' == buf[0]))
3549*44704f69SBart Van Assche         return -1;
3550*44704f69SBart Van Assche     len = strlen(buf);
3551*44704f69SBart Van Assche     if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
3552*44704f69SBart Van Assche         res = sscanf(buf + 2, "%" SCNx64 "", &unum);
3553*44704f69SBart Van Assche         num = unum;
3554*44704f69SBart Van Assche     } else if ('H' == toupper(buf[len - 1])) {
3555*44704f69SBart Van Assche         res = sscanf(buf, "%" SCNx64 "", &unum);
3556*44704f69SBart Van Assche         num = unum;
3557*44704f69SBart Van Assche     } else
3558*44704f69SBart Van Assche         res = sscanf(buf, "%" SCNd64 "", &num);
3559*44704f69SBart Van Assche     return (1 == res) ? num : -1;
3560*44704f69SBart Van Assche }
3561*44704f69SBart Van Assche 
3562*44704f69SBart Van Assche #define MAX_NUM_ASCII_LINES 1048576
3563*44704f69SBart Van Assche 
3564*44704f69SBart Van Assche /* Read ASCII hex bytes or binary from fname (a file named '-' taken as
3565*44704f69SBart Van Assche  * stdin). If reading ASCII hex then there should be either one entry per
3566*44704f69SBart Van Assche  * line or a comma, space, hyphen or tab separated list of bytes. If no_space
3567*44704f69SBart Van Assche  * is set then a string of ACSII hex digits is expected, 2 perbyte.
3568*44704f69SBart Van Assche  * Everything from and including a '#' on a line is ignored. Returns 0 if ok,
3569*44704f69SBart Van Assche  * or an error code. If the error code is SG_LIB_LBA_OUT_OF_RANGE then mp_arr
3570*44704f69SBart Van Assche  * would be exceeded and both mp_arr and mp_arr_len are written to.
3571*44704f69SBart Van Assche  * The max_arr_len_and argument may carry extra information: when it
3572*44704f69SBart Van Assche  * is negative its absolute value is used for the maximum number of bytes to
3573*44704f69SBart Van Assche  * write to mp_arr _and_ the first hexadecimal value on each line is skipped.
3574*44704f69SBart Van Assche  * Many hexadecimal output programs place a running address (index) as the
3575*44704f69SBart Van Assche  * first field on each line. When as_binary and/or no_space are true, the
3576*44704f69SBart Van Assche  * absolute value of max_arr_len_and is used. */
3577*44704f69SBart Van Assche int
sg_f2hex_arr(const char * fname,bool as_binary,bool no_space,uint8_t * mp_arr,int * mp_arr_len,int max_arr_len_and)3578*44704f69SBart Van Assche sg_f2hex_arr(const char * fname, bool as_binary, bool no_space,
3579*44704f69SBart Van Assche              uint8_t * mp_arr, int * mp_arr_len, int max_arr_len_and)
3580*44704f69SBart Van Assche {
3581*44704f69SBart Van Assche     bool has_stdin, split_line, skip_first, redo_first;
3582*44704f69SBart Van Assche     int fn_len, in_len, k, j, m, fd, err, max_arr_len;
3583*44704f69SBart Van Assche     int off = 0;
3584*44704f69SBart Van Assche     int ret = 0;
3585*44704f69SBart Van Assche     unsigned int h;
3586*44704f69SBart Van Assche     const char * lcp;
3587*44704f69SBart Van Assche     FILE * fp = NULL;
3588*44704f69SBart Van Assche     struct stat a_stat;
3589*44704f69SBart Van Assche     char line[512];
3590*44704f69SBart Van Assche     char carry_over[4];
3591*44704f69SBart Van Assche 
3592*44704f69SBart Van Assche     if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len)) {
3593*44704f69SBart Van Assche         pr2ws("%s: bad arguments\n", __func__);
3594*44704f69SBart Van Assche         return SG_LIB_LOGIC_ERROR;
3595*44704f69SBart Van Assche     }
3596*44704f69SBart Van Assche     if (max_arr_len_and < 0) {
3597*44704f69SBart Van Assche         skip_first = true;
3598*44704f69SBart Van Assche         max_arr_len = -max_arr_len_and;
3599*44704f69SBart Van Assche     } else {
3600*44704f69SBart Van Assche         skip_first = false;
3601*44704f69SBart Van Assche         max_arr_len = max_arr_len_and;
3602*44704f69SBart Van Assche     }
3603*44704f69SBart Van Assche     fn_len = strlen(fname);
3604*44704f69SBart Van Assche     if (0 == fn_len)
3605*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
3606*44704f69SBart Van Assche     has_stdin = ((1 == fn_len) && ('-' == fname[0]));   /* read from stdin */
3607*44704f69SBart Van Assche     if (as_binary) {
3608*44704f69SBart Van Assche         if (has_stdin)
3609*44704f69SBart Van Assche             fd = STDIN_FILENO;
3610*44704f69SBart Van Assche         else {
3611*44704f69SBart Van Assche             fd = open(fname, O_RDONLY);
3612*44704f69SBart Van Assche             if (fd < 0) {
3613*44704f69SBart Van Assche                 err = errno;
3614*44704f69SBart Van Assche                 pr2ws("unable to open binary file %s: %s\n", fname,
3615*44704f69SBart Van Assche                          safe_strerror(err));
3616*44704f69SBart Van Assche                 return sg_convert_errno(err);
3617*44704f69SBart Van Assche             }
3618*44704f69SBart Van Assche         }
3619*44704f69SBart Van Assche         k = read(fd, mp_arr, max_arr_len);
3620*44704f69SBart Van Assche         if (k <= 0) {
3621*44704f69SBart Van Assche             if (0 == k) {
3622*44704f69SBart Van Assche                 ret = SG_LIB_FILE_ERROR;
3623*44704f69SBart Van Assche                 pr2ws("read 0 bytes from binary file %s\n", fname);
3624*44704f69SBart Van Assche             } else {
3625*44704f69SBart Van Assche                 ret = sg_convert_errno(errno);
3626*44704f69SBart Van Assche                 pr2ws("read from binary file %s: %s\n", fname,
3627*44704f69SBart Van Assche                         safe_strerror(errno));
3628*44704f69SBart Van Assche             }
3629*44704f69SBart Van Assche         } else if ((k < max_arr_len) && (0 == fstat(fd, &a_stat)) &&
3630*44704f69SBart Van Assche                    S_ISFIFO(a_stat.st_mode)) {
3631*44704f69SBart Van Assche             /* pipe; keep reading till error or 0 read */
3632*44704f69SBart Van Assche             while (k < max_arr_len) {
3633*44704f69SBart Van Assche                 m = read(fd, mp_arr + k, max_arr_len - k);
3634*44704f69SBart Van Assche                 if (0 == m)
3635*44704f69SBart Van Assche                    break;
3636*44704f69SBart Van Assche                 if (m < 0) {
3637*44704f69SBart Van Assche                     err = errno;
3638*44704f69SBart Van Assche                     pr2ws("read from binary pipe %s: %s\n", fname,
3639*44704f69SBart Van Assche                             safe_strerror(err));
3640*44704f69SBart Van Assche                     ret = sg_convert_errno(err);
3641*44704f69SBart Van Assche                     break;
3642*44704f69SBart Van Assche                 }
3643*44704f69SBart Van Assche                 k += m;
3644*44704f69SBart Van Assche             }
3645*44704f69SBart Van Assche         }
3646*44704f69SBart Van Assche         if (k >= 0)
3647*44704f69SBart Van Assche             *mp_arr_len = k;
3648*44704f69SBart Van Assche         if ((fd >= 0) && (! has_stdin))
3649*44704f69SBart Van Assche             close(fd);
3650*44704f69SBart Van Assche         return ret;
3651*44704f69SBart Van Assche     }
3652*44704f69SBart Van Assche 
3653*44704f69SBart Van Assche     /* So read the file as ASCII hex */
3654*44704f69SBart Van Assche     if (has_stdin)
3655*44704f69SBart Van Assche         fp = stdin;
3656*44704f69SBart Van Assche     else {
3657*44704f69SBart Van Assche         fp = fopen(fname, "r");
3658*44704f69SBart Van Assche         if (NULL == fp) {
3659*44704f69SBart Van Assche             err = errno;
3660*44704f69SBart Van Assche             pr2ws("Unable to open %s for reading: %s\n", fname,
3661*44704f69SBart Van Assche                     safe_strerror(err));
3662*44704f69SBart Van Assche             ret = sg_convert_errno(err);
3663*44704f69SBart Van Assche             goto fini;
3664*44704f69SBart Van Assche         }
3665*44704f69SBart Van Assche     }
3666*44704f69SBart Van Assche 
3667*44704f69SBart Van Assche     carry_over[0] = 0;
3668*44704f69SBart Van Assche     for (j = 0; j < MAX_NUM_ASCII_LINES; ++j) {
3669*44704f69SBart Van Assche         if (NULL == fgets(line, sizeof(line), fp))
3670*44704f69SBart Van Assche             break;
3671*44704f69SBart Van Assche         in_len = strlen(line);
3672*44704f69SBart Van Assche         if (in_len > 0) {
3673*44704f69SBart Van Assche             if ('\n' == line[in_len - 1]) {
3674*44704f69SBart Van Assche                 --in_len;
3675*44704f69SBart Van Assche                 line[in_len] = '\0';
3676*44704f69SBart Van Assche                 split_line = false;
3677*44704f69SBart Van Assche             } else
3678*44704f69SBart Van Assche                 split_line = true;
3679*44704f69SBart Van Assche         }
3680*44704f69SBart Van Assche         if (in_len < 1) {
3681*44704f69SBart Van Assche             carry_over[0] = 0;
3682*44704f69SBart Van Assche             continue;
3683*44704f69SBart Van Assche         }
3684*44704f69SBart Van Assche         if (carry_over[0]) {
3685*44704f69SBart Van Assche             if (isxdigit(line[0])) {
3686*44704f69SBart Van Assche                 carry_over[1] = line[0];
3687*44704f69SBart Van Assche                 carry_over[2] = '\0';
3688*44704f69SBart Van Assche                 if (1 == sscanf(carry_over, "%4x", &h)) {
3689*44704f69SBart Van Assche                     if (off > 0) {
3690*44704f69SBart Van Assche                         if (off > max_arr_len) {
3691*44704f69SBart Van Assche                             pr2ws("%s: array length exceeded\n", __func__);
3692*44704f69SBart Van Assche                             ret = SG_LIB_LBA_OUT_OF_RANGE;
3693*44704f69SBart Van Assche                             *mp_arr_len = max_arr_len;
3694*44704f69SBart Van Assche                             goto fini;
3695*44704f69SBart Van Assche                         } else
3696*44704f69SBart Van Assche                             mp_arr[off - 1] = h; /* back up and overwrite */
3697*44704f69SBart Van Assche                     }
3698*44704f69SBart Van Assche                 } else {
3699*44704f69SBart Van Assche                     pr2ws("%s: carry_over error ['%s'] around line %d\n",
3700*44704f69SBart Van Assche                             __func__, carry_over, j + 1);
3701*44704f69SBart Van Assche                     ret = SG_LIB_SYNTAX_ERROR;
3702*44704f69SBart Van Assche                     goto fini;
3703*44704f69SBart Van Assche                 }
3704*44704f69SBart Van Assche                 lcp = line + 1;
3705*44704f69SBart Van Assche                 --in_len;
3706*44704f69SBart Van Assche             } else
3707*44704f69SBart Van Assche                 lcp = line;
3708*44704f69SBart Van Assche             carry_over[0] = 0;
3709*44704f69SBart Van Assche         } else
3710*44704f69SBart Van Assche             lcp = line;
3711*44704f69SBart Van Assche 
3712*44704f69SBart Van Assche         m = strspn(lcp, " \t");
3713*44704f69SBart Van Assche         if (m == in_len)
3714*44704f69SBart Van Assche             continue;
3715*44704f69SBart Van Assche         lcp += m;
3716*44704f69SBart Van Assche         in_len -= m;
3717*44704f69SBart Van Assche         if ('#' == *lcp)
3718*44704f69SBart Van Assche             continue;
3719*44704f69SBart Van Assche         k = strspn(lcp, "0123456789aAbBcCdDeEfF ,-\t");
3720*44704f69SBart Van Assche         if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) {
3721*44704f69SBart Van Assche             pr2ws("%s: syntax error at line %d, pos %d\n", __func__,
3722*44704f69SBart Van Assche                     j + 1, m + k + 1);
3723*44704f69SBart Van Assche             ret = SG_LIB_SYNTAX_ERROR;
3724*44704f69SBart Van Assche             goto fini;
3725*44704f69SBart Van Assche         }
3726*44704f69SBart Van Assche         if (no_space) {
3727*44704f69SBart Van Assche             for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
3728*44704f69SBart Van Assche                  ++k, lcp += 2) {
3729*44704f69SBart Van Assche                 if (1 != sscanf(lcp, "%2x", &h)) {
3730*44704f69SBart Van Assche                     pr2ws("%s: bad hex number in line %d, pos %d\n",
3731*44704f69SBart Van Assche                             __func__, j + 1, (int)(lcp - line + 1));
3732*44704f69SBart Van Assche                     ret = SG_LIB_SYNTAX_ERROR;
3733*44704f69SBart Van Assche                     goto fini;
3734*44704f69SBart Van Assche                 }
3735*44704f69SBart Van Assche                 if ((off + k) >= max_arr_len) {
3736*44704f69SBart Van Assche                     pr2ws("%s: array length exceeded\n", __func__);
3737*44704f69SBart Van Assche                     *mp_arr_len = max_arr_len;
3738*44704f69SBart Van Assche                     ret = SG_LIB_LBA_OUT_OF_RANGE;
3739*44704f69SBart Van Assche                     goto fini;
3740*44704f69SBart Van Assche                 } else
3741*44704f69SBart Van Assche                     mp_arr[off + k] = h;
3742*44704f69SBart Van Assche             }
3743*44704f69SBart Van Assche             if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
3744*44704f69SBart Van Assche                 carry_over[0] = *lcp;
3745*44704f69SBart Van Assche             off += k;
3746*44704f69SBart Van Assche         } else {        /* (white)space separated ASCII hexadecimal bytes */
3747*44704f69SBart Van Assche             for (redo_first = false, k = 0; k < 1024;
3748*44704f69SBart Van Assche                  k = (redo_first ? k : k + 1)) {
3749*44704f69SBart Van Assche                 if (1 == sscanf(lcp, "%10x", &h)) {
3750*44704f69SBart Van Assche                     if (h > 0xff) {
3751*44704f69SBart Van Assche                         pr2ws("%s: hex number larger than 0xff in line "
3752*44704f69SBart Van Assche                                 "%d, pos %d\n", __func__, j + 1,
3753*44704f69SBart Van Assche                                 (int)(lcp - line + 1));
3754*44704f69SBart Van Assche                         ret = SG_LIB_SYNTAX_ERROR;
3755*44704f69SBart Van Assche                         goto fini;
3756*44704f69SBart Van Assche                     }
3757*44704f69SBart Van Assche                     if (split_line && (1 == strlen(lcp))) {
3758*44704f69SBart Van Assche                         /* single trailing hex digit might be a split pair */
3759*44704f69SBart Van Assche                         carry_over[0] = *lcp;
3760*44704f69SBart Van Assche                     }
3761*44704f69SBart Van Assche                     if ((off + k) >= max_arr_len) {
3762*44704f69SBart Van Assche                         pr2ws("%s: array length exceeded\n", __func__);
3763*44704f69SBart Van Assche                         ret = SG_LIB_LBA_OUT_OF_RANGE;
3764*44704f69SBart Van Assche                         *mp_arr_len = max_arr_len;
3765*44704f69SBart Van Assche                         goto fini;
3766*44704f69SBart Van Assche                     } else if ((0 == k) && skip_first && (! redo_first))
3767*44704f69SBart Van Assche                         redo_first = true;
3768*44704f69SBart Van Assche                     else {
3769*44704f69SBart Van Assche                         redo_first = false;
3770*44704f69SBart Van Assche                         mp_arr[off + k] = h;
3771*44704f69SBart Van Assche                     }
3772*44704f69SBart Van Assche                     lcp = strpbrk(lcp, " ,-\t");
3773*44704f69SBart Van Assche                     if (NULL == lcp)
3774*44704f69SBart Van Assche                         break;
3775*44704f69SBart Van Assche                     lcp += strspn(lcp, " ,-\t");
3776*44704f69SBart Van Assche                     if ('\0' == *lcp)
3777*44704f69SBart Van Assche                         break;
3778*44704f69SBart Van Assche                 } else {
3779*44704f69SBart Van Assche                     if (('#' == *lcp) || ('\r' == *lcp)) {
3780*44704f69SBart Van Assche                         --k;
3781*44704f69SBart Van Assche                         break;
3782*44704f69SBart Van Assche                     }
3783*44704f69SBart Van Assche                     pr2ws("%s: error in line %d, at pos %d\n", __func__,
3784*44704f69SBart Van Assche                             j + 1, (int)(lcp - line + 1));
3785*44704f69SBart Van Assche                     ret = SG_LIB_SYNTAX_ERROR;
3786*44704f69SBart Van Assche                     goto fini;
3787*44704f69SBart Van Assche                 }
3788*44704f69SBart Van Assche             }
3789*44704f69SBart Van Assche             off += (k + 1);
3790*44704f69SBart Van Assche         }
3791*44704f69SBart Van Assche     }           /* end of per line loop */
3792*44704f69SBart Van Assche     if (j >= MAX_NUM_ASCII_LINES) {
3793*44704f69SBart Van Assche         pr2ws("%s: wow, more than %d lines of ASCII, give up\n", __func__,
3794*44704f69SBart Van Assche               SG_LIB_LBA_OUT_OF_RANGE);
3795*44704f69SBart Van Assche         return SG_LIB_LBA_OUT_OF_RANGE;
3796*44704f69SBart Van Assche     }
3797*44704f69SBart Van Assche     *mp_arr_len = off;
3798*44704f69SBart Van Assche     if (fp && (! has_stdin))
3799*44704f69SBart Van Assche         fclose(fp);
3800*44704f69SBart Van Assche     return 0;
3801*44704f69SBart Van Assche fini:
3802*44704f69SBart Van Assche     if (fp && (! has_stdin))
3803*44704f69SBart Van Assche         fclose(fp);
3804*44704f69SBart Van Assche     return ret;
3805*44704f69SBart Van Assche }
3806*44704f69SBart Van Assche 
3807*44704f69SBart Van Assche /* Extract character sequence from ATA words as in the model string
3808*44704f69SBart Van Assche  * in a IDENTIFY DEVICE response. Returns number of characters
3809*44704f69SBart Van Assche  * written to 'ochars' before 0 character is found or 'num' words
3810*44704f69SBart Van Assche  * are processed. */
3811*44704f69SBart Van Assche int
sg_ata_get_chars(const uint16_t * word_arr,int start_word,int num_words,bool is_big_endian,char * ochars)3812*44704f69SBart Van Assche sg_ata_get_chars(const uint16_t * word_arr, int start_word,
3813*44704f69SBart Van Assche                  int num_words, bool is_big_endian, char * ochars)
3814*44704f69SBart Van Assche {
3815*44704f69SBart Van Assche     int k;
3816*44704f69SBart Van Assche     char * op = ochars;
3817*44704f69SBart Van Assche 
3818*44704f69SBart Van Assche     for (k = start_word; k < (start_word + num_words); ++k) {
3819*44704f69SBart Van Assche         char a, b;
3820*44704f69SBart Van Assche         uint16_t s = word_arr[k];
3821*44704f69SBart Van Assche 
3822*44704f69SBart Van Assche         if (is_big_endian) {
3823*44704f69SBart Van Assche             a = s & 0xff;
3824*44704f69SBart Van Assche             b = (s >> 8) & 0xff;
3825*44704f69SBart Van Assche         } else {
3826*44704f69SBart Van Assche             a = (s >> 8) & 0xff;
3827*44704f69SBart Van Assche             b = s & 0xff;
3828*44704f69SBart Van Assche         }
3829*44704f69SBart Van Assche         if (a == 0)
3830*44704f69SBart Van Assche             break;
3831*44704f69SBart Van Assche         *op++ = a;
3832*44704f69SBart Van Assche         if (b == 0)
3833*44704f69SBart Van Assche             break;
3834*44704f69SBart Van Assche         *op++ = b;
3835*44704f69SBart Van Assche     }
3836*44704f69SBart Van Assche     return op - ochars;
3837*44704f69SBart Van Assche }
3838*44704f69SBart Van Assche 
3839*44704f69SBart Van Assche #ifdef SG_LIB_FREEBSD
3840*44704f69SBart Van Assche #include <sys/param.h>
3841*44704f69SBart Van Assche #elif defined(SG_LIB_WIN32)
3842*44704f69SBart Van Assche #include <windows.h>
3843*44704f69SBart Van Assche #endif
3844*44704f69SBart Van Assche 
3845*44704f69SBart Van Assche uint32_t
sg_get_page_size(void)3846*44704f69SBart Van Assche sg_get_page_size(void)
3847*44704f69SBart Van Assche {
3848*44704f69SBart Van Assche #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
3849*44704f69SBart Van Assche     {
3850*44704f69SBart Van Assche         long res = sysconf(_SC_PAGESIZE);   /* POSIX.1 (was getpagesize()) */
3851*44704f69SBart Van Assche 
3852*44704f69SBart Van Assche         return (res <= 0) ? 4096 : res;
3853*44704f69SBart Van Assche     }
3854*44704f69SBart Van Assche #elif defined(SG_LIB_WIN32)
3855*44704f69SBart Van Assche     static bool got_page_size = false;
3856*44704f69SBart Van Assche     static uint32_t win_page_size;
3857*44704f69SBart Van Assche 
3858*44704f69SBart Van Assche     if (! got_page_size) {
3859*44704f69SBart Van Assche         SYSTEM_INFO si;
3860*44704f69SBart Van Assche 
3861*44704f69SBart Van Assche         GetSystemInfo(&si);
3862*44704f69SBart Van Assche         win_page_size = si.dwPageSize;
3863*44704f69SBart Van Assche         got_page_size = true;
3864*44704f69SBart Van Assche     }
3865*44704f69SBart Van Assche     return win_page_size;
3866*44704f69SBart Van Assche #elif defined(SG_LIB_FREEBSD)
3867*44704f69SBart Van Assche     return PAGE_SIZE;
3868*44704f69SBart Van Assche #else
3869*44704f69SBart Van Assche     return 4096;     /* give up, pick likely figure */
3870*44704f69SBart Van Assche #endif
3871*44704f69SBart Van Assche }
3872*44704f69SBart Van Assche 
3873*44704f69SBart Van Assche #if defined(SG_LIB_WIN32)
3874*44704f69SBart Van Assche #if defined(MSC_VER) || defined(__MINGW32__)
3875*44704f69SBart Van Assche /* windows.h already included above */
3876*44704f69SBart Van Assche #define sg_sleep_for(seconds)    Sleep( (seconds) * 1000)
3877*44704f69SBart Van Assche #else
3878*44704f69SBart Van Assche #define sg_sleep_for(seconds)    sleep(seconds)
3879*44704f69SBart Van Assche #endif
3880*44704f69SBart Van Assche #else
3881*44704f69SBart Van Assche #define sg_sleep_for(seconds)    sleep(seconds)
3882*44704f69SBart Van Assche #endif
3883*44704f69SBart Van Assche 
3884*44704f69SBart Van Assche void
sg_sleep_secs(int num_secs)3885*44704f69SBart Van Assche sg_sleep_secs(int num_secs)
3886*44704f69SBart Van Assche {
3887*44704f69SBart Van Assche     sg_sleep_for(num_secs);
3888*44704f69SBart Van Assche }
3889*44704f69SBart Van Assche 
3890*44704f69SBart Van Assche void
sg_warn_and_wait(const char * cmd_name,const char * dev_name,bool stress_all)3891*44704f69SBart Van Assche sg_warn_and_wait(const char * cmd_name, const char * dev_name,
3892*44704f69SBart Van Assche                  bool stress_all)
3893*44704f69SBart Van Assche {
3894*44704f69SBart Van Assche     int k, j;
3895*44704f69SBart Van Assche     const char * stressp = stress_all ? "ALL d" : "D";
3896*44704f69SBart Van Assche     const char * will_mayp = stress_all ? "will" : "may";
3897*44704f69SBart Van Assche 
3898*44704f69SBart Van Assche     for (k = 0, j = 15; k < 3; ++k, j -= 5) {
3899*44704f69SBart Van Assche         printf("\nA %s command will commence in %d seconds\n", cmd_name, j);
3900*44704f69SBart Van Assche         printf("    %sata on %s %s be DESTROYED%s\n", stressp, dev_name,
3901*44704f69SBart Van Assche                will_mayp, (stress_all ? "" : " or modified"));
3902*44704f69SBart Van Assche         printf("        Press control-C to abort\n");
3903*44704f69SBart Van Assche         sg_sleep_secs(5);
3904*44704f69SBart Van Assche     }
3905*44704f69SBart Van Assche     sg_sleep_secs(1);
3906*44704f69SBart Van Assche }
3907*44704f69SBart Van Assche 
3908*44704f69SBart Van Assche /* Returns pointer to heap (or NULL) that is aligned to a align_to byte
3909*44704f69SBart Van Assche  * boundary. Sends back *buff_to_free pointer in third argument that may be
3910*44704f69SBart Van Assche  * different from the return value. If it is different then the *buff_to_free
3911*44704f69SBart Van Assche  * pointer should be freed (rather than the returned value) when the heap is
3912*44704f69SBart Van Assche  * no longer needed. If align_to is 0 then aligns to OS's page size. Sets all
3913*44704f69SBart Van Assche  * returned heap to zeros. If num_bytes is 0 then set to page size. */
3914*44704f69SBart Van Assche uint8_t *
sg_memalign(uint32_t num_bytes,uint32_t align_to,uint8_t ** buff_to_free,bool vb)3915*44704f69SBart Van Assche sg_memalign(uint32_t num_bytes, uint32_t align_to, uint8_t ** buff_to_free,
3916*44704f69SBart Van Assche             bool vb)
3917*44704f69SBart Van Assche {
3918*44704f69SBart Van Assche     size_t psz;
3919*44704f69SBart Van Assche 
3920*44704f69SBart Van Assche     if (buff_to_free)   /* make sure buff_to_free is NULL if alloc fails */
3921*44704f69SBart Van Assche         *buff_to_free = NULL;
3922*44704f69SBart Van Assche     psz = (align_to > 0) ? align_to : sg_get_page_size();
3923*44704f69SBart Van Assche     if (0 == num_bytes)
3924*44704f69SBart Van Assche         num_bytes = psz;        /* ugly to handle otherwise */
3925*44704f69SBart Van Assche 
3926*44704f69SBart Van Assche #ifdef HAVE_POSIX_MEMALIGN
3927*44704f69SBart Van Assche     {
3928*44704f69SBart Van Assche         int err;
3929*44704f69SBart Van Assche         uint8_t * res;
3930*44704f69SBart Van Assche         void * wp = NULL;
3931*44704f69SBart Van Assche 
3932*44704f69SBart Van Assche         err = posix_memalign(&wp, psz, num_bytes);
3933*44704f69SBart Van Assche         if (err || (NULL == wp)) {
3934*44704f69SBart Van Assche             pr2ws("%s: posix_memalign: error [%d], out of memory?\n",
3935*44704f69SBart Van Assche                   __func__, err);
3936*44704f69SBart Van Assche             return NULL;
3937*44704f69SBart Van Assche         }
3938*44704f69SBart Van Assche         memset(wp, 0, num_bytes);
3939*44704f69SBart Van Assche         if (buff_to_free)
3940*44704f69SBart Van Assche             *buff_to_free = (uint8_t *)wp;
3941*44704f69SBart Van Assche         res = (uint8_t *)wp;
3942*44704f69SBart Van Assche         if (vb) {
3943*44704f69SBart Van Assche             pr2ws("%s: posix_ma, len=%d, ", __func__, num_bytes);
3944*44704f69SBart Van Assche             if (buff_to_free)
3945*44704f69SBart Van Assche                 pr2ws("wrkBuffp=%p, ", (void *)res);
3946*44704f69SBart Van Assche             pr2ws("psz=%u, rp=%p\n", (unsigned int)psz, (void *)res);
3947*44704f69SBart Van Assche         }
3948*44704f69SBart Van Assche         return res;
3949*44704f69SBart Van Assche     }
3950*44704f69SBart Van Assche #else
3951*44704f69SBart Van Assche     {
3952*44704f69SBart Van Assche         void * wrkBuff;
3953*44704f69SBart Van Assche         uint8_t * res;
3954*44704f69SBart Van Assche         sg_uintptr_t align_1 = psz - 1;
3955*44704f69SBart Van Assche 
3956*44704f69SBart Van Assche         wrkBuff = (uint8_t *)calloc(num_bytes + psz, 1);
3957*44704f69SBart Van Assche         if (NULL == wrkBuff) {
3958*44704f69SBart Van Assche             if (buff_to_free)
3959*44704f69SBart Van Assche                 *buff_to_free = NULL;
3960*44704f69SBart Van Assche             return NULL;
3961*44704f69SBart Van Assche         } else if (buff_to_free)
3962*44704f69SBart Van Assche             *buff_to_free = (uint8_t *)wrkBuff;
3963*44704f69SBart Van Assche         res = (uint8_t *)(void *)
3964*44704f69SBart Van Assche             (((sg_uintptr_t)wrkBuff + align_1) & (~align_1));
3965*44704f69SBart Van Assche         if (vb) {
3966*44704f69SBart Van Assche             pr2ws("%s: hack, len=%d, ", __func__, num_bytes);
3967*44704f69SBart Van Assche             if (buff_to_free)
3968*44704f69SBart Van Assche                 pr2ws("buff_to_free=%p, ", wrkBuff);
3969*44704f69SBart Van Assche             pr2ws("align_1=%" PRIuPTR "u, rp=%p\n", align_1, (void *)res);
3970*44704f69SBart Van Assche         }
3971*44704f69SBart Van Assche         return res;
3972*44704f69SBart Van Assche     }
3973*44704f69SBart Van Assche #endif
3974*44704f69SBart Van Assche }
3975*44704f69SBart Van Assche 
3976*44704f69SBart Van Assche /* If byte_count is 0 or less then the OS page size is used as denominator.
3977*44704f69SBart Van Assche  * Returns true  if the remainder of ((unsigned)pointer % byte_count) is 0,
3978*44704f69SBart Van Assche  * else returns false. */
3979*44704f69SBart Van Assche bool
sg_is_aligned(const void * pointer,int byte_count)3980*44704f69SBart Van Assche sg_is_aligned(const void * pointer, int byte_count)
3981*44704f69SBart Van Assche {
3982*44704f69SBart Van Assche     return 0 == ((sg_uintptr_t)pointer %
3983*44704f69SBart Van Assche                  ((byte_count > 0) ? (uint32_t)byte_count :
3984*44704f69SBart Van Assche                                      sg_get_page_size()));
3985*44704f69SBart Van Assche }
3986*44704f69SBart Van Assche 
3987*44704f69SBart Van Assche /* Does similar job to sg_get_unaligned_be*() but this function starts at
3988*44704f69SBart Van Assche  * a given start_bit (i.e. within byte, so 7 is MSbit of byte and 0 is LSbit)
3989*44704f69SBart Van Assche  * offset. Maximum number of num_bits is 64. For example, these two
3990*44704f69SBart Van Assche  * invocations are equivalent (and should yield the same result);
3991*44704f69SBart Van Assche  *       sg_get_big_endian(from_bp, 7, 16)
3992*44704f69SBart Van Assche  *       sg_get_unaligned_be16(from_bp)  */
3993*44704f69SBart Van Assche uint64_t
sg_get_big_endian(const uint8_t * from_bp,int start_bit,int num_bits)3994*44704f69SBart Van Assche sg_get_big_endian(const uint8_t * from_bp, int start_bit /* 0 to 7 */,
3995*44704f69SBart Van Assche                   int num_bits /* 1 to 64 */)
3996*44704f69SBart Van Assche {
3997*44704f69SBart Van Assche     uint64_t res;
3998*44704f69SBart Van Assche     int sbit_o1 = start_bit + 1;
3999*44704f69SBart Van Assche 
4000*44704f69SBart Van Assche     res = (*from_bp++ & ((1 << sbit_o1) - 1));
4001*44704f69SBart Van Assche     num_bits -= sbit_o1;
4002*44704f69SBart Van Assche     while (num_bits > 0) {
4003*44704f69SBart Van Assche         res <<= 8;
4004*44704f69SBart Van Assche         res |= *from_bp++;
4005*44704f69SBart Van Assche         num_bits -= 8;
4006*44704f69SBart Van Assche     }
4007*44704f69SBart Van Assche     if (num_bits < 0)
4008*44704f69SBart Van Assche         res >>= (-num_bits);
4009*44704f69SBart Van Assche     return res;
4010*44704f69SBart Van Assche }
4011*44704f69SBart Van Assche 
4012*44704f69SBart Van Assche /* Does similar job to sg_put_unaligned_be*() but this function starts at
4013*44704f69SBart Van Assche  * a given start_bit offset. Maximum number of num_bits is 64. Preserves
4014*44704f69SBart Van Assche  * residual bits in partially written bytes. start_bit 7 is MSb. */
4015*44704f69SBart Van Assche void
sg_set_big_endian(uint64_t val,uint8_t * to,int start_bit,int num_bits)4016*44704f69SBart Van Assche sg_set_big_endian(uint64_t val, uint8_t * to,
4017*44704f69SBart Van Assche                   int start_bit /* 0 to 7 */, int num_bits /* 1 to 64 */)
4018*44704f69SBart Van Assche {
4019*44704f69SBart Van Assche     int sbit_o1 = start_bit + 1;
4020*44704f69SBart Van Assche     int mask, num, k, x;
4021*44704f69SBart Van Assche 
4022*44704f69SBart Van Assche     if ((NULL == to) || (start_bit > 7) || (num_bits > 64)) {
4023*44704f69SBart Van Assche         pr2ws("%s: bad args: start_bit=%d, num_bits=%d\n", __func__,
4024*44704f69SBart Van Assche               start_bit, num_bits);
4025*44704f69SBart Van Assche         return;
4026*44704f69SBart Van Assche     }
4027*44704f69SBart Van Assche     mask = (8 != sbit_o1) ? ((1 << sbit_o1) - 1) : 0xff;
4028*44704f69SBart Van Assche     k = start_bit - ((num_bits - 1) % 8);
4029*44704f69SBart Van Assche     if (0 != k)
4030*44704f69SBart Van Assche         val <<= ((k > 0) ? k : (8 + k));
4031*44704f69SBart Van Assche     num = (num_bits + 15 - sbit_o1) / 8;
4032*44704f69SBart Van Assche     for (k = 0; k < num; ++k) {
4033*44704f69SBart Van Assche         if ((sbit_o1 - num_bits) > 0)
4034*44704f69SBart Van Assche             mask &= ~((1 << (sbit_o1 - num_bits)) - 1);
4035*44704f69SBart Van Assche         if (k < (num - 1))
4036*44704f69SBart Van Assche             x = (val >> ((num - k - 1) * 8)) & 0xff;
4037*44704f69SBart Van Assche         else
4038*44704f69SBart Van Assche             x = val & 0xff;
4039*44704f69SBart Van Assche         to[k] = (to[k] & ~mask) | (x & mask);
4040*44704f69SBart Van Assche         mask = 0xff;
4041*44704f69SBart Van Assche         num_bits -= sbit_o1;
4042*44704f69SBart Van Assche         sbit_o1 = 8;
4043*44704f69SBart Van Assche     }
4044*44704f69SBart Van Assche }
4045*44704f69SBart Van Assche 
4046*44704f69SBart Van Assche const char *
sg_lib_version()4047*44704f69SBart Van Assche sg_lib_version()
4048*44704f69SBart Van Assche {
4049*44704f69SBart Van Assche     return sg_lib_version_str;
4050*44704f69SBart Van Assche }
4051*44704f69SBart Van Assche 
4052*44704f69SBart Van Assche 
4053*44704f69SBart Van Assche #ifdef SG_LIB_MINGW
4054*44704f69SBart Van Assche /* Non Unix OSes distinguish between text and binary files.
4055*44704f69SBart Van Assche    Set text mode on fd. Does nothing in Unix. Returns negative number on
4056*44704f69SBart Van Assche    failure. */
4057*44704f69SBart Van Assche 
4058*44704f69SBart Van Assche #include <fcntl.h>
4059*44704f69SBart Van Assche 
4060*44704f69SBart Van Assche int
sg_set_text_mode(int fd)4061*44704f69SBart Van Assche sg_set_text_mode(int fd)
4062*44704f69SBart Van Assche {
4063*44704f69SBart Van Assche     return setmode(fd, O_TEXT);
4064*44704f69SBart Van Assche }
4065*44704f69SBart Van Assche 
4066*44704f69SBart Van Assche /* Set binary mode on fd. Does nothing in Unix. Returns negative number on
4067*44704f69SBart Van Assche    failure. */
4068*44704f69SBart Van Assche int
sg_set_binary_mode(int fd)4069*44704f69SBart Van Assche sg_set_binary_mode(int fd)
4070*44704f69SBart Van Assche {
4071*44704f69SBart Van Assche     return setmode(fd, O_BINARY);
4072*44704f69SBart Van Assche }
4073*44704f69SBart Van Assche 
4074*44704f69SBart Van Assche #else
4075*44704f69SBart Van Assche /* For Unix the following functions are dummies. */
4076*44704f69SBart Van Assche int
sg_set_text_mode(int fd)4077*44704f69SBart Van Assche sg_set_text_mode(int fd)
4078*44704f69SBart Van Assche {
4079*44704f69SBart Van Assche     return fd;  /* fd should be >= 0 */
4080*44704f69SBart Van Assche }
4081*44704f69SBart Van Assche 
4082*44704f69SBart Van Assche int
sg_set_binary_mode(int fd)4083*44704f69SBart Van Assche sg_set_binary_mode(int fd)
4084*44704f69SBart Van Assche {
4085*44704f69SBart Van Assche     return fd;
4086*44704f69SBart Van Assche }
4087*44704f69SBart Van Assche 
4088*44704f69SBart Van Assche #endif
4089