xref: /aosp_15_r20/external/sg3_utils/src/sg_raw.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * A utility program originally written for the Linux OS SCSI subsystem.
3  *
4  * Copyright (C) 2000-2022 Ingo van Lil <[email protected]>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * SPDX-License-Identifier: GPL-2.0-or-later
12  *
13  * This program can be used to send raw SCSI commands (with an optional
14  * data phase) through a Generic SCSI interface.
15  */
16 
17 #define _XOPEN_SOURCE 600       /* clear up posix_memalign() warning */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <stdbool.h>
23 #include <ctype.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <getopt.h>
28 #include <inttypes.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 #include "sg_lib.h"
37 #include "sg_pt.h"
38 #include "sg_pt_nvme.h"
39 #include "sg_pr2serr.h"
40 #include "sg_unaligned.h"
41 
42 #define SG_RAW_VERSION "0.4.39 (2022-04-25)"
43 
44 #define DEFAULT_TIMEOUT 20
45 #define MIN_SCSI_CDBSZ 6
46 #define MAX_SCSI_CDBSZ 260
47 #define MAX_SCSI_DXLEN (1024 * 1024)
48 
49 #define NVME_ADDR_DATA_IN  0xfffffffffffffffe
50 #define NVME_ADDR_DATA_OUT 0xfffffffffffffffd
51 #define NVME_DATA_LEN_DATA_IN  0xfffffffe
52 #define NVME_DATA_LEN_DATA_OUT 0xfffffffd
53 
54 static struct option long_options[] = {
55     { "binary",  no_argument,       NULL, 'b' },
56     { "cmdfile", required_argument, NULL, 'c' },
57     { "cmdset",  required_argument, NULL, 'C' },
58     { "enumerate", no_argument,     NULL, 'e' },
59     { "help",    no_argument,       NULL, 'h' },
60     { "infile",  required_argument, NULL, 'i' },
61     { "skip",    required_argument, NULL, 'k' },
62     { "nosense", no_argument,       NULL, 'n' },
63     { "nvm",     no_argument,       NULL, 'N' },
64     { "outfile", required_argument, NULL, 'o' },
65     { "raw",     no_argument,       NULL, 'w' },
66     { "request", required_argument, NULL, 'r' },
67     { "readonly", no_argument,      NULL, 'R' },
68     { "scan",    required_argument, NULL, 'Q' },
69     { "send",    required_argument, NULL, 's' },
70     { "timeout", required_argument, NULL, 't' },
71     { "verbose", no_argument,       NULL, 'v' },
72     { "version", no_argument,       NULL, 'V' },
73     { 0, 0, 0, 0 }
74 };
75 
76 struct opts_t {
77     bool cmdfile_given;
78     bool do_datain;
79     bool datain_binary;
80     bool do_dataout;
81     bool do_enumerate;
82     bool no_sense;
83     bool do_nvm;     /* the NVMe command set: NVM containing its READ+WRITE */
84     bool do_help;
85     bool verbose_given;
86     bool version_given;
87     int cdb_length;
88     int cmdset;
89     int datain_len;
90     int dataout_len;
91     int timeout;
92     int raw;
93     int readonly;
94     int scan_first;
95     int scan_last;
96     int verbose;
97     off_t dataout_offset;
98     uint8_t cdb[MAX_SCSI_CDBSZ];        /* might be NVMe command (64 byte) */
99     const char *cmd_file;
100     const char *datain_file;
101     const char *dataout_file;
102     char *device_name;
103 };
104 
105 
106 static void
pr_version()107 pr_version()
108 {
109     pr2serr("sg_raw " SG_RAW_VERSION "\n"
110             "Copyright (C) 2007-2021 Ingo van Lil <[email protected]>\n"
111             "This is free software.  You may redistribute copies of it "
112             "under the terms of\n"
113             "the GNU General Public License "
114             "<https://www.gnu.org/licenses/gpl.html>.\n"
115             "There is NO WARRANTY, to the extent permitted by law.\n");
116 }
117 
118 static void
usage()119 usage()
120 {
121     pr2serr("Usage: sg_raw [OPTION]* DEVICE [CDB0 CDB1 ...]\n"
122             "\n"
123             "Options:\n"
124             "  --binary|-b            Dump data in binary form, even when "
125             "writing to\n"
126             "                         stdout\n"
127             "  --cmdfile=CF|-c CF     CF is file containing command in hex "
128             "bytes\n"
129             "  --cmdset=CS|-C CS      CS is 0 (def) heuristic chooses "
130             "command set;\n"
131             "                         1: force SCSI; 2: force NVMe\n"
132             "  --enumerate|-e         Decodes cdb name then exits; requires "
133             "DEVICE but\n"
134             "                         ignores it\n"
135             "  --help|-h              Show this message and exit\n"
136             "  --infile=IFILE|-i IFILE    Read binary data to send (i.e. "
137             "data-out)\n"
138             "                             from IFILE (default: stdin)\n"
139             "  --nosense|-n           Don't display sense information\n"
140             "  --nvm|-N               command is for NVM command set (e.g. "
141             "Read);\n"
142             "                         default, if NVMe fd, Admin command "
143             "set\n"
144             "  --outfile=OFILE|-o OFILE    Write binary data from device "
145             "(i.e. data-in)\n"
146             "                              to OFILE (def: hexdump to "
147             "stdout)\n"
148             "  --raw|-w               interpret CF (command file) as "
149             "binary (def:\n"
150             "                         interpret as ASCII hex)\n"
151             "  --readonly|-R          Open DEVICE read-only (default: "
152             "read-write)\n"
153             "  --request=RLEN|-r RLEN    Request up to RLEN bytes of data "
154             "(data-in)\n"
155             "  --scan=FO,LO|-Q FO,LO    scan command set from FO (first "
156             "opcode)\n"
157             "                           to LO (last opcode) inclusive. Uses "
158             "given\n"
159             "                           command bytes, varying the opcode\n"
160             "  --send=SLEN|-s SLEN    Send SLEN bytes of data (data-out)\n"
161             "  --skip=KLEN|-k KLEN    Skip the first KLEN bytes when "
162             "reading\n"
163             "                         data to send (default: 0)\n"
164             "  --timeout=SECS|-t SECS    Timeout in seconds (default: 20)\n"
165             "  --verbose|-v           Increase verbosity\n"
166             "  --version|-V           Show version information and exit\n"
167             "\n"
168             "Between 6 and 260 command bytes (two hex digits each) can be "
169             "specified\nand will be sent to DEVICE. Lengths RLEN, SLEN and "
170             "KLEN are decimal by\ndefault. Bidirectional commands "
171             "accepted.\n\nSimple example: Perform INQUIRY on /dev/sg0:\n"
172             "  sg_raw -r 1k /dev/sg0 12 00 00 00 60 00\n");
173 }
174 
175 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])176 parse_cmd_line(struct opts_t * op, int argc, char *argv[])
177 {
178     while (1) {
179         int c, n;
180         const char * cp;
181 
182         c = getopt_long(argc, argv, "bc:C:ehi:k:nNo:Q:r:Rs:t:vVw",
183                         long_options, NULL);
184         if (c == -1)
185             break;
186 
187         switch (c) {
188         case 'b':
189             op->datain_binary = true;
190             break;
191         case 'c':
192             op->cmd_file = optarg;
193             op->cmdfile_given = true;
194             break;
195         case 'C':
196             n = sg_get_num(optarg);
197             if ((n < 0) || (n > 2)) {
198                 pr2serr("Invalid argument to --cmdset= expect 0, 1 or 2\n");
199                 return SG_LIB_SYNTAX_ERROR;
200             }
201             op->cmdset = n;
202             break;
203         case 'e':
204             op->do_enumerate = true;
205             break;
206         case 'h':
207         case '?':
208             op->do_help = true;
209             return 0;
210         case 'i':
211             if (op->dataout_file) {
212                 pr2serr("Too many '--infile=' options\n");
213                 return SG_LIB_CONTRADICT;
214             }
215             op->dataout_file = optarg;
216             break;
217         case 'k':
218             n = sg_get_num(optarg);
219             if (n < 0) {
220                 pr2serr("Invalid argument to '--skip'\n");
221                 return SG_LIB_SYNTAX_ERROR;
222             }
223             op->dataout_offset = n;
224             break;
225         case 'n':
226             op->no_sense = true;
227             break;
228         case 'N':
229             op->do_nvm = true;
230             break;
231         case 'o':
232             if (op->datain_file) {
233                 pr2serr("Too many '--outfile=' options\n");
234                 return SG_LIB_CONTRADICT;
235             }
236             op->datain_file = optarg;
237             break;
238         case 'Q':       /* --scan=FO,LO */
239             cp = strchr(optarg, ',');
240             if (NULL == cp) {
241                 pr2serr("--scan= expects two numbers, comma separated\n");
242                 return SG_LIB_SYNTAX_ERROR;
243             }
244             n = sg_get_num(optarg);
245             if ((n < 0) || (n > 255)) {
246                 pr2serr("Invalid first number to --scan= expect 0 to 255\n");
247                 return SG_LIB_SYNTAX_ERROR;
248             }
249             op->scan_first = n;
250             n = sg_get_num(cp + 1);
251             if ((n < 0) || (n > 255)) {
252                 pr2serr("Invalid second number to --scan= expect 0 to 255\n");
253                 return SG_LIB_SYNTAX_ERROR;
254             }
255             op->scan_last = n;
256             if (op->scan_first >= n)
257                 pr2serr("Warning: scan range degenerate, ignore\n");
258             break;
259         case 'r':
260             op->do_datain = true;
261             n = sg_get_num(optarg);
262             if (n < 0 || n > MAX_SCSI_DXLEN) {
263                 pr2serr("Invalid argument to '--request'\n");
264                 return SG_LIB_SYNTAX_ERROR;
265             }
266             op->datain_len = n;
267             break;
268         case 'R':
269             ++op->readonly;
270             break;
271         case 's':
272             op->do_dataout = true;
273             n = sg_get_num(optarg);
274             if (n < 0 || n > MAX_SCSI_DXLEN) {
275                 pr2serr("Invalid argument to '--send'\n");
276                 return SG_LIB_SYNTAX_ERROR;
277             }
278             op->dataout_len = n;
279             break;
280         case 't':
281             n = sg_get_num(optarg);
282             if (n < 0) {
283                 pr2serr("Invalid argument to '--timeout'\n");
284                 return SG_LIB_SYNTAX_ERROR;
285             }
286             op->timeout = n;
287             break;
288         case 'v':
289             op->verbose_given = true;
290             ++op->verbose;
291             break;
292         case 'V':
293             op->version_given = true;
294             break;
295         case 'w':       /* -r and -R already in use, this is --raw */
296             ++op->raw;
297             break;
298         default:
299             return SG_LIB_SYNTAX_ERROR;
300         }
301     }
302 
303     if (op->version_given
304 #ifdef DEBUG
305         && ! op->verbose_given
306 #endif
307        )
308         return 0;
309 
310     if (optind >= argc) {
311         pr2serr("No device specified\n");
312         return SG_LIB_SYNTAX_ERROR;
313     }
314     op->device_name = argv[optind];
315     ++optind;
316 
317     while (optind < argc) {
318         char *opt = argv[optind++];
319         char *endptr;
320         int cmd = strtol(opt, &endptr, 16);
321 
322         if (*opt == '\0' || *endptr != '\0' || cmd < 0x00 || cmd > 0xff) {
323             pr2serr("Invalid command byte '%s'\n", opt);
324             return SG_LIB_SYNTAX_ERROR;
325         }
326 
327         if (op->cdb_length >= MAX_SCSI_CDBSZ) {
328             pr2serr("CDB too long (max. %d bytes)\n", MAX_SCSI_CDBSZ);
329             return SG_LIB_SYNTAX_ERROR;
330         }
331         op->cdb[op->cdb_length] = cmd;
332         ++op->cdb_length;
333     }
334 
335     if (op->cmdfile_given) {
336         int err;
337 
338         err = sg_f2hex_arr(op->cmd_file, (op->raw > 0) /* as_binary */,
339                            false /* no_space */, op->cdb, &op->cdb_length,
340                            MAX_SCSI_CDBSZ);
341         if (err) {
342             pr2serr("Unable to parse: %s as %s\n", op->cmd_file,
343                     (op->raw > 0) ? "binary" : "hex");
344             return SG_LIB_SYNTAX_ERROR;
345         }
346         if (op->verbose > 2) {
347             pr2serr("Read %d from %s . They are in hex:\n", op->cdb_length,
348                     op->cmd_file);
349             hex2stderr(op->cdb, op->cdb_length, -1);
350         }
351     }
352     if (op->cdb_length < MIN_SCSI_CDBSZ) {
353         pr2serr("CDB too short (min. %d bytes)\n", MIN_SCSI_CDBSZ);
354         return SG_LIB_SYNTAX_ERROR;
355     }
356     if (op->do_enumerate || (op->verbose > 1)) {
357         bool is_scsi_cdb = sg_is_scsi_cdb(op->cdb, op->cdb_length);
358         int sa;
359         char b[80];
360 
361         if ((1 == op->cmdset) && !is_scsi_cdb) {
362             is_scsi_cdb = true;
363             if (op->verbose > 3)
364                 printf(">>> overriding cmdset guess to SCSI\n");
365         }
366         if ((2 == op->cmdset) && is_scsi_cdb) {
367             is_scsi_cdb = false;
368             if (op->verbose > 3)
369                 printf(">>> overriding cmdset guess to NVMe\n");
370         }
371         if (is_scsi_cdb) {
372             if (op->cdb_length > 16) {
373                 sa = sg_get_unaligned_be16(op->cdb + 8);
374                 if ((0x7f != op->cdb[0]) && (0x7e != op->cdb[0]))
375                     printf(">>> Unlikely to be SCSI CDB since all over 16 "
376                            "bytes long should\n>>> start with 0x7f or "
377                            "0x7e\n");
378             } else
379                 sa = op->cdb[1] & 0x1f;
380             sg_get_opcode_sa_name(op->cdb[0], sa, 0, sizeof(b), b);
381             printf("Attempt to decode cdb name: %s\n", b);
382         } else
383             printf(">>> Seems to be NVMe %s command\n",
384                    sg_get_nvme_opcode_name(op->cdb[0], ! op->do_nvm,
385                                            sizeof(b), b));
386     }
387     return 0;
388 }
389 
390 static int
skip(int fd,off_t offset)391 skip(int fd, off_t offset)
392 {
393     int err;
394     off_t remain;
395     char buffer[512];
396 
397     if (lseek(fd, offset, SEEK_SET) >= 0)
398         return 0;
399 
400     // lseek failed; fall back to reading and discarding data
401     remain = offset;
402     while (remain > 0) {
403         ssize_t amount, done;
404         amount = (remain < (off_t)sizeof(buffer)) ? remain
405                                          : (off_t)sizeof(buffer);
406         done = read(fd, buffer, amount);
407         if (done < 0) {
408             err = errno;
409             perror("Error reading input data to skip");
410             return sg_convert_errno(err);
411         } else if (done == 0) {
412             pr2serr("EOF on input file/stream\n");
413             return SG_LIB_FILE_ERROR;
414         } else
415             remain -= done;
416     }
417     return 0;
418 }
419 
420 static uint8_t *
fetch_dataout(struct opts_t * op,uint8_t ** free_buf,int * errp)421 fetch_dataout(struct opts_t * op, uint8_t ** free_buf, int * errp)
422 {
423     bool ok = false;
424     int fd, len, tot_len, boff, err;
425     uint8_t *buf = NULL;
426 
427     *free_buf = NULL;
428     if (errp)
429         *errp = 0;
430     if (op->dataout_file) {
431         fd = open(op->dataout_file, O_RDONLY);
432         if (fd < 0) {
433             err = errno;
434             if (errp)
435                 *errp = sg_convert_errno(err);
436             perror(op->dataout_file);
437             goto bail;
438         }
439     } else
440         fd = STDIN_FILENO;
441     if (sg_set_binary_mode(fd) < 0) {
442         err = errno;
443         if (errp)
444             *errp = err;
445         perror("sg_set_binary_mode");
446         goto bail;
447     }
448 
449     if (op->dataout_offset > 0) {
450         err = skip(fd, op->dataout_offset);
451         if (err != 0) {
452             if (errp)
453                 *errp = err;
454             goto bail;
455         }
456     }
457 
458     tot_len = op->dataout_len;
459     buf = sg_memalign(tot_len, 0 /* page_size */, free_buf, false);
460     if (buf == NULL) {
461         pr2serr("sg_memalign: failed to get %d bytes of memory\n", tot_len);
462         if (errp)
463             *errp = sg_convert_errno(ENOMEM);
464         goto bail;
465     }
466 
467     for (boff = 0; boff < tot_len; boff += len) {
468         len = read(fd, buf + boff , tot_len - boff);
469         if (len < 0) {
470             err = errno;
471             if (errp)
472                 *errp = sg_convert_errno(err);
473             perror("Failed to read input data");
474             goto bail;
475         } else if (0 == len) {
476             if (errp)
477                 *errp = SG_LIB_FILE_ERROR;
478             pr2serr("EOF on input file/stream at buffer offset %d\n", boff);
479             goto bail;
480         }
481     }
482     ok = true;
483 
484 bail:
485     if (fd >= 0 && fd != STDIN_FILENO)
486         close(fd);
487     if (! ok) {
488         if (*free_buf) {
489             free(*free_buf);
490             *free_buf = NULL;
491         }
492         return NULL;
493     }
494     return buf;
495 }
496 
497 static int
write_dataout(const char * filename,uint8_t * buf,int len)498 write_dataout(const char *filename, uint8_t *buf, int len)
499 {
500     int ret = SG_LIB_FILE_ERROR;
501     int fd;
502 
503     if ((filename == NULL) ||
504         ((1 == strlen(filename)) && ('-' == filename[0])))
505         fd = STDOUT_FILENO;
506     else {
507         fd = creat(filename, 0666);
508         if (fd < 0) {
509             ret = sg_convert_errno(errno);
510             perror(filename);
511             goto bail;
512         }
513     }
514     if (sg_set_binary_mode(fd) < 0) {
515         perror("sg_set_binary_mode");
516         goto bail;
517     }
518 
519     if (write(fd, buf, len) != len) {
520         ret = sg_convert_errno(errno);
521         perror(filename ? filename : "stdout");
522         goto bail;
523     }
524 
525     ret = 0;
526 
527 bail:
528     if (fd >= 0 && fd != STDOUT_FILENO)
529         close(fd);
530     return ret;
531 }
532 
533 
534 int
main(int argc,char * argv[])535 main(int argc, char *argv[])
536 {
537     bool is_scsi_cdb = true;
538     bool do_scan = false;
539     int ret = 0;
540     int err = 0;
541     int res_cat, status, s_len, k, ret2;
542     int sg_fd = -1;
543     uint16_t sct_sc;
544     uint32_t result;
545     struct sg_pt_base *ptvp = NULL;
546     uint8_t sense_buffer[32] SG_C_CPP_ZERO_INIT;
547     uint8_t * dinp = NULL;
548     uint8_t * doutp = NULL;
549     uint8_t * free_buf_out = NULL;
550     uint8_t * wrkBuf = NULL;
551     struct opts_t opts;
552     struct opts_t * op;
553     char b[128];
554     const int b_len = sizeof(b);
555 
556     op = &opts;
557     memset(op, 0, sizeof(opts));
558     op->timeout = DEFAULT_TIMEOUT;
559     ret = parse_cmd_line(op, argc, argv);
560 #ifdef DEBUG
561     pr2serr("In DEBUG mode, ");
562     if (op->verbose_given && op->version_given) {
563         pr2serr("but override: '-vV' given, zero verbose and continue\n");
564         op->verbose_given = false;
565         op->version_given = false;
566         op->verbose = 0;
567     } else if (! op->verbose_given) {
568         pr2serr("set '-vv'\n");
569         op->verbose = 2;
570     } else
571         pr2serr("keep verbose=%d\n", op->verbose);
572 #else
573     if (op->verbose_given && op->version_given)
574         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
575 #endif
576     if (op->version_given) {
577         pr_version();
578         goto done;
579     }
580 
581     if (ret != 0) {
582         pr2serr("\n");  /* blank line before outputting usage */
583         usage();
584         goto done;
585     } else if (op->do_help) {
586         usage();
587         goto done;
588     } else if (op->do_enumerate)
589         goto done;
590 
591     sg_fd = scsi_pt_open_device(op->device_name, op->readonly,
592                                 op->verbose);
593     if (sg_fd < 0) {
594         pr2serr("%s: %s\n", op->device_name, safe_strerror(-sg_fd));
595         ret = sg_convert_errno(-sg_fd);
596         goto done;
597     }
598 
599     ptvp = construct_scsi_pt_obj_with_fd(sg_fd, op->verbose);
600     if (ptvp == NULL) {
601         pr2serr("construct_scsi_pt_obj_with_fd() failed\n");
602         ret = SG_LIB_CAT_OTHER;
603         goto done;
604     }
605 
606     if (op->scan_first < op->scan_last)
607         do_scan = true;
608 
609 and_again:
610     if (do_scan) {
611         op->cdb[0] = op->scan_first;
612         printf("Command bytes in hex:");
613         for (k = 0; k < op->cdb_length; ++k)
614             printf(" %02x", op->cdb[k]);
615         printf("\n");
616     }
617 
618     is_scsi_cdb = sg_is_scsi_cdb(op->cdb, op->cdb_length);
619     if ((1 == op->cmdset) && !is_scsi_cdb)
620         is_scsi_cdb = true;
621     else if ((2 == op->cmdset) && is_scsi_cdb)
622         is_scsi_cdb = false;
623 
624     if (op->do_dataout) {
625         uint32_t dout_len;
626 
627         doutp = fetch_dataout(op, &free_buf_out, &err);
628         if (doutp == NULL) {
629             ret = err;
630             goto done;
631         }
632         dout_len = op->dataout_len;
633         if (op->verbose > 2)
634             pr2serr("dxfer_buffer_out=%p, length=%d\n",
635                     (void *)doutp, dout_len);
636         set_scsi_pt_data_out(ptvp, doutp, dout_len);
637         if (op->cmdfile_given) {
638             if (NVME_ADDR_DATA_OUT ==
639                 sg_get_unaligned_le64(op->cdb + SG_NVME_PT_ADDR))
640                 sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)doutp,
641                                       op->cdb + SG_NVME_PT_ADDR);
642             if (NVME_DATA_LEN_DATA_OUT ==
643                 sg_get_unaligned_le32(op->cdb + SG_NVME_PT_DATA_LEN))
644                 sg_put_unaligned_le32(dout_len,
645                                       op->cdb + SG_NVME_PT_DATA_LEN);
646         }
647     }
648     if (op->do_datain) {
649         uint32_t din_len = op->datain_len;
650 
651         dinp = sg_memalign(din_len, 0 /* page_size */, &wrkBuf, false);
652         if (dinp == NULL) {
653             pr2serr("sg_memalign: failed to get %d bytes of memory\n",
654                     din_len);
655             ret = sg_convert_errno(ENOMEM);
656             goto done;
657         }
658         if (op->verbose > 2)
659             pr2serr("dxfer_buffer_in=%p, length=%d\n", (void *)dinp, din_len);
660         set_scsi_pt_data_in(ptvp, dinp, din_len);
661         if (op->cmdfile_given) {
662             if (NVME_ADDR_DATA_IN ==
663                 sg_get_unaligned_le64(op->cdb + SG_NVME_PT_ADDR))
664                 sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)dinp,
665                                       op->cdb + SG_NVME_PT_ADDR);
666             if (NVME_DATA_LEN_DATA_IN ==
667                 sg_get_unaligned_le32(op->cdb + SG_NVME_PT_DATA_LEN))
668                 sg_put_unaligned_le32(din_len,
669                                       op->cdb + SG_NVME_PT_DATA_LEN);
670         }
671     }
672     if (op->verbose) {
673         char d[128];
674 
675         pr2serr("    %s to send: ", is_scsi_cdb ? "cdb" : "cmd");
676         if (is_scsi_cdb) {
677             pr2serr("%s\n", sg_get_command_str(op->cdb, op->cdb_length,
678                                                op->verbose > 1,
679                                                sizeof(d), d));
680         } else {        /* If not SCSI cdb then treat as NVMe command */
681             pr2serr("\n");
682             hex2stderr(op->cdb, op->cdb_length, -1);
683             if (op->verbose > 1)
684                 pr2serr("  Command name: %s\n",
685                         sg_get_nvme_opcode_name(op->cdb[0], ! op->do_nvm,
686                                                 b_len, b));
687         }
688     }
689     set_scsi_pt_cdb(ptvp, op->cdb, op->cdb_length);
690     if (op->verbose > 2)
691         pr2serr("sense_buffer=%p, length=%d\n", (void *)sense_buffer,
692                 (int)sizeof(sense_buffer));
693     set_scsi_pt_sense(ptvp, sense_buffer, sizeof(sense_buffer));
694 
695     if (op->do_nvm)
696         ret = do_nvm_pt(ptvp, 0, op->timeout, op->verbose);
697     else
698         ret = do_scsi_pt(ptvp, -1, op->timeout, op->verbose);
699     if (ret > 0) {
700         switch (ret) {
701         case SCSI_PT_DO_BAD_PARAMS:
702             pr2serr("do_scsi_pt: bad pass through setup\n");
703             ret = SG_LIB_CAT_OTHER;
704             break;
705         case SCSI_PT_DO_TIMEOUT:
706             pr2serr("do_scsi_pt: timeout\n");
707             ret = SG_LIB_CAT_TIMEOUT;
708             break;
709         case SCSI_PT_DO_NVME_STATUS:
710             sct_sc = (uint16_t)get_scsi_pt_status_response(ptvp);
711             pr2serr("NVMe Status: %s [0x%x]\n",
712                     sg_get_nvme_cmd_status_str(sct_sc, b_len, b), sct_sc);
713             if (op->verbose) {
714                 result = get_pt_result(ptvp);
715                 pr2serr("NVMe Result=0x%x\n", result);
716                 s_len = get_scsi_pt_sense_len(ptvp);
717                 if ((op->verbose > 1) && (s_len > 0)) {
718                     pr2serr("NVMe completion queue 4 DWords (as byte "
719                             "string):\n");
720                     hex2stderr(sense_buffer, s_len, -1);
721                 }
722             }
723             break;
724         case SCSI_PT_DO_NOT_SUPPORTED:
725             pr2serr("do_scsi_pt: not supported\n");
726             ret = SG_LIB_CAT_TIMEOUT;
727             break;
728         default:
729             pr2serr("do_scsi_pt: unknown error: %d\n", ret);
730             ret = SG_LIB_CAT_OTHER;
731             break;
732         }
733         goto done;
734     } else if (ret < 0) {
735         k = -ret;
736         pr2serr("do_scsi_pt: %s\n", safe_strerror(k));
737         err = get_scsi_pt_os_err(ptvp);
738         if ((err != 0) && (err != k))
739             pr2serr("    ... or perhaps: %s\n", safe_strerror(err));
740         ret = sg_convert_errno(err);
741         goto done;
742     }
743 
744     s_len = get_scsi_pt_sense_len(ptvp);
745     if (is_scsi_cdb) {
746         res_cat = get_scsi_pt_result_category(ptvp);
747         switch (res_cat) {
748         case SCSI_PT_RESULT_GOOD:
749             ret = 0;
750             break;
751         case SCSI_PT_RESULT_SENSE:
752             ret = sg_err_category_sense(sense_buffer, s_len);
753             break;
754         case SCSI_PT_RESULT_TRANSPORT_ERR:
755             get_scsi_pt_transport_err_str(ptvp, b_len, b);
756             pr2serr(">>> transport error: %s\n", b);
757             ret = SG_LIB_CAT_OTHER;
758             break;
759         case SCSI_PT_RESULT_OS_ERR:
760             get_scsi_pt_os_err_str(ptvp, b_len, b);
761             pr2serr(">>> os error: %s\n", b);
762             ret = SG_LIB_CAT_OTHER;
763             break;
764         default:
765             pr2serr(">>> unknown pass through result category (%d)\n",
766                     res_cat);
767             ret = SG_LIB_CAT_OTHER;
768             break;
769         }
770 
771         status = get_scsi_pt_status_response(ptvp);
772         pr2serr("SCSI Status: ");
773         sg_print_scsi_status(status);
774         pr2serr("\n\n");
775         if ((SAM_STAT_CHECK_CONDITION == status) && (! op->no_sense)) {
776             if (0 == s_len)
777                 pr2serr(">>> Strange: status is CHECK CONDITION but no Sense "
778                         "Information\n");
779             else {
780                 pr2serr("Sense Information:\n");
781                 sg_print_sense(NULL, sense_buffer, s_len, (op->verbose > 0));
782                 pr2serr("\n");
783             }
784         }
785         if (SAM_STAT_RESERVATION_CONFLICT == status)
786             ret = SG_LIB_CAT_RES_CONFLICT;
787     } else {    /* NVMe command */
788         result = get_pt_result(ptvp);
789         pr2serr("NVMe Result=0x%x\n", result);
790         if (op->verbose && (s_len > 0)) {
791             pr2serr("NVMe completion queue 4 DWords (as byte string):\n");
792             hex2stderr(sense_buffer, s_len, -1);
793         }
794     }
795 
796     if (op->do_datain) {
797         int data_len = op->datain_len - get_scsi_pt_resid(ptvp);
798 
799         if (ret && !(SG_LIB_CAT_RECOVERED == ret ||
800                      SG_LIB_CAT_NO_SENSE == ret))
801             pr2serr("Error %d occurred, no data received\n", ret);
802         else if (data_len == 0) {
803             pr2serr("No data received\n");
804         } else {
805             if (op->datain_file == NULL && !op->datain_binary) {
806                 pr2serr("Received %d bytes of data:\n", data_len);
807                 hex2stderr(dinp, data_len, 0);
808             } else {
809                 const char * cp = "stdout";
810 
811                 if (op->datain_file &&
812                     ! ((1 == strlen(op->datain_file)) &&
813                        ('-' == op->datain_file[0])))
814                     cp = op->datain_file;
815                 pr2serr("Writing %d bytes of data to %s\n", data_len, cp);
816                 ret2 = write_dataout(op->datain_file, dinp,
817                                      data_len);
818                 if (0 != ret2) {
819                     if (0 == ret)
820                         ret = ret2;
821                     goto done;
822                 }
823             }
824         }
825     }
826 
827 done:
828     if (do_scan) {
829         ++op->scan_first;
830         if (op->scan_first <= op->scan_last) {
831             clear_scsi_pt_obj(ptvp);
832             goto and_again;
833         }
834     }
835 
836     if (op->verbose && is_scsi_cdb) {
837         sg_get_category_sense_str(ret, b_len, b, op->verbose - 1);
838         pr2serr("%s\n", b);
839     }
840     if (wrkBuf)
841         free(wrkBuf);
842     if (free_buf_out)
843         free(free_buf_out);
844     if (ptvp)
845         destruct_scsi_pt_obj(ptvp);
846     if (sg_fd >= 0)
847         scsi_pt_close_device(sg_fd);
848     return ret >= 0 ? ret : SG_LIB_CAT_OTHER;
849 }
850