xref: /aosp_15_r20/external/sg3_utils/lib/sg_cmds_basic.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 /*
11*44704f69SBart Van Assche  * CONTENTS
12*44704f69SBart Van Assche  *    Some SCSI commands are executed in many contexts and hence began
13*44704f69SBart Van Assche  *    to appear in several sg3_utils utilities. This files centralizes
14*44704f69SBart Van Assche  *    some of the low level command execution code. In most cases the
15*44704f69SBart Van Assche  *    interpretation of the command response is left to the each
16*44704f69SBart Van Assche  *    utility.
17*44704f69SBart Van Assche  */
18*44704f69SBart Van Assche 
19*44704f69SBart Van Assche #include <stdio.h>
20*44704f69SBart Van Assche #include <stdlib.h>
21*44704f69SBart Van Assche #include <stdarg.h>
22*44704f69SBart Van Assche #include <stdbool.h>
23*44704f69SBart Van Assche #include <errno.h>
24*44704f69SBart Van Assche #include <string.h>
25*44704f69SBart Van Assche #include <unistd.h>
26*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
27*44704f69SBart Van Assche #include <inttypes.h>
28*44704f69SBart Van Assche 
29*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
30*44704f69SBart Van Assche #include "config.h"
31*44704f69SBart Van Assche #endif
32*44704f69SBart Van Assche 
33*44704f69SBart Van Assche #include "sg_lib.h"
34*44704f69SBart Van Assche #include "sg_cmds_basic.h"
35*44704f69SBart Van Assche #include "sg_pt.h"
36*44704f69SBart Van Assche #include "sg_unaligned.h"
37*44704f69SBart Van Assche #include "sg_pr2serr.h"
38*44704f69SBart Van Assche 
39*44704f69SBart Van Assche /* Needs to be after config.h */
40*44704f69SBart Van Assche #ifdef SG_LIB_LINUX
41*44704f69SBart Van Assche #include <errno.h>
42*44704f69SBart Van Assche #endif
43*44704f69SBart Van Assche 
44*44704f69SBart Van Assche 
45*44704f69SBart Van Assche static const char * const version_str = "2.00 20220118";
46*44704f69SBart Van Assche 
47*44704f69SBart Van Assche 
48*44704f69SBart Van Assche #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
49*44704f69SBart Van Assche #define EBUFF_SZ 256
50*44704f69SBart Van Assche 
51*44704f69SBart Van Assche #define DEF_PT_TIMEOUT 60       /* 60 seconds */
52*44704f69SBart Van Assche #define START_PT_TIMEOUT 120    /* 120 seconds == 2 minutes */
53*44704f69SBart Van Assche #define LONG_PT_TIMEOUT 7200    /* 7,200 seconds == 120 minutes */
54*44704f69SBart Van Assche 
55*44704f69SBart Van Assche #define INQUIRY_CMD     0x12
56*44704f69SBart Van Assche #define INQUIRY_CMDLEN  6
57*44704f69SBart Van Assche #define REQUEST_SENSE_CMD 0x3
58*44704f69SBart Van Assche #define REQUEST_SENSE_CMDLEN 6
59*44704f69SBart Van Assche #define REPORT_LUNS_CMD 0xa0
60*44704f69SBart Van Assche #define REPORT_LUNS_CMDLEN 12
61*44704f69SBart Van Assche #define TUR_CMD  0x0
62*44704f69SBart Van Assche #define TUR_CMDLEN  6
63*44704f69SBart Van Assche 
64*44704f69SBart Van Assche #define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */
65*44704f69SBart Van Assche 
66*44704f69SBart Van Assche 
67*44704f69SBart Van Assche const char *
sg_cmds_version()68*44704f69SBart Van Assche sg_cmds_version()
69*44704f69SBart Van Assche {
70*44704f69SBart Van Assche     return version_str;
71*44704f69SBart Van Assche }
72*44704f69SBart Van Assche 
73*44704f69SBart Van Assche /* Returns file descriptor >= 0 if successful. If error in Unix returns
74*44704f69SBart Van Assche    negated errno. */
75*44704f69SBart Van Assche int
sg_cmds_open_device(const char * device_name,bool read_only,int verbose)76*44704f69SBart Van Assche sg_cmds_open_device(const char * device_name, bool read_only, int verbose)
77*44704f69SBart Van Assche {
78*44704f69SBart Van Assche     return scsi_pt_open_device(device_name, read_only, verbose);
79*44704f69SBart Van Assche }
80*44704f69SBart Van Assche 
81*44704f69SBart Van Assche /* Returns file descriptor >= 0 if successful. If error in Unix returns
82*44704f69SBart Van Assche    negated errno. */
83*44704f69SBart Van Assche int
sg_cmds_open_flags(const char * device_name,int flags,int verbose)84*44704f69SBart Van Assche sg_cmds_open_flags(const char * device_name, int flags, int verbose)
85*44704f69SBart Van Assche {
86*44704f69SBart Van Assche     return scsi_pt_open_flags(device_name, flags, verbose);
87*44704f69SBart Van Assche }
88*44704f69SBart Van Assche 
89*44704f69SBart Van Assche /* Returns 0 if successful. If error in Unix returns negated errno. */
90*44704f69SBart Van Assche int
sg_cmds_close_device(int device_fd)91*44704f69SBart Van Assche sg_cmds_close_device(int device_fd)
92*44704f69SBart Van Assche {
93*44704f69SBart Van Assche     return scsi_pt_close_device(device_fd);
94*44704f69SBart Van Assche }
95*44704f69SBart Van Assche 
96*44704f69SBart Van Assche static const char * const pass_through_s = "pass-through";
97*44704f69SBart Van Assche 
98*44704f69SBart Van Assche static void
sg_cmds_resid_print(const char * leadin,bool is_din,int num_req,int num_got)99*44704f69SBart Van Assche sg_cmds_resid_print(const char * leadin, bool is_din, int num_req,
100*44704f69SBart Van Assche                     int num_got)
101*44704f69SBart Van Assche {
102*44704f69SBart Van Assche     pr2ws("    %s: %s requested %d bytes (data-%s  got %d "
103*44704f69SBart Van Assche           "bytes%s\n", leadin, pass_through_s,num_req,
104*44704f69SBart Van Assche           (is_din ? "in), got" : "out) but reported"), num_got,
105*44704f69SBart Van Assche           (is_din ? "" : " sent"));
106*44704f69SBart Van Assche }
107*44704f69SBart Van Assche 
108*44704f69SBart Van Assche static int
sg_cmds_process_helper(const char * leadin,int req_din_x,int act_din_x,int req_dout_x,int act_dout_x,const uint8_t * sbp,int slen,bool noisy,int verbose,int * o_sense_cat)109*44704f69SBart Van Assche sg_cmds_process_helper(const char * leadin, int req_din_x, int act_din_x,
110*44704f69SBart Van Assche                        int req_dout_x, int act_dout_x, const uint8_t * sbp,
111*44704f69SBart Van Assche                        int slen, bool noisy, int verbose, int * o_sense_cat)
112*44704f69SBart Van Assche {
113*44704f69SBart Van Assche     int scat;
114*44704f69SBart Van Assche     bool n = false;
115*44704f69SBart Van Assche     bool check_data_in = false;
116*44704f69SBart Van Assche 
117*44704f69SBart Van Assche     scat = sg_err_category_sense(sbp, slen);
118*44704f69SBart Van Assche     switch (scat) {
119*44704f69SBart Van Assche     case SG_LIB_CAT_NOT_READY:
120*44704f69SBart Van Assche     case SG_LIB_CAT_INVALID_OP:
121*44704f69SBart Van Assche     case SG_LIB_CAT_ILLEGAL_REQ:
122*44704f69SBart Van Assche     case SG_LIB_LBA_OUT_OF_RANGE:
123*44704f69SBart Van Assche     case SG_LIB_CAT_ABORTED_COMMAND:
124*44704f69SBart Van Assche     case SG_LIB_CAT_COPY_ABORTED:
125*44704f69SBart Van Assche     case SG_LIB_CAT_DATA_PROTECT:
126*44704f69SBart Van Assche     case SG_LIB_CAT_PROTECTION:
127*44704f69SBart Van Assche     case SG_LIB_CAT_NO_SENSE:
128*44704f69SBart Van Assche     case SG_LIB_CAT_MISCOMPARE:
129*44704f69SBart Van Assche     case SG_LIB_CAT_STANDBY:
130*44704f69SBart Van Assche     case SG_LIB_CAT_UNAVAILABLE:
131*44704f69SBart Van Assche         n = false;
132*44704f69SBart Van Assche         break;
133*44704f69SBart Van Assche     case SG_LIB_CAT_RECOVERED:
134*44704f69SBart Van Assche     case SG_LIB_CAT_MEDIUM_HARD:
135*44704f69SBart Van Assche         check_data_in = true;
136*44704f69SBart Van Assche #if defined(__GNUC__)
137*44704f69SBart Van Assche #if (__GNUC__ >= 7)
138*44704f69SBart Van Assche         __attribute__((fallthrough));
139*44704f69SBart Van Assche         /* FALL THROUGH */
140*44704f69SBart Van Assche #endif
141*44704f69SBart Van Assche #endif
142*44704f69SBart Van Assche     case SG_LIB_CAT_UNIT_ATTENTION:
143*44704f69SBart Van Assche     case SG_LIB_CAT_SENSE:
144*44704f69SBart Van Assche     default:
145*44704f69SBart Van Assche         n = noisy;
146*44704f69SBart Van Assche         break;
147*44704f69SBart Van Assche     }
148*44704f69SBart Van Assche     if (verbose || n) {
149*44704f69SBart Van Assche         char b[512];
150*44704f69SBart Van Assche 
151*44704f69SBart Van Assche         if (leadin && (strlen(leadin) > 0))
152*44704f69SBart Van Assche             pr2ws("%s:\n", leadin);
153*44704f69SBart Van Assche         sg_get_sense_str(NULL, sbp, slen, (verbose > 1),
154*44704f69SBart Van Assche                          sizeof(b), b);
155*44704f69SBart Van Assche         pr2ws("%s", b);
156*44704f69SBart Van Assche         if (req_din_x > 0) {
157*44704f69SBart Van Assche             if (act_din_x != req_din_x) {
158*44704f69SBart Van Assche                 if ((verbose > 2) || check_data_in || (act_din_x > 0))
159*44704f69SBart Van Assche                     sg_cmds_resid_print(leadin, true, req_din_x, act_din_x);
160*44704f69SBart Van Assche                 if (act_din_x < 0) {
161*44704f69SBart Van Assche                     if (verbose)
162*44704f69SBart Van Assche                         pr2ws("    %s: %s can't get negative bytes, say it "
163*44704f69SBart Van Assche                               "got none\n", leadin, pass_through_s);
164*44704f69SBart Van Assche                 }
165*44704f69SBart Van Assche             }
166*44704f69SBart Van Assche         }
167*44704f69SBart Van Assche         if (req_dout_x > 0) {
168*44704f69SBart Van Assche             if (act_dout_x != req_dout_x) {
169*44704f69SBart Van Assche                 if ((verbose > 1) && (act_dout_x > 0))
170*44704f69SBart Van Assche                     sg_cmds_resid_print(leadin, false, req_dout_x, act_dout_x);
171*44704f69SBart Van Assche                 if (act_dout_x < 0) {
172*44704f69SBart Van Assche                     if (verbose)
173*44704f69SBart Van Assche                         pr2ws("    %s: %s can't send negative bytes, say it "
174*44704f69SBart Van Assche                               "sent none\n", leadin, pass_through_s);
175*44704f69SBart Van Assche                 }
176*44704f69SBart Van Assche             }
177*44704f69SBart Van Assche         }
178*44704f69SBart Van Assche     }
179*44704f69SBart Van Assche     if (o_sense_cat)
180*44704f69SBart Van Assche         *o_sense_cat = scat;
181*44704f69SBart Van Assche     return -2;
182*44704f69SBart Van Assche }
183*44704f69SBart Van Assche 
184*44704f69SBart Van Assche /* This is a helper function used by sg_cmds_* implementations after the
185*44704f69SBart Van Assche  * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
186*44704f69SBart Van Assche  * sense data is found it is decoded and output to sg_warnings_strm (def:
187*44704f69SBart Van Assche  * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
188*44704f69SBart Van Assche  * o_sense_cat (sense category) written which may not be fatal. Returns
189*44704f69SBart Van Assche  * -1 for other types of failure. Returns 0, or a positive number. If data-in
190*44704f69SBart Van Assche  * type command (or bidi) then returns actual number of bytes read
191*44704f69SBart Van Assche  * (din_len - resid); otherwise returns 0. Note that several sense categories
192*44704f69SBart Van Assche  * also have data in bytes received; -2 is still returned. */
193*44704f69SBart Van Assche int
sg_cmds_process_resp(struct sg_pt_base * ptvp,const char * leadin,int pt_res,bool noisy,int verbose,int * o_sense_cat)194*44704f69SBart Van Assche sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
195*44704f69SBart Van Assche                      int pt_res, bool noisy, int verbose, int * o_sense_cat)
196*44704f69SBart Van Assche {
197*44704f69SBart Van Assche     int cat, slen, sstat, req_din_x, req_dout_x;
198*44704f69SBart Van Assche     int act_din_x, act_dout_x;
199*44704f69SBart Van Assche     const uint8_t * sbp;
200*44704f69SBart Van Assche     char b[1024];
201*44704f69SBart Van Assche 
202*44704f69SBart Van Assche     if (NULL == leadin)
203*44704f69SBart Van Assche         leadin = "";
204*44704f69SBart Van Assche     if (pt_res < 0) {
205*44704f69SBart Van Assche #ifdef SG_LIB_LINUX
206*44704f69SBart Van Assche         if (verbose)
207*44704f69SBart Van Assche             pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
208*44704f69SBart Van Assche                   safe_strerror(-pt_res));
209*44704f69SBart Van Assche         if ((-ENXIO == pt_res) && o_sense_cat) {
210*44704f69SBart Van Assche             if (verbose > 2)
211*44704f69SBart Van Assche                 pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n");
212*44704f69SBart Van Assche             *o_sense_cat = SG_LIB_CAT_NOT_READY;
213*44704f69SBart Van Assche             return -2;
214*44704f69SBart Van Assche         } else if (noisy && (0 == verbose))
215*44704f69SBart Van Assche             pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
216*44704f69SBart Van Assche                   safe_strerror(-pt_res));
217*44704f69SBart Van Assche #else
218*44704f69SBart Van Assche         if (noisy || verbose)
219*44704f69SBart Van Assche             pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
220*44704f69SBart Van Assche                   safe_strerror(-pt_res));
221*44704f69SBart Van Assche #endif
222*44704f69SBart Van Assche         return -1;
223*44704f69SBart Van Assche     } else if (SCSI_PT_DO_BAD_PARAMS == pt_res) {
224*44704f69SBart Van Assche         pr2ws("%s: bad %s setup\n", leadin, pass_through_s);
225*44704f69SBart Van Assche         return -1;
226*44704f69SBart Van Assche     } else if (SCSI_PT_DO_TIMEOUT == pt_res) {
227*44704f69SBart Van Assche         pr2ws("%s: %s timeout\n", leadin, pass_through_s);
228*44704f69SBart Van Assche         return -1;
229*44704f69SBart Van Assche     }
230*44704f69SBart Van Assche     if (verbose > 2) {
231*44704f69SBart Van Assche         uint64_t duration = get_pt_duration_ns(ptvp);
232*44704f69SBart Van Assche 
233*44704f69SBart Van Assche         if (duration > 0)
234*44704f69SBart Van Assche             pr2ws("      duration=%" PRIu64 " ns\n", duration);
235*44704f69SBart Van Assche         else {
236*44704f69SBart Van Assche             int d = get_scsi_pt_duration_ms(ptvp);
237*44704f69SBart Van Assche 
238*44704f69SBart Van Assche             if (d != -1)
239*44704f69SBart Van Assche                 pr2ws("      duration=%u ms\n", (uint32_t)d);
240*44704f69SBart Van Assche         }
241*44704f69SBart Van Assche     }
242*44704f69SBart Van Assche     get_pt_req_lengths(ptvp, &req_din_x, &req_dout_x);
243*44704f69SBart Van Assche     get_pt_actual_lengths(ptvp, &act_din_x, &act_dout_x);
244*44704f69SBart Van Assche     slen = get_scsi_pt_sense_len(ptvp);
245*44704f69SBart Van Assche     sbp = get_scsi_pt_sense_buf(ptvp);
246*44704f69SBart Van Assche     switch ((cat = get_scsi_pt_result_category(ptvp))) {
247*44704f69SBart Van Assche     case SCSI_PT_RESULT_GOOD:
248*44704f69SBart Van Assche         if (sbp && (slen > 7)) {
249*44704f69SBart Van Assche             int resp_code = sbp[0] & 0x7f;
250*44704f69SBart Van Assche 
251*44704f69SBart Van Assche             /* SBC referrals can have status=GOOD and sense_key=COMPLETED */
252*44704f69SBart Van Assche             if (resp_code >= 0x70) {
253*44704f69SBart Van Assche                 if (resp_code < 0x72) {
254*44704f69SBart Van Assche                     if (SPC_SK_NO_SENSE != (0xf & sbp[2]))
255*44704f69SBart Van Assche                         sg_err_category_sense(sbp, slen);
256*44704f69SBart Van Assche                 } else if (resp_code < 0x74) {
257*44704f69SBart Van Assche                     if (SPC_SK_NO_SENSE != (0xf & sbp[1]))
258*44704f69SBart Van Assche                         sg_err_category_sense(sbp, slen);
259*44704f69SBart Van Assche                 }
260*44704f69SBart Van Assche             }
261*44704f69SBart Van Assche         }
262*44704f69SBart Van Assche         if (req_din_x > 0) {
263*44704f69SBart Van Assche             if (act_din_x != req_din_x) {
264*44704f69SBart Van Assche                 if ((verbose > 1) && (act_din_x >= 0))
265*44704f69SBart Van Assche                     sg_cmds_resid_print(leadin, true, req_din_x, act_din_x);
266*44704f69SBart Van Assche                 if (act_din_x < 0) {
267*44704f69SBart Van Assche                     if (verbose)
268*44704f69SBart Van Assche                         pr2ws("    %s: %s can't get negative bytes, say it "
269*44704f69SBart Van Assche                               "got none\n", leadin, pass_through_s);
270*44704f69SBart Van Assche                     act_din_x = 0;
271*44704f69SBart Van Assche                 }
272*44704f69SBart Van Assche             }
273*44704f69SBart Van Assche         }
274*44704f69SBart Van Assche         if (req_dout_x > 0) {
275*44704f69SBart Van Assche             if (act_dout_x != req_dout_x) {
276*44704f69SBart Van Assche                 if ((verbose > 1) && (act_dout_x >= 0))
277*44704f69SBart Van Assche                     sg_cmds_resid_print(leadin, false, req_dout_x, act_dout_x);
278*44704f69SBart Van Assche                 if (act_dout_x < 0) {
279*44704f69SBart Van Assche                     if (verbose)
280*44704f69SBart Van Assche                         pr2ws("    %s: %s can't send negative bytes, say it "
281*44704f69SBart Van Assche                               "sent none\n", leadin, pass_through_s);
282*44704f69SBart Van Assche                     act_dout_x = 0;
283*44704f69SBart Van Assche                 }
284*44704f69SBart Van Assche             }
285*44704f69SBart Van Assche         }
286*44704f69SBart Van Assche         return act_din_x;
287*44704f69SBart Van Assche     case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
288*44704f69SBart Van Assche         sstat = get_scsi_pt_status_response(ptvp);
289*44704f69SBart Van Assche         if (o_sense_cat) {
290*44704f69SBart Van Assche             switch (sstat) {
291*44704f69SBart Van Assche             case SAM_STAT_RESERVATION_CONFLICT:
292*44704f69SBart Van Assche                 *o_sense_cat = SG_LIB_CAT_RES_CONFLICT;
293*44704f69SBart Van Assche                 return -2;
294*44704f69SBart Van Assche             case SAM_STAT_CONDITION_MET:
295*44704f69SBart Van Assche                 *o_sense_cat = SG_LIB_CAT_CONDITION_MET;
296*44704f69SBart Van Assche                 return -2;
297*44704f69SBart Van Assche             case SAM_STAT_BUSY:
298*44704f69SBart Van Assche                 *o_sense_cat = SG_LIB_CAT_BUSY;
299*44704f69SBart Van Assche                 return -2;
300*44704f69SBart Van Assche             case SAM_STAT_TASK_SET_FULL:
301*44704f69SBart Van Assche                 *o_sense_cat = SG_LIB_CAT_TS_FULL;
302*44704f69SBart Van Assche                 return -2;
303*44704f69SBart Van Assche             case SAM_STAT_ACA_ACTIVE:
304*44704f69SBart Van Assche                 *o_sense_cat = SG_LIB_CAT_ACA_ACTIVE;
305*44704f69SBart Van Assche                 return -2;
306*44704f69SBart Van Assche             case SAM_STAT_TASK_ABORTED:
307*44704f69SBart Van Assche                 *o_sense_cat = SG_LIB_CAT_TASK_ABORTED;
308*44704f69SBart Van Assche                 return -2;
309*44704f69SBart Van Assche             default:
310*44704f69SBart Van Assche                 break;
311*44704f69SBart Van Assche             }
312*44704f69SBart Van Assche         }
313*44704f69SBart Van Assche         if (verbose || noisy) {
314*44704f69SBart Van Assche             sg_get_scsi_status_str(sstat, sizeof(b), b);
315*44704f69SBart Van Assche             pr2ws("%s: scsi status: %s\n", leadin, b);
316*44704f69SBart Van Assche         }
317*44704f69SBart Van Assche         return -1;
318*44704f69SBart Van Assche     case SCSI_PT_RESULT_SENSE:
319*44704f69SBart Van Assche         return sg_cmds_process_helper(leadin, req_din_x, act_din_x,
320*44704f69SBart Van Assche                                       req_dout_x, act_dout_x, sbp, slen,
321*44704f69SBart Van Assche                                       noisy, verbose, o_sense_cat);
322*44704f69SBart Van Assche     case SCSI_PT_RESULT_TRANSPORT_ERR:
323*44704f69SBart Van Assche         if (verbose || noisy) {
324*44704f69SBart Van Assche             get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
325*44704f69SBart Van Assche             pr2ws("%s: transport: %s\n", leadin, b);
326*44704f69SBart Van Assche         }
327*44704f69SBart Van Assche #ifdef SG_LIB_LINUX
328*44704f69SBart Van Assche         return -1;      /* DRIVER_SENSE is not passed through */
329*44704f69SBart Van Assche #else
330*44704f69SBart Van Assche         /* Shall we favour sense data over a transport error (given both) */
331*44704f69SBart Van Assche         {
332*44704f69SBart Van Assche             bool favour_sense = ((SAM_STAT_CHECK_CONDITION ==
333*44704f69SBart Van Assche                     get_scsi_pt_status_response(ptvp)) && (slen > 0));
334*44704f69SBart Van Assche 
335*44704f69SBart Van Assche             if (favour_sense)
336*44704f69SBart Van Assche                 return sg_cmds_process_helper(leadin, req_din_x, act_din_x,
337*44704f69SBart Van Assche                                               req_dout_x, act_dout_x, sbp,
338*44704f69SBart Van Assche                                               slen, noisy, verbose,
339*44704f69SBart Van Assche                                               o_sense_cat);
340*44704f69SBart Van Assche             else
341*44704f69SBart Van Assche                 return -1;
342*44704f69SBart Van Assche         }
343*44704f69SBart Van Assche #endif
344*44704f69SBart Van Assche     case SCSI_PT_RESULT_OS_ERR:
345*44704f69SBart Van Assche         if (verbose || noisy) {
346*44704f69SBart Van Assche             get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
347*44704f69SBart Van Assche             pr2ws("%s: os: %s\n", leadin, b);
348*44704f69SBart Van Assche         }
349*44704f69SBart Van Assche         return -1;
350*44704f69SBart Van Assche     default:
351*44704f69SBart Van Assche         pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s,
352*44704f69SBart Van Assche                cat);
353*44704f69SBart Van Assche         return -1;
354*44704f69SBart Van Assche     }
355*44704f69SBart Van Assche }
356*44704f69SBart Van Assche 
357*44704f69SBart Van Assche bool
sg_cmds_is_nvme(const struct sg_pt_base * ptvp)358*44704f69SBart Van Assche sg_cmds_is_nvme(const struct sg_pt_base * ptvp)
359*44704f69SBart Van Assche {
360*44704f69SBart Van Assche     return pt_device_is_nvme(ptvp);
361*44704f69SBart Van Assche }
362*44704f69SBart Van Assche 
363*44704f69SBart Van Assche static struct sg_pt_base *
create_pt_obj(const char * cname)364*44704f69SBart Van Assche create_pt_obj(const char * cname)
365*44704f69SBart Van Assche {
366*44704f69SBart Van Assche     struct sg_pt_base * ptvp = construct_scsi_pt_obj();
367*44704f69SBart Van Assche     if (NULL == ptvp)
368*44704f69SBart Van Assche         pr2ws("%s: out of memory\n", cname);
369*44704f69SBart Van Assche     return ptvp;
370*44704f69SBart Van Assche }
371*44704f69SBart Van Assche 
372*44704f69SBart Van Assche static const char * const inquiry_s = "inquiry";
373*44704f69SBart Van Assche 
374*44704f69SBart Van Assche 
375*44704f69SBart Van Assche /* Returns 0 on success, while positive values are SG_LIB_CAT_* errors
376*44704f69SBart Van Assche  * (e.g. SG_LIB_CAT_MALFORMED). If OS error, returns negated errno or -1. */
377*44704f69SBart Van Assche static int
sg_ll_inquiry_com(struct sg_pt_base * ptvp,int sg_fd,bool cmddt,bool evpd,int pg_op,void * resp,int mx_resp_len,int timeout_secs,int * residp,bool noisy,int verbose)378*44704f69SBart Van Assche sg_ll_inquiry_com(struct sg_pt_base * ptvp, int sg_fd, bool cmddt, bool evpd,
379*44704f69SBart Van Assche                   int pg_op, void * resp, int mx_resp_len, int timeout_secs,
380*44704f69SBart Van Assche                   int * residp, bool noisy, int verbose)
381*44704f69SBart Van Assche {
382*44704f69SBart Van Assche     bool ptvp_given = false;
383*44704f69SBart Van Assche     bool local_sense = true;
384*44704f69SBart Van Assche     bool local_cdb = true;
385*44704f69SBart Van Assche     int res, ret, sense_cat, resid;
386*44704f69SBart Van Assche     uint8_t inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
387*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
388*44704f69SBart Van Assche     uint8_t * up;
389*44704f69SBart Van Assche 
390*44704f69SBart Van Assche     if (resp == NULL) {
391*44704f69SBart Van Assche         if (verbose)
392*44704f69SBart Van Assche             pr2ws("Got NULL `resp` pointer");
393*44704f69SBart Van Assche         return SG_LIB_CAT_MALFORMED;
394*44704f69SBart Van Assche     }
395*44704f69SBart Van Assche     if (cmddt)
396*44704f69SBart Van Assche         inq_cdb[1] |= 0x2;
397*44704f69SBart Van Assche     if (evpd)
398*44704f69SBart Van Assche         inq_cdb[1] |= 0x1;
399*44704f69SBart Van Assche     inq_cdb[2] = (uint8_t)pg_op;
400*44704f69SBart Van Assche     /* 16 bit allocation length (was 8, increased in spc3r09, 200209) */
401*44704f69SBart Van Assche     sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3);
402*44704f69SBart Van Assche     if (verbose) {
403*44704f69SBart Van Assche         char b[128];
404*44704f69SBart Van Assche 
405*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", inquiry_s,
406*44704f69SBart Van Assche               sg_get_command_str(inq_cdb, INQUIRY_CMDLEN, false, sizeof(b),
407*44704f69SBart Van Assche                                  b));
408*44704f69SBart Van Assche     }
409*44704f69SBart Van Assche     if (mx_resp_len > 0) {
410*44704f69SBart Van Assche         up = (uint8_t *)resp;
411*44704f69SBart Van Assche         up[0] = 0x7f;   /* defensive prefill */
412*44704f69SBart Van Assche         if (mx_resp_len > 4)
413*44704f69SBart Van Assche             up[4] = 0;
414*44704f69SBart Van Assche     }
415*44704f69SBart Van Assche     if (timeout_secs <= 0)
416*44704f69SBart Van Assche         timeout_secs = DEF_PT_TIMEOUT;
417*44704f69SBart Van Assche     if (ptvp) {
418*44704f69SBart Van Assche         ptvp_given = true;
419*44704f69SBart Van Assche         partial_clear_scsi_pt_obj(ptvp);
420*44704f69SBart Van Assche         if (get_scsi_pt_cdb_buf(ptvp))
421*44704f69SBart Van Assche             local_cdb = false; /* N.B. Ignores locally built cdb */
422*44704f69SBart Van Assche         else
423*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
424*44704f69SBart Van Assche         if (get_scsi_pt_sense_buf(ptvp))
425*44704f69SBart Van Assche             local_sense = false;
426*44704f69SBart Van Assche         else
427*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
428*44704f69SBart Van Assche     } else {
429*44704f69SBart Van Assche         ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
430*44704f69SBart Van Assche         if (NULL == ptvp)
431*44704f69SBart Van Assche             return sg_convert_errno(ENOMEM);
432*44704f69SBart Van Assche         set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
433*44704f69SBart Van Assche         set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
434*44704f69SBart Van Assche     }
435*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
436*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, -1, timeout_secs, verbose);
437*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, inquiry_s, res, noisy, verbose,
438*44704f69SBart Van Assche                                &sense_cat);
439*44704f69SBart Van Assche     resid = get_scsi_pt_resid(ptvp);
440*44704f69SBart Van Assche     if (residp)
441*44704f69SBart Van Assche         *residp = resid;
442*44704f69SBart Van Assche     if (-1 == ret) {
443*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
444*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
445*44704f69SBart Van Assche         else
446*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
447*44704f69SBart Van Assche     } else if (-2 == ret) {
448*44704f69SBart Van Assche         switch (sense_cat) {
449*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
450*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
451*44704f69SBart Van Assche             ret = 0;
452*44704f69SBart Van Assche             break;
453*44704f69SBart Van Assche         default:
454*44704f69SBart Van Assche             ret = sense_cat;
455*44704f69SBart Van Assche             break;
456*44704f69SBart Van Assche         }
457*44704f69SBart Van Assche     } else if (ret < 4) {
458*44704f69SBart Van Assche         if (verbose)
459*44704f69SBart Van Assche             pr2ws("%s: got too few bytes (%d)\n", __func__, ret);
460*44704f69SBart Van Assche         ret = SG_LIB_CAT_MALFORMED;
461*44704f69SBart Van Assche     } else
462*44704f69SBart Van Assche         ret = 0;
463*44704f69SBart Van Assche 
464*44704f69SBart Van Assche     if (resid > 0) {
465*44704f69SBart Van Assche         if (resid > mx_resp_len) {
466*44704f69SBart Van Assche             pr2ws("%s resid (%d) should never exceed requested "
467*44704f69SBart Van Assche                     "len=%d\n", inquiry_s, resid, mx_resp_len);
468*44704f69SBart Van Assche             if (0 == ret)
469*44704f69SBart Van Assche                 ret = SG_LIB_CAT_MALFORMED;
470*44704f69SBart Van Assche             goto fini;
471*44704f69SBart Van Assche         }
472*44704f69SBart Van Assche         /* zero unfilled section of response buffer, based on resid */
473*44704f69SBart Van Assche         memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid);
474*44704f69SBart Van Assche     }
475*44704f69SBart Van Assche fini:
476*44704f69SBart Van Assche     if (ptvp_given) {
477*44704f69SBart Van Assche         if (local_sense)    /* stop caller trying to access local sense */
478*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, NULL, 0);
479*44704f69SBart Van Assche         if (local_cdb)
480*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, NULL, 0);
481*44704f69SBart Van Assche     } else {
482*44704f69SBart Van Assche         if (ptvp)
483*44704f69SBart Van Assche             destruct_scsi_pt_obj(ptvp);
484*44704f69SBart Van Assche     }
485*44704f69SBart Van Assche     return ret;
486*44704f69SBart Van Assche }
487*44704f69SBart Van Assche 
488*44704f69SBart Van Assche /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
489*44704f69SBart Van Assche  * successful, various SG_LIB_CAT_* positive values, negated errno or
490*44704f69SBart Van Assche  * -1 -> other errors. The CMDDT field is obsolete in the INQUIRY cdb. */
491*44704f69SBart Van Assche int
sg_ll_inquiry(int sg_fd,bool cmddt,bool evpd,int pg_op,void * resp,int mx_resp_len,bool noisy,int verbose)492*44704f69SBart Van Assche sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
493*44704f69SBart Van Assche               int mx_resp_len, bool noisy, int verbose)
494*44704f69SBart Van Assche {
495*44704f69SBart Van Assche     return sg_ll_inquiry_com(NULL, sg_fd, cmddt, evpd, pg_op, resp,
496*44704f69SBart Van Assche                              mx_resp_len, 0 /* timeout_sec */, NULL, noisy,
497*44704f69SBart Van Assche                              verbose);
498*44704f69SBart Van Assche }
499*44704f69SBart Van Assche 
500*44704f69SBart Van Assche /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
501*44704f69SBart Van Assche  * successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
502*44704f69SBart Van Assche  * The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
503*44704f69SBart Van Assche  * an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
504*44704f69SBart Van Assche  * CODES command instead). Adds the ability to set the command abort timeout
505*44704f69SBart Van Assche  * and the ability to report the residual count. If timeout_secs is zero
506*44704f69SBart Van Assche  * or less the default command abort timeout (60 seconds) is used.
507*44704f69SBart Van Assche  * If residp is non-NULL then the residual value is written where residp
508*44704f69SBart Van Assche  * points. A residual value of 0 implies mx_resp_len bytes have be written
509*44704f69SBart Van Assche  * where resp points. If the residual value equals mx_resp_len then no
510*44704f69SBart Van Assche  * bytes have been written. */
511*44704f69SBart Van Assche int
sg_ll_inquiry_v2(int sg_fd,bool evpd,int pg_op,void * resp,int mx_resp_len,int timeout_secs,int * residp,bool noisy,int verbose)512*44704f69SBart Van Assche sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
513*44704f69SBart Van Assche                  int mx_resp_len, int timeout_secs, int * residp,
514*44704f69SBart Van Assche                  bool noisy, int verbose)
515*44704f69SBart Van Assche {
516*44704f69SBart Van Assche     return sg_ll_inquiry_com(NULL, sg_fd, false, evpd, pg_op, resp,
517*44704f69SBart Van Assche                              mx_resp_len, timeout_secs, residp, noisy,
518*44704f69SBart Van Assche                              verbose);
519*44704f69SBart Van Assche }
520*44704f69SBart Van Assche 
521*44704f69SBart Van Assche /* Similar to _v2 but takes a pointer to an object (derived from) sg_pt_base.
522*44704f69SBart Van Assche  * That object is assumed to be constructed and have a device file descriptor
523*44704f69SBart Van Assche  * associated with it. Caller is responsible for lifetime of ptp. */
524*44704f69SBart Van Assche int
sg_ll_inquiry_pt(struct sg_pt_base * ptvp,bool evpd,int pg_op,void * resp,int mx_resp_len,int timeout_secs,int * residp,bool noisy,int verbose)525*44704f69SBart Van Assche sg_ll_inquiry_pt(struct sg_pt_base * ptvp, bool evpd, int pg_op, void * resp,
526*44704f69SBart Van Assche                  int mx_resp_len, int timeout_secs, int * residp, bool noisy,
527*44704f69SBart Van Assche                  int verbose)
528*44704f69SBart Van Assche {
529*44704f69SBart Van Assche     return sg_ll_inquiry_com(ptvp, -1, false, evpd, pg_op, resp, mx_resp_len,
530*44704f69SBart Van Assche                              timeout_secs, residp, noisy, verbose);
531*44704f69SBart Van Assche }
532*44704f69SBart Van Assche 
533*44704f69SBart Van Assche /* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
534*44704f69SBart Van Assche  * Returns 0 when successful, various SG_LIB_CAT_* positive values, negated
535*44704f69SBart Van Assche  * errno or -1 -> other errors */
536*44704f69SBart Van Assche int
sg_simple_inquiry(int sg_fd,struct sg_simple_inquiry_resp * inq_data,bool noisy,int verbose)537*44704f69SBart Van Assche sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
538*44704f69SBart Van Assche                   bool noisy, int verbose)
539*44704f69SBart Van Assche {
540*44704f69SBart Van Assche     int ret;
541*44704f69SBart Van Assche     uint8_t * inq_resp = NULL;
542*44704f69SBart Van Assche     uint8_t * free_irp = NULL;
543*44704f69SBart Van Assche 
544*44704f69SBart Van Assche     if (inq_data) {
545*44704f69SBart Van Assche         memset(inq_data, 0, sizeof(* inq_data));
546*44704f69SBart Van Assche         inq_data->peripheral_qualifier = 0x3;
547*44704f69SBart Van Assche         inq_data->peripheral_type = PDT_UNKNOWN;
548*44704f69SBart Van Assche     }
549*44704f69SBart Van Assche     inq_resp = sg_memalign(SAFE_STD_INQ_RESP_LEN, 0, &free_irp, false);
550*44704f69SBart Van Assche     if (NULL == inq_resp) {
551*44704f69SBart Van Assche         pr2ws("%s: out of memory\n", __func__);
552*44704f69SBart Van Assche         return sg_convert_errno(ENOMEM);
553*44704f69SBart Van Assche     }
554*44704f69SBart Van Assche     ret = sg_ll_inquiry_com(NULL, sg_fd, false, false, 0, inq_resp,
555*44704f69SBart Van Assche                             SAFE_STD_INQ_RESP_LEN, 0, NULL, noisy, verbose);
556*44704f69SBart Van Assche 
557*44704f69SBart Van Assche     if (inq_data && (0 == ret)) {
558*44704f69SBart Van Assche         inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
559*44704f69SBart Van Assche         inq_data->peripheral_type = inq_resp[0] & PDT_MASK;
560*44704f69SBart Van Assche         inq_data->byte_1 = inq_resp[1];
561*44704f69SBart Van Assche         inq_data->version = inq_resp[2];
562*44704f69SBart Van Assche         inq_data->byte_3 = inq_resp[3];
563*44704f69SBart Van Assche         inq_data->byte_5 = inq_resp[5];
564*44704f69SBart Van Assche         inq_data->byte_6 = inq_resp[6];
565*44704f69SBart Van Assche         inq_data->byte_7 = inq_resp[7];
566*44704f69SBart Van Assche         memcpy(inq_data->vendor, inq_resp + 8, 8);
567*44704f69SBart Van Assche         memcpy(inq_data->product, inq_resp + 16, 16);
568*44704f69SBart Van Assche         memcpy(inq_data->revision, inq_resp + 32, 4);
569*44704f69SBart Van Assche     }
570*44704f69SBart Van Assche     if (free_irp)
571*44704f69SBart Van Assche         free(free_irp);
572*44704f69SBart Van Assche     return ret;
573*44704f69SBart Van Assche }
574*44704f69SBart Van Assche 
575*44704f69SBart Van Assche /* Similar to sg_simple_inquiry() but takes pointer to pt object rather
576*44704f69SBart Van Assche  * than device file descriptor. */
577*44704f69SBart Van Assche int
sg_simple_inquiry_pt(struct sg_pt_base * ptvp,struct sg_simple_inquiry_resp * inq_data,bool noisy,int verbose)578*44704f69SBart Van Assche sg_simple_inquiry_pt(struct sg_pt_base * ptvp,
579*44704f69SBart Van Assche                      struct sg_simple_inquiry_resp * inq_data,
580*44704f69SBart Van Assche                      bool noisy, int verbose)
581*44704f69SBart Van Assche {
582*44704f69SBart Van Assche     int ret;
583*44704f69SBart Van Assche     uint8_t * inq_resp = NULL;
584*44704f69SBart Van Assche     uint8_t * free_irp = NULL;
585*44704f69SBart Van Assche 
586*44704f69SBart Van Assche     if (inq_data) {
587*44704f69SBart Van Assche         memset(inq_data, 0, sizeof(* inq_data));
588*44704f69SBart Van Assche         inq_data->peripheral_qualifier = 0x3;
589*44704f69SBart Van Assche         inq_data->peripheral_type = PDT_MASK;
590*44704f69SBart Van Assche     }
591*44704f69SBart Van Assche     inq_resp = sg_memalign(SAFE_STD_INQ_RESP_LEN, 0, &free_irp, false);
592*44704f69SBart Van Assche     if (NULL == inq_resp) {
593*44704f69SBart Van Assche         pr2ws("%s: out of memory\n", __func__);
594*44704f69SBart Van Assche         return sg_convert_errno(ENOMEM);
595*44704f69SBart Van Assche     }
596*44704f69SBart Van Assche     ret = sg_ll_inquiry_com(ptvp, -1, false, false, 0, inq_resp,
597*44704f69SBart Van Assche                             SAFE_STD_INQ_RESP_LEN, 0, NULL, noisy, verbose);
598*44704f69SBart Van Assche 
599*44704f69SBart Van Assche     if (inq_data && (0 == ret)) {
600*44704f69SBart Van Assche         inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
601*44704f69SBart Van Assche         inq_data->peripheral_type = inq_resp[0] & PDT_MASK;
602*44704f69SBart Van Assche         inq_data->byte_1 = inq_resp[1];
603*44704f69SBart Van Assche         inq_data->version = inq_resp[2];
604*44704f69SBart Van Assche         inq_data->byte_3 = inq_resp[3];
605*44704f69SBart Van Assche         inq_data->byte_5 = inq_resp[5];
606*44704f69SBart Van Assche         inq_data->byte_6 = inq_resp[6];
607*44704f69SBart Van Assche         inq_data->byte_7 = inq_resp[7];
608*44704f69SBart Van Assche         memcpy(inq_data->vendor, inq_resp + 8, 8);
609*44704f69SBart Van Assche         memcpy(inq_data->product, inq_resp + 16, 16);
610*44704f69SBart Van Assche         memcpy(inq_data->revision, inq_resp + 32, 4);
611*44704f69SBart Van Assche     }
612*44704f69SBart Van Assche     if (free_irp)
613*44704f69SBart Van Assche         free(free_irp);
614*44704f69SBart Van Assche     return ret;
615*44704f69SBart Van Assche }
616*44704f69SBart Van Assche 
617*44704f69SBart Van Assche /* Invokes a SCSI TEST UNIT READY command.
618*44704f69SBart Van Assche  * N.B. To access the sense buffer outside this routine then one be
619*44704f69SBart Van Assche  * provided by the caller.
620*44704f69SBart Van Assche  * 'pack_id' is just for diagnostics, safe to set to 0.
621*44704f69SBart Van Assche  * Looks for progress indicator if 'progress' non-NULL;
622*44704f69SBart Van Assche  * if found writes value [0..65535] else write -1.
623*44704f69SBart Van Assche  * Returns 0 when successful, various SG_LIB_CAT_* positive values or
624*44704f69SBart Van Assche  * -1 -> other errors */
625*44704f69SBart Van Assche static int
sg_ll_test_unit_ready_com(struct sg_pt_base * ptvp,int sg_fd,int pack_id,int * progress,bool noisy,int verbose)626*44704f69SBart Van Assche sg_ll_test_unit_ready_com(struct sg_pt_base * ptvp, int sg_fd, int pack_id,
627*44704f69SBart Van Assche                           int * progress, bool noisy, int verbose)
628*44704f69SBart Van Assche {
629*44704f69SBart Van Assche     static const char * const tur_s = "test unit ready";
630*44704f69SBart Van Assche     bool ptvp_given = false;
631*44704f69SBart Van Assche     bool local_sense = true;
632*44704f69SBart Van Assche     bool local_cdb = true;
633*44704f69SBart Van Assche     int res, ret, sense_cat;
634*44704f69SBart Van Assche     uint8_t tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
635*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
636*44704f69SBart Van Assche 
637*44704f69SBart Van Assche     if (verbose) {
638*44704f69SBart Van Assche         char b[128];
639*44704f69SBart Van Assche 
640*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", tur_s,
641*44704f69SBart Van Assche               sg_get_command_str(tur_cdb, TUR_CMDLEN, false, sizeof(b), b));
642*44704f69SBart Van Assche     }
643*44704f69SBart Van Assche     if (ptvp) {
644*44704f69SBart Van Assche         ptvp_given = true;
645*44704f69SBart Van Assche         partial_clear_scsi_pt_obj(ptvp);
646*44704f69SBart Van Assche         if (get_scsi_pt_cdb_buf(ptvp))
647*44704f69SBart Van Assche             local_cdb = false; /* N.B. Ignores locally built cdb */
648*44704f69SBart Van Assche         else
649*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
650*44704f69SBart Van Assche         if (get_scsi_pt_sense_buf(ptvp))
651*44704f69SBart Van Assche             local_sense = false;
652*44704f69SBart Van Assche         else
653*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
654*44704f69SBart Van Assche     } else {
655*44704f69SBart Van Assche         ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
656*44704f69SBart Van Assche         if (NULL == ptvp)
657*44704f69SBart Van Assche             return sg_convert_errno(ENOMEM);
658*44704f69SBart Van Assche         set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
659*44704f69SBart Van Assche         set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
660*44704f69SBart Van Assche     }
661*44704f69SBart Van Assche     set_scsi_pt_packet_id(ptvp, pack_id);
662*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose);
663*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, tur_s, res, noisy, verbose, &sense_cat);
664*44704f69SBart Van Assche     if (-1 == ret) {
665*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
666*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
667*44704f69SBart Van Assche         else
668*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
669*44704f69SBart Van Assche     } else if (-2 == ret) {
670*44704f69SBart Van Assche         if (progress) {
671*44704f69SBart Van Assche             int slen = get_scsi_pt_sense_len(ptvp);
672*44704f69SBart Van Assche 
673*44704f69SBart Van Assche             if (! sg_get_sense_progress_fld(sense_b, slen, progress))
674*44704f69SBart Van Assche                 *progress = -1;
675*44704f69SBart Van Assche         }
676*44704f69SBart Van Assche         switch (sense_cat) {
677*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
678*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
679*44704f69SBart Van Assche             ret = 0;
680*44704f69SBart Van Assche             break;
681*44704f69SBart Van Assche         default:
682*44704f69SBart Van Assche             ret = sense_cat;
683*44704f69SBart Van Assche             break;
684*44704f69SBart Van Assche         }
685*44704f69SBart Van Assche     } else
686*44704f69SBart Van Assche         ret = 0;
687*44704f69SBart Van Assche     if (ptvp_given) {
688*44704f69SBart Van Assche         if (local_sense)    /* stop caller trying to access local sense */
689*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, NULL, 0);
690*44704f69SBart Van Assche         if (local_cdb)
691*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, NULL, 0);
692*44704f69SBart Van Assche     } else {
693*44704f69SBart Van Assche         if (ptvp)
694*44704f69SBart Van Assche             destruct_scsi_pt_obj(ptvp);
695*44704f69SBart Van Assche     }
696*44704f69SBart Van Assche     return ret;
697*44704f69SBart Van Assche }
698*44704f69SBart Van Assche 
699*44704f69SBart Van Assche int
sg_ll_test_unit_ready_progress_pt(struct sg_pt_base * ptvp,int pack_id,int * progress,bool noisy,int verbose)700*44704f69SBart Van Assche sg_ll_test_unit_ready_progress_pt(struct sg_pt_base * ptvp, int pack_id,
701*44704f69SBart Van Assche                                   int * progress, bool noisy, int verbose)
702*44704f69SBart Van Assche {
703*44704f69SBart Van Assche     return sg_ll_test_unit_ready_com(ptvp, -1, pack_id, progress, noisy,
704*44704f69SBart Van Assche                                      verbose);
705*44704f69SBart Van Assche }
706*44704f69SBart Van Assche 
707*44704f69SBart Van Assche int
sg_ll_test_unit_ready_progress(int sg_fd,int pack_id,int * progress,bool noisy,int verbose)708*44704f69SBart Van Assche sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
709*44704f69SBart Van Assche                                bool noisy, int verbose)
710*44704f69SBart Van Assche {
711*44704f69SBart Van Assche     return sg_ll_test_unit_ready_com(NULL, sg_fd, pack_id, progress, noisy,
712*44704f69SBart Van Assche                                      verbose);
713*44704f69SBart Van Assche }
714*44704f69SBart Van Assche 
715*44704f69SBart Van Assche /* Invokes a SCSI TEST UNIT READY command.
716*44704f69SBart Van Assche  * 'pack_id' is just for diagnostics, safe to set to 0.
717*44704f69SBart Van Assche  * Returns 0 when successful, various SG_LIB_CAT_* positive values or
718*44704f69SBart Van Assche  * -1 -> other errors */
719*44704f69SBart Van Assche int
sg_ll_test_unit_ready(int sg_fd,int pack_id,bool noisy,int verbose)720*44704f69SBart Van Assche sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose)
721*44704f69SBart Van Assche {
722*44704f69SBart Van Assche     return sg_ll_test_unit_ready_com(NULL, sg_fd, pack_id, NULL, noisy,
723*44704f69SBart Van Assche                                      verbose);
724*44704f69SBart Van Assche }
725*44704f69SBart Van Assche 
726*44704f69SBart Van Assche int
sg_ll_test_unit_ready_pt(struct sg_pt_base * ptvp,int pack_id,bool noisy,int verbose)727*44704f69SBart Van Assche sg_ll_test_unit_ready_pt(struct sg_pt_base * ptvp, int pack_id, bool noisy,
728*44704f69SBart Van Assche                          int verbose)
729*44704f69SBart Van Assche {
730*44704f69SBart Van Assche     return sg_ll_test_unit_ready_com(ptvp, -1, pack_id, NULL, noisy, verbose);
731*44704f69SBart Van Assche }
732*44704f69SBart Van Assche 
733*44704f69SBart Van Assche /* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various
734*44704f69SBart Van Assche  * SG_LIB_CAT_* positive values or -1 -> other errors */
735*44704f69SBart Van Assche static int
sg_ll_request_sense_com(struct sg_pt_base * ptvp,int sg_fd,bool desc,void * resp,int mx_resp_len,bool noisy,int verbose)736*44704f69SBart Van Assche sg_ll_request_sense_com(struct sg_pt_base * ptvp, int sg_fd, bool desc,
737*44704f69SBart Van Assche                         void * resp, int mx_resp_len, bool noisy, int verbose)
738*44704f69SBart Van Assche {
739*44704f69SBart Van Assche     bool ptvp_given = false;
740*44704f69SBart Van Assche     bool local_cdb = true;
741*44704f69SBart Van Assche     bool local_sense = true;
742*44704f69SBart Van Assche     int ret, res, sense_cat;
743*44704f69SBart Van Assche     static const char * const rq_s = "request sense";
744*44704f69SBart Van Assche     uint8_t rs_cdb[REQUEST_SENSE_CMDLEN] =
745*44704f69SBart Van Assche         {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
746*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
747*44704f69SBart Van Assche 
748*44704f69SBart Van Assche     if (desc)
749*44704f69SBart Van Assche         rs_cdb[1] |= 0x1;
750*44704f69SBart Van Assche     if (mx_resp_len > 0xff) {
751*44704f69SBart Van Assche         pr2ws("mx_resp_len cannot exceed 255\n");
752*44704f69SBart Van Assche         return -1;
753*44704f69SBart Van Assche     }
754*44704f69SBart Van Assche     rs_cdb[4] = mx_resp_len & 0xff;
755*44704f69SBart Van Assche     if (verbose) {
756*44704f69SBart Van Assche         char b[128];
757*44704f69SBart Van Assche 
758*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", rq_s,
759*44704f69SBart Van Assche               sg_get_command_str(rs_cdb, REQUEST_SENSE_CMDLEN, false,
760*44704f69SBart Van Assche                                  sizeof(b), b));
761*44704f69SBart Van Assche     }
762*44704f69SBart Van Assche     if (ptvp) {
763*44704f69SBart Van Assche         ptvp_given = true;
764*44704f69SBart Van Assche         if (get_scsi_pt_cdb_buf(ptvp))
765*44704f69SBart Van Assche             local_cdb = false;
766*44704f69SBart Van Assche         else
767*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
768*44704f69SBart Van Assche         if (get_scsi_pt_sense_buf(ptvp))
769*44704f69SBart Van Assche             local_sense = false;
770*44704f69SBart Van Assche         else
771*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
772*44704f69SBart Van Assche     } else {
773*44704f69SBart Van Assche         ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
774*44704f69SBart Van Assche         if (NULL == ptvp)
775*44704f69SBart Van Assche             return sg_convert_errno(ENOMEM);
776*44704f69SBart Van Assche         set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
777*44704f69SBart Van Assche         set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
778*44704f69SBart Van Assche     }
779*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
780*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose);
781*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, rq_s, res, noisy, verbose, &sense_cat);
782*44704f69SBart Van Assche     if (-1 == ret) {
783*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
784*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
785*44704f69SBart Van Assche         else
786*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
787*44704f69SBart Van Assche     } else if (-2 == ret) {
788*44704f69SBart Van Assche         switch (sense_cat) {
789*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
790*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
791*44704f69SBart Van Assche             ret = 0;
792*44704f69SBart Van Assche             break;
793*44704f69SBart Van Assche         default:
794*44704f69SBart Van Assche             ret = sense_cat;
795*44704f69SBart Van Assche             break;
796*44704f69SBart Van Assche         }
797*44704f69SBart Van Assche     } else {
798*44704f69SBart Van Assche         if ((mx_resp_len >= 8) && (ret < 8)) {
799*44704f69SBart Van Assche             if (verbose)
800*44704f69SBart Van Assche                 pr2ws("    %s: got %d bytes in response, too short\n", rq_s,
801*44704f69SBart Van Assche                       ret);
802*44704f69SBart Van Assche             ret = -1;
803*44704f69SBart Van Assche         } else
804*44704f69SBart Van Assche             ret = 0;
805*44704f69SBart Van Assche     }
806*44704f69SBart Van Assche     if (ptvp_given) {
807*44704f69SBart Van Assche         if (local_sense)        /* stop caller accessing local sense */
808*44704f69SBart Van Assche         set_scsi_pt_sense(ptvp, NULL, 0);
809*44704f69SBart Van Assche         if (local_cdb)  /* stop caller accessing local sense */
810*44704f69SBart Van Assche         set_scsi_pt_cdb(ptvp, NULL, 0);
811*44704f69SBart Van Assche     } else if (ptvp)
812*44704f69SBart Van Assche         destruct_scsi_pt_obj(ptvp);
813*44704f69SBart Van Assche     return ret;
814*44704f69SBart Van Assche }
815*44704f69SBart Van Assche 
816*44704f69SBart Van Assche int
sg_ll_request_sense(int sg_fd,bool desc,void * resp,int mx_resp_len,bool noisy,int verbose)817*44704f69SBart Van Assche sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
818*44704f69SBart Van Assche                     bool noisy, int verbose)
819*44704f69SBart Van Assche {
820*44704f69SBart Van Assche     return sg_ll_request_sense_com(NULL, sg_fd, desc, resp, mx_resp_len,
821*44704f69SBart Van Assche                                    noisy, verbose);
822*44704f69SBart Van Assche }
823*44704f69SBart Van Assche 
824*44704f69SBart Van Assche int
sg_ll_request_sense_pt(struct sg_pt_base * ptvp,bool desc,void * resp,int mx_resp_len,bool noisy,int verbose)825*44704f69SBart Van Assche sg_ll_request_sense_pt(struct sg_pt_base * ptvp, bool desc, void * resp,
826*44704f69SBart Van Assche                        int mx_resp_len, bool noisy, int verbose)
827*44704f69SBart Van Assche {
828*44704f69SBart Van Assche     return sg_ll_request_sense_com(ptvp, -1, desc, resp, mx_resp_len,
829*44704f69SBart Van Assche                                    noisy, verbose);
830*44704f69SBart Van Assche }
831*44704f69SBart Van Assche 
832*44704f69SBart Van Assche /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
833*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
834*44704f69SBart Van Assche static int
sg_ll_report_luns_com(struct sg_pt_base * ptvp,int sg_fd,int select_report,void * resp,int mx_resp_len,bool noisy,int verbose)835*44704f69SBart Van Assche sg_ll_report_luns_com(struct sg_pt_base * ptvp, int sg_fd, int select_report,
836*44704f69SBart Van Assche                       void * resp, int mx_resp_len, bool noisy, int verbose)
837*44704f69SBart Van Assche {
838*44704f69SBart Van Assche     static const char * const report_luns_s = "report luns";
839*44704f69SBart Van Assche     bool ptvp_given = false;
840*44704f69SBart Van Assche     bool local_cdb = true;
841*44704f69SBart Van Assche     bool local_sense = true;
842*44704f69SBart Van Assche     int ret, res, sense_cat;
843*44704f69SBart Van Assche     uint8_t rl_cdb[REPORT_LUNS_CMDLEN] =
844*44704f69SBart Van Assche                          {REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
845*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
846*44704f69SBart Van Assche 
847*44704f69SBart Van Assche     rl_cdb[2] = select_report & 0xff;
848*44704f69SBart Van Assche     sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6);
849*44704f69SBart Van Assche     if (verbose) {
850*44704f69SBart Van Assche         char b[128];
851*44704f69SBart Van Assche 
852*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", report_luns_s,
853*44704f69SBart Van Assche               sg_get_command_str(rl_cdb, REPORT_LUNS_CMDLEN, false,
854*44704f69SBart Van Assche                                  sizeof(b), b));
855*44704f69SBart Van Assche     }
856*44704f69SBart Van Assche     if (ptvp) {
857*44704f69SBart Van Assche         ptvp_given = true;
858*44704f69SBart Van Assche         partial_clear_scsi_pt_obj(ptvp);
859*44704f69SBart Van Assche         if (get_scsi_pt_cdb_buf(ptvp))
860*44704f69SBart Van Assche             local_cdb = false;
861*44704f69SBart Van Assche         else
862*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
863*44704f69SBart Van Assche         if (get_scsi_pt_sense_buf(ptvp))
864*44704f69SBart Van Assche             local_sense = false;
865*44704f69SBart Van Assche         else
866*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
867*44704f69SBart Van Assche     } else {
868*44704f69SBart Van Assche         if (NULL == ((ptvp = create_pt_obj(report_luns_s))))
869*44704f69SBart Van Assche             return sg_convert_errno(ENOMEM);
870*44704f69SBart Van Assche         set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
871*44704f69SBart Van Assche         set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
872*44704f69SBart Van Assche     }
873*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
874*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
875*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, report_luns_s, res, noisy, verbose,
876*44704f69SBart Van Assche                                &sense_cat);
877*44704f69SBart Van Assche     if (-1 == ret) {
878*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
879*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
880*44704f69SBart Van Assche         else
881*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
882*44704f69SBart Van Assche     } else if (-2 == ret) {
883*44704f69SBart Van Assche         switch (sense_cat) {
884*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
885*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
886*44704f69SBart Van Assche             ret = 0;
887*44704f69SBart Van Assche             break;
888*44704f69SBart Van Assche         default:
889*44704f69SBart Van Assche             ret = sense_cat;
890*44704f69SBart Van Assche             break;
891*44704f69SBart Van Assche         }
892*44704f69SBart Van Assche     } else
893*44704f69SBart Van Assche         ret = 0;
894*44704f69SBart Van Assche     if (ptvp_given) {
895*44704f69SBart Van Assche         if (local_sense)        /* stop caller accessing local sense */
896*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, NULL, 0);
897*44704f69SBart Van Assche         if (local_cdb)
898*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, NULL, 0);
899*44704f69SBart Van Assche     } else {
900*44704f69SBart Van Assche         if (ptvp)
901*44704f69SBart Van Assche             destruct_scsi_pt_obj(ptvp);
902*44704f69SBart Van Assche     }
903*44704f69SBart Van Assche     return ret;
904*44704f69SBart Van Assche }
905*44704f69SBart Van Assche 
906*44704f69SBart Van Assche /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
907*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors,
908*44704f69SBart Van Assche  * Expects sg_fd to be >= 0 representing an open device fd. */
909*44704f69SBart Van Assche int
sg_ll_report_luns(int sg_fd,int select_report,void * resp,int mx_resp_len,bool noisy,int verbose)910*44704f69SBart Van Assche sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len,
911*44704f69SBart Van Assche                   bool noisy, int verbose)
912*44704f69SBart Van Assche {
913*44704f69SBart Van Assche     return sg_ll_report_luns_com(NULL, sg_fd, select_report, resp,
914*44704f69SBart Van Assche                                  mx_resp_len, noisy, verbose);
915*44704f69SBart Van Assche }
916*44704f69SBart Van Assche 
917*44704f69SBart Van Assche 
918*44704f69SBart Van Assche /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
919*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors.
920*44704f69SBart Van Assche  * Expects a non-NULL ptvp containing an open device fd. */
921*44704f69SBart Van Assche int
sg_ll_report_luns_pt(struct sg_pt_base * ptvp,int select_report,void * resp,int mx_resp_len,bool noisy,int verbose)922*44704f69SBart Van Assche sg_ll_report_luns_pt(struct sg_pt_base * ptvp, int select_report,
923*44704f69SBart Van Assche                      void * resp, int mx_resp_len, bool noisy, int verbose)
924*44704f69SBart Van Assche {
925*44704f69SBart Van Assche     return sg_ll_report_luns_com(ptvp, -1, select_report, resp,
926*44704f69SBart Van Assche                                  mx_resp_len, noisy, verbose);
927*44704f69SBart Van Assche }
928