xref: /aosp_15_r20/external/sg3_utils/lib/sg_cmds_basic2.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 <string.h>
23*44704f69SBart Van Assche #include <unistd.h>
24*44704f69SBart Van Assche #include <errno.h>
25*44704f69SBart Van Assche 
26*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
27*44704f69SBart Van Assche #include "config.h"
28*44704f69SBart Van Assche #endif
29*44704f69SBart Van Assche 
30*44704f69SBart Van Assche #include "sg_lib.h"
31*44704f69SBart Van Assche #include "sg_cmds_basic.h"
32*44704f69SBart Van Assche #include "sg_pt.h"
33*44704f69SBart Van Assche #include "sg_unaligned.h"
34*44704f69SBart Van Assche #include "sg_pr2serr.h"
35*44704f69SBart Van Assche 
36*44704f69SBart Van Assche 
37*44704f69SBart Van Assche 
38*44704f69SBart Van Assche #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
39*44704f69SBart Van Assche #define EBUFF_SZ 256
40*44704f69SBart Van Assche 
41*44704f69SBart Van Assche #define DEF_PT_TIMEOUT 60       /* 60 seconds */
42*44704f69SBart Van Assche #define START_PT_TIMEOUT 120    /* 120 seconds == 2 minutes */
43*44704f69SBart Van Assche #define LONG_PT_TIMEOUT 7200    /* 7,200 seconds == 120 minutes */
44*44704f69SBart Van Assche 
45*44704f69SBart Van Assche #define SYNCHRONIZE_CACHE_CMD     0x35
46*44704f69SBart Van Assche #define SYNCHRONIZE_CACHE_CMDLEN  10
47*44704f69SBart Van Assche #define SERVICE_ACTION_IN_16_CMD 0x9e
48*44704f69SBart Van Assche #define SERVICE_ACTION_IN_16_CMDLEN 16
49*44704f69SBart Van Assche #define READ_CAPACITY_16_SA 0x10
50*44704f69SBart Van Assche #define READ_CAPACITY_10_CMD 0x25
51*44704f69SBart Van Assche #define READ_CAPACITY_10_CMDLEN 10
52*44704f69SBart Van Assche #define MODE_SENSE6_CMD      0x1a
53*44704f69SBart Van Assche #define MODE_SENSE6_CMDLEN   6
54*44704f69SBart Van Assche #define MODE_SENSE10_CMD     0x5a
55*44704f69SBart Van Assche #define MODE_SENSE10_CMDLEN  10
56*44704f69SBart Van Assche #define MODE_SELECT6_CMD   0x15
57*44704f69SBart Van Assche #define MODE_SELECT6_CMDLEN   6
58*44704f69SBart Van Assche #define MODE_SELECT10_CMD   0x55
59*44704f69SBart Van Assche #define MODE_SELECT10_CMDLEN  10
60*44704f69SBart Van Assche #define LOG_SENSE_CMD     0x4d
61*44704f69SBart Van Assche #define LOG_SENSE_CMDLEN  10
62*44704f69SBart Van Assche #define LOG_SELECT_CMD     0x4c
63*44704f69SBart Van Assche #define LOG_SELECT_CMDLEN  10
64*44704f69SBart Van Assche #define START_STOP_CMD          0x1b
65*44704f69SBart Van Assche #define START_STOP_CMDLEN       6
66*44704f69SBart Van Assche #define PREVENT_ALLOW_CMD    0x1e
67*44704f69SBart Van Assche #define PREVENT_ALLOW_CMDLEN   6
68*44704f69SBart Van Assche 
69*44704f69SBart Van Assche #define MODE6_RESP_HDR_LEN 4
70*44704f69SBart Van Assche #define MODE10_RESP_HDR_LEN 8
71*44704f69SBart Van Assche #define MODE_RESP_ARB_LEN 1024
72*44704f69SBart Van Assche 
73*44704f69SBart Van Assche #define INQUIRY_RESP_INITIAL_LEN 36
74*44704f69SBart Van Assche 
75*44704f69SBart Van Assche 
76*44704f69SBart Van Assche static struct sg_pt_base *
create_pt_obj(const char * cname)77*44704f69SBart Van Assche create_pt_obj(const char * cname)
78*44704f69SBart Van Assche {
79*44704f69SBart Van Assche     struct sg_pt_base * ptvp = construct_scsi_pt_obj();
80*44704f69SBart Van Assche     if (NULL == ptvp)
81*44704f69SBart Van Assche         pr2ws("%s: out of memory\n", cname);
82*44704f69SBart Van Assche     return ptvp;
83*44704f69SBart Van Assche }
84*44704f69SBart Van Assche 
85*44704f69SBart Van Assche /* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success,
86*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
87*44704f69SBart Van Assche int
sg_ll_sync_cache_10(int sg_fd,bool sync_nv,bool immed,int group,unsigned int lba,unsigned int count,bool noisy,int verbose)88*44704f69SBart Van Assche sg_ll_sync_cache_10(int sg_fd, bool sync_nv, bool immed, int group,
89*44704f69SBart Van Assche                     unsigned int lba, unsigned int count, bool noisy,
90*44704f69SBart Van Assche                     int verbose)
91*44704f69SBart Van Assche {
92*44704f69SBart Van Assche     static const char * const cdb_s = "synchronize cache(10)";
93*44704f69SBart Van Assche     int res, ret, sense_cat;
94*44704f69SBart Van Assche     uint8_t sc_cdb[SYNCHRONIZE_CACHE_CMDLEN] =
95*44704f69SBart Van Assche                 {SYNCHRONIZE_CACHE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
96*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
97*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
98*44704f69SBart Van Assche 
99*44704f69SBart Van Assche     if (sync_nv)
100*44704f69SBart Van Assche         sc_cdb[1] |= 4;
101*44704f69SBart Van Assche     if (immed)
102*44704f69SBart Van Assche         sc_cdb[1] |= 2;
103*44704f69SBart Van Assche     sg_put_unaligned_be32((uint32_t)lba, sc_cdb + 2);
104*44704f69SBart Van Assche     sc_cdb[6] = group & GRPNUM_MASK;
105*44704f69SBart Van Assche     if (count > 0xffff) {
106*44704f69SBart Van Assche         pr2ws("count too big\n");
107*44704f69SBart Van Assche         return -1;
108*44704f69SBart Van Assche     }
109*44704f69SBart Van Assche     sg_put_unaligned_be16((int16_t)count, sc_cdb + 7);
110*44704f69SBart Van Assche 
111*44704f69SBart Van Assche     if (verbose) {
112*44704f69SBart Van Assche         char b[128];
113*44704f69SBart Van Assche 
114*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
115*44704f69SBart Van Assche               sg_get_command_str(sc_cdb, SYNCHRONIZE_CACHE_CMDLEN, false,
116*44704f69SBart Van Assche                                  sizeof(b), b));
117*44704f69SBart Van Assche     }
118*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
119*44704f69SBart Van Assche         return -1;
120*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, sc_cdb, sizeof(sc_cdb));
121*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
122*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
123*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
124*44704f69SBart Van Assche     if (-1 == ret) {
125*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
126*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
127*44704f69SBart Van Assche         else
128*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
129*44704f69SBart Van Assche     } else if (-2 == ret) {
130*44704f69SBart Van Assche         switch (sense_cat) {
131*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
132*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
133*44704f69SBart Van Assche             ret = 0;
134*44704f69SBart Van Assche             break;
135*44704f69SBart Van Assche         default:
136*44704f69SBart Van Assche             ret = sense_cat;
137*44704f69SBart Van Assche             break;
138*44704f69SBart Van Assche         }
139*44704f69SBart Van Assche     } else
140*44704f69SBart Van Assche         ret = 0;
141*44704f69SBart Van Assche 
142*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
143*44704f69SBart Van Assche     return ret;
144*44704f69SBart Van Assche }
145*44704f69SBart Van Assche 
146*44704f69SBart Van Assche /* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success,
147*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
148*44704f69SBart Van Assche int
sg_ll_readcap_16(int sg_fd,bool pmi,uint64_t llba,void * resp,int mx_resp_len,bool noisy,int verbose)149*44704f69SBart Van Assche sg_ll_readcap_16(int sg_fd, bool pmi, uint64_t llba, void * resp,
150*44704f69SBart Van Assche                  int mx_resp_len, bool noisy, int verbose)
151*44704f69SBart Van Assche {
152*44704f69SBart Van Assche     static const char * const cdb_s = "read capacity(16)";
153*44704f69SBart Van Assche     int ret, res, sense_cat;
154*44704f69SBart Van Assche     uint8_t rc_cdb[SERVICE_ACTION_IN_16_CMDLEN] =
155*44704f69SBart Van Assche                         {SERVICE_ACTION_IN_16_CMD, READ_CAPACITY_16_SA,
156*44704f69SBart Van Assche                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
157*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
158*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
159*44704f69SBart Van Assche 
160*44704f69SBart Van Assche     if (pmi) { /* lbs only valid when pmi set */
161*44704f69SBart Van Assche         rc_cdb[14] |= 1;
162*44704f69SBart Van Assche         sg_put_unaligned_be64(llba, rc_cdb + 2);
163*44704f69SBart Van Assche     }
164*44704f69SBart Van Assche     /* Allocation length, no guidance in SBC-2 rev 15b */
165*44704f69SBart Van Assche     sg_put_unaligned_be32((uint32_t)mx_resp_len, rc_cdb + 10);
166*44704f69SBart Van Assche     if (verbose) {
167*44704f69SBart Van Assche         char b[128];
168*44704f69SBart Van Assche 
169*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
170*44704f69SBart Van Assche               sg_get_command_str(rc_cdb, SERVICE_ACTION_IN_16_CMDLEN, false,
171*44704f69SBart Van Assche                                  sizeof(b), b));
172*44704f69SBart Van Assche     }
173*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
174*44704f69SBart Van Assche         return -1;
175*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb));
176*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
177*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
178*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
179*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
180*44704f69SBart Van Assche     if (-1 == ret) {
181*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
182*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
183*44704f69SBart Van Assche         else
184*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
185*44704f69SBart Van Assche     } else if (-2 == ret) {
186*44704f69SBart Van Assche         switch (sense_cat) {
187*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
188*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
189*44704f69SBart Van Assche             ret = 0;
190*44704f69SBart Van Assche             break;
191*44704f69SBart Van Assche         default:
192*44704f69SBart Van Assche             ret = sense_cat;
193*44704f69SBart Van Assche             break;
194*44704f69SBart Van Assche         }
195*44704f69SBart Van Assche     } else
196*44704f69SBart Van Assche         ret = 0;
197*44704f69SBart Van Assche 
198*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
199*44704f69SBart Van Assche     return ret;
200*44704f69SBart Van Assche }
201*44704f69SBart Van Assche 
202*44704f69SBart Van Assche /* Invokes a SCSI READ CAPACITY (10) command. Returns 0 -> success,
203*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
204*44704f69SBart Van Assche int
sg_ll_readcap_10(int sg_fd,bool pmi,unsigned int lba,void * resp,int mx_resp_len,bool noisy,int verbose)205*44704f69SBart Van Assche sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp,
206*44704f69SBart Van Assche                  int mx_resp_len, bool noisy, int verbose)
207*44704f69SBart Van Assche {
208*44704f69SBart Van Assche     static const char * const cdb_s = "read capacity(10)";
209*44704f69SBart Van Assche     int ret, res, sense_cat;
210*44704f69SBart Van Assche     uint8_t rc_cdb[READ_CAPACITY_10_CMDLEN] =
211*44704f69SBart Van Assche                          {READ_CAPACITY_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
212*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
213*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
214*44704f69SBart Van Assche 
215*44704f69SBart Van Assche     if (pmi) { /* lbs only valid when pmi set */
216*44704f69SBart Van Assche         rc_cdb[8] |= 1;
217*44704f69SBart Van Assche         sg_put_unaligned_be32((uint32_t)lba, rc_cdb + 2);
218*44704f69SBart Van Assche     }
219*44704f69SBart Van Assche     if (verbose) {
220*44704f69SBart Van Assche         char b[128];
221*44704f69SBart Van Assche 
222*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
223*44704f69SBart Van Assche               sg_get_command_str(rc_cdb, READ_CAPACITY_10_CMDLEN, false,
224*44704f69SBart Van Assche                                  sizeof(b), b));
225*44704f69SBart Van Assche     }
226*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
227*44704f69SBart Van Assche         return -1;
228*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb));
229*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
230*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
231*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
232*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
233*44704f69SBart Van Assche     if (-1 == ret) {
234*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
235*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
236*44704f69SBart Van Assche         else
237*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
238*44704f69SBart Van Assche     } else if (-2 == ret) {
239*44704f69SBart Van Assche         switch (sense_cat) {
240*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
241*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
242*44704f69SBart Van Assche             ret = 0;
243*44704f69SBart Van Assche             break;
244*44704f69SBart Van Assche         default:
245*44704f69SBart Van Assche             ret = sense_cat;
246*44704f69SBart Van Assche             break;
247*44704f69SBart Van Assche         }
248*44704f69SBart Van Assche     } else
249*44704f69SBart Van Assche         ret = 0;
250*44704f69SBart Van Assche 
251*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
252*44704f69SBart Van Assche     return ret;
253*44704f69SBart Van Assche }
254*44704f69SBart Van Assche 
255*44704f69SBart Van Assche /* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success,
256*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
257*44704f69SBart Van Assche int
sg_ll_mode_sense6(int sg_fd,bool dbd,int pc,int pg_code,int sub_pg_code,void * resp,int mx_resp_len,bool noisy,int verbose)258*44704f69SBart Van Assche sg_ll_mode_sense6(int sg_fd, bool dbd, int pc, int pg_code, int sub_pg_code,
259*44704f69SBart Van Assche                   void * resp, int mx_resp_len, bool noisy, int verbose)
260*44704f69SBart Van Assche {
261*44704f69SBart Van Assche     static const char * const cdb_s = "mode sense(6)";
262*44704f69SBart Van Assche     int res, ret, sense_cat, resid;
263*44704f69SBart Van Assche     uint8_t modes_cdb[MODE_SENSE6_CMDLEN] =
264*44704f69SBart Van Assche         {MODE_SENSE6_CMD, 0, 0, 0, 0, 0};
265*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
266*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
267*44704f69SBart Van Assche 
268*44704f69SBart Van Assche     modes_cdb[1] = (uint8_t)(dbd ? 0x8 : 0);
269*44704f69SBart Van Assche     modes_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
270*44704f69SBart Van Assche     modes_cdb[3] = (uint8_t)(sub_pg_code & 0xff);
271*44704f69SBart Van Assche     modes_cdb[4] = (uint8_t)(mx_resp_len & 0xff);
272*44704f69SBart Van Assche     if (mx_resp_len > 0xff) {
273*44704f69SBart Van Assche         pr2ws("mx_resp_len too big\n");
274*44704f69SBart Van Assche         return -1;
275*44704f69SBart Van Assche     }
276*44704f69SBart Van Assche     if (verbose) {
277*44704f69SBart Van Assche         char b[128];
278*44704f69SBart Van Assche 
279*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
280*44704f69SBart Van Assche               sg_get_command_str(modes_cdb, MODE_SENSE6_CMDLEN, false,
281*44704f69SBart Van Assche                                  sizeof(b), b));
282*44704f69SBart Van Assche     }
283*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
284*44704f69SBart Van Assche         return -1;
285*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb));
286*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
287*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
288*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
289*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
290*44704f69SBart Van Assche     resid = get_scsi_pt_resid(ptvp);
291*44704f69SBart Van Assche     if (-1 == ret) {
292*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
293*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
294*44704f69SBart Van Assche         else
295*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
296*44704f69SBart Van Assche     } else if (-2 == ret) {
297*44704f69SBart Van Assche         switch (sense_cat) {
298*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
299*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
300*44704f69SBart Van Assche             ret = 0;
301*44704f69SBart Van Assche             break;
302*44704f69SBart Van Assche         default:
303*44704f69SBart Van Assche             ret = sense_cat;
304*44704f69SBart Van Assche             break;
305*44704f69SBart Van Assche         }
306*44704f69SBart Van Assche     } else {
307*44704f69SBart Van Assche         if ((verbose > 2) && (ret > 0)) {
308*44704f69SBart Van Assche             pr2ws("    %s: response", cdb_s);
309*44704f69SBart Van Assche             if (3 == verbose) {
310*44704f69SBart Van Assche                 pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : ""));
311*44704f69SBart Van Assche                 hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret),
312*44704f69SBart Van Assche                            -1);
313*44704f69SBart Van Assche             } else {
314*44704f69SBart Van Assche                 pr2ws(":\n");
315*44704f69SBart Van Assche                 hex2stderr((const uint8_t *)resp, ret, 0);
316*44704f69SBart Van Assche             }
317*44704f69SBart Van Assche         }
318*44704f69SBart Van Assche         ret = 0;
319*44704f69SBart Van Assche     }
320*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
321*44704f69SBart Van Assche 
322*44704f69SBart Van Assche     if (resid > 0) {
323*44704f69SBart Van Assche         if (resid > mx_resp_len) {
324*44704f69SBart Van Assche             pr2ws("%s: resid (%d) should never exceed requested len=%d\n",
325*44704f69SBart Van Assche                   cdb_s, resid, mx_resp_len);
326*44704f69SBart Van Assche             return ret ? ret : SG_LIB_CAT_MALFORMED;
327*44704f69SBart Van Assche         }
328*44704f69SBart Van Assche         /* zero unfilled section of response buffer */
329*44704f69SBart Van Assche         memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid);
330*44704f69SBart Van Assche     }
331*44704f69SBart Van Assche     return ret;
332*44704f69SBart Van Assche }
333*44704f69SBart Van Assche 
334*44704f69SBart Van Assche /* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
335*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
336*44704f69SBart Van Assche int
sg_ll_mode_sense10(int sg_fd,bool llbaa,bool dbd,int pc,int pg_code,int sub_pg_code,void * resp,int mx_resp_len,bool noisy,int verbose)337*44704f69SBart Van Assche sg_ll_mode_sense10(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code,
338*44704f69SBart Van Assche                    int sub_pg_code, void * resp, int mx_resp_len,
339*44704f69SBart Van Assche                    bool noisy, int verbose)
340*44704f69SBart Van Assche {
341*44704f69SBart Van Assche     return sg_ll_mode_sense10_v2(sg_fd, llbaa, dbd, pc, pg_code, sub_pg_code,
342*44704f69SBart Van Assche                                  resp, mx_resp_len, 0, NULL, noisy, verbose);
343*44704f69SBart Van Assche }
344*44704f69SBart Van Assche 
345*44704f69SBart Van Assche /* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
346*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors.
347*44704f69SBart Van Assche  * Adds the ability to set the command abort timeout
348*44704f69SBart Van Assche  * and the ability to report the residual count. If timeout_secs is zero
349*44704f69SBart Van Assche  * or less the default command abort timeout (60 seconds) is used.
350*44704f69SBart Van Assche  * If residp is non-NULL then the residual value is written where residp
351*44704f69SBart Van Assche  * points. A residual value of 0 implies mx_resp_len bytes have be written
352*44704f69SBart Van Assche  * where resp points. If the residual value equals mx_resp_len then no
353*44704f69SBart Van Assche  * bytes have been written. */
354*44704f69SBart Van Assche int
sg_ll_mode_sense10_v2(int sg_fd,bool llbaa,bool dbd,int pc,int pg_code,int sub_pg_code,void * resp,int mx_resp_len,int timeout_secs,int * residp,bool noisy,int verbose)355*44704f69SBart Van Assche sg_ll_mode_sense10_v2(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code,
356*44704f69SBart Van Assche                       int sub_pg_code, void * resp, int mx_resp_len,
357*44704f69SBart Van Assche                       int timeout_secs, int * residp, bool noisy, int verbose)
358*44704f69SBart Van Assche {
359*44704f69SBart Van Assche     int res, ret, sense_cat, resid;
360*44704f69SBart Van Assche     static const char * const cdb_s = "mode sense(10)";
361*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
362*44704f69SBart Van Assche     uint8_t modes_cdb[MODE_SENSE10_CMDLEN] =
363*44704f69SBart Van Assche         {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
364*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
365*44704f69SBart Van Assche 
366*44704f69SBart Van Assche     modes_cdb[1] = (uint8_t)((dbd ? 0x8 : 0) | (llbaa ? 0x10 : 0));
367*44704f69SBart Van Assche     modes_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
368*44704f69SBart Van Assche     modes_cdb[3] = (uint8_t)(sub_pg_code & 0xff);
369*44704f69SBart Van Assche     sg_put_unaligned_be16((int16_t)mx_resp_len, modes_cdb + 7);
370*44704f69SBart Van Assche     if (mx_resp_len > 0xffff) {
371*44704f69SBart Van Assche         pr2ws("mx_resp_len too big\n");
372*44704f69SBart Van Assche         goto gen_err;
373*44704f69SBart Van Assche     }
374*44704f69SBart Van Assche     if (verbose) {
375*44704f69SBart Van Assche         char b[128];
376*44704f69SBart Van Assche 
377*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
378*44704f69SBart Van Assche               sg_get_command_str(modes_cdb, MODE_SENSE10_CMDLEN, false,
379*44704f69SBart Van Assche                                  sizeof(b), b));
380*44704f69SBart Van Assche     }
381*44704f69SBart Van Assche     if (timeout_secs <= 0)
382*44704f69SBart Van Assche         timeout_secs = DEF_PT_TIMEOUT;
383*44704f69SBart Van Assche 
384*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
385*44704f69SBart Van Assche         goto gen_err;
386*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb));
387*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
388*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
389*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose);
390*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
391*44704f69SBart Van Assche     resid = get_scsi_pt_resid(ptvp);
392*44704f69SBart Van Assche     if (residp)
393*44704f69SBart Van Assche         *residp = resid;
394*44704f69SBart Van Assche     if (-1 == ret) {
395*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
396*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
397*44704f69SBart Van Assche         else
398*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
399*44704f69SBart Van Assche     } else if (-2 == ret) {
400*44704f69SBart Van Assche         switch (sense_cat) {
401*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
402*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
403*44704f69SBart Van Assche             ret = 0;
404*44704f69SBart Van Assche             break;
405*44704f69SBart Van Assche         default:
406*44704f69SBart Van Assche             ret = sense_cat;
407*44704f69SBart Van Assche             break;
408*44704f69SBart Van Assche         }
409*44704f69SBart Van Assche     } else {
410*44704f69SBart Van Assche         if ((verbose > 2) && (ret > 0)) {
411*44704f69SBart Van Assche             pr2ws("    %s: response", cdb_s);
412*44704f69SBart Van Assche             if (3 == verbose) {
413*44704f69SBart Van Assche                 pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : ""));
414*44704f69SBart Van Assche                 hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret),
415*44704f69SBart Van Assche                            -1);
416*44704f69SBart Van Assche             } else {
417*44704f69SBart Van Assche                 pr2ws(":\n");
418*44704f69SBart Van Assche                 hex2stderr((const uint8_t *)resp, ret, 0);
419*44704f69SBart Van Assche             }
420*44704f69SBart Van Assche         }
421*44704f69SBart Van Assche         ret = 0;
422*44704f69SBart Van Assche     }
423*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
424*44704f69SBart Van Assche 
425*44704f69SBart Van Assche     if (resid > 0) {
426*44704f69SBart Van Assche         if (resid > mx_resp_len) {
427*44704f69SBart Van Assche             pr2ws("%s: resid (%d) should never exceed requested len=%d\n",
428*44704f69SBart Van Assche                   cdb_s, resid, mx_resp_len);
429*44704f69SBart Van Assche             return ret ? ret : SG_LIB_CAT_MALFORMED;
430*44704f69SBart Van Assche         }
431*44704f69SBart Van Assche         /* zero unfilled section of response buffer */
432*44704f69SBart Van Assche         memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid);
433*44704f69SBart Van Assche     }
434*44704f69SBart Van Assche     return ret;
435*44704f69SBart Van Assche gen_err:
436*44704f69SBart Van Assche     if (residp)
437*44704f69SBart Van Assche         *residp = 0;
438*44704f69SBart Van Assche     return -1;
439*44704f69SBart Van Assche }
440*44704f69SBart Van Assche 
441*44704f69SBart Van Assche /* Invokes a SCSI MODE SELECT (6) command.  Return of 0 -> success,
442*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
443*44704f69SBart Van Assche int
sg_ll_mode_select6_v2(int sg_fd,bool pf,bool rtd,bool sp,void * paramp,int param_len,bool noisy,int verbose)444*44704f69SBart Van Assche sg_ll_mode_select6_v2(int sg_fd, bool pf, bool rtd, bool sp, void * paramp,
445*44704f69SBart Van Assche                       int param_len, bool noisy, int verbose)
446*44704f69SBart Van Assche {
447*44704f69SBart Van Assche     static const char * const cdb_s = "mode select(6)";
448*44704f69SBart Van Assche     int res, ret, sense_cat;
449*44704f69SBart Van Assche     uint8_t modes_cdb[MODE_SELECT6_CMDLEN] =
450*44704f69SBart Van Assche         {MODE_SELECT6_CMD, 0, 0, 0, 0, 0};
451*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
452*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
453*44704f69SBart Van Assche 
454*44704f69SBart Van Assche     modes_cdb[1] = (uint8_t)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0));
455*44704f69SBart Van Assche     if (rtd)
456*44704f69SBart Van Assche         modes_cdb[1] |= 0x2;
457*44704f69SBart Van Assche     modes_cdb[4] = (uint8_t)(param_len & 0xff);
458*44704f69SBart Van Assche     if (param_len > 0xff) {
459*44704f69SBart Van Assche         pr2ws("%s: param_len too big\n", cdb_s);
460*44704f69SBart Van Assche         return -1;
461*44704f69SBart Van Assche     }
462*44704f69SBart Van Assche     if (verbose) {
463*44704f69SBart Van Assche         char b[128];
464*44704f69SBart Van Assche 
465*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
466*44704f69SBart Van Assche               sg_get_command_str(modes_cdb, MODE_SELECT6_CMDLEN, false,
467*44704f69SBart Van Assche                                  sizeof(b), b));
468*44704f69SBart Van Assche     }
469*44704f69SBart Van Assche     if (verbose > 1) {
470*44704f69SBart Van Assche         pr2ws("    %s parameter list\n", cdb_s);
471*44704f69SBart Van Assche         hex2stderr((const uint8_t *)paramp, param_len, -1);
472*44704f69SBart Van Assche     }
473*44704f69SBart Van Assche 
474*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
475*44704f69SBart Van Assche         return -1;
476*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb));
477*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
478*44704f69SBart Van Assche     set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len);
479*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
480*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
481*44704f69SBart Van Assche     if (-1 == ret) {
482*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
483*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
484*44704f69SBart Van Assche         else
485*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
486*44704f69SBart Van Assche     } else if (-2 == ret) {
487*44704f69SBart Van Assche         switch (sense_cat) {
488*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
489*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
490*44704f69SBart Van Assche             ret = 0;
491*44704f69SBart Van Assche             break;
492*44704f69SBart Van Assche         default:
493*44704f69SBart Van Assche             ret = sense_cat;
494*44704f69SBart Van Assche             break;
495*44704f69SBart Van Assche         }
496*44704f69SBart Van Assche     } else
497*44704f69SBart Van Assche         ret = 0;
498*44704f69SBart Van Assche 
499*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
500*44704f69SBart Van Assche     return ret;
501*44704f69SBart Van Assche }
502*44704f69SBart Van Assche 
503*44704f69SBart Van Assche int
sg_ll_mode_select6(int sg_fd,bool pf,bool sp,void * paramp,int param_len,bool noisy,int verbose)504*44704f69SBart Van Assche sg_ll_mode_select6(int sg_fd, bool pf, bool sp, void * paramp, int param_len,
505*44704f69SBart Van Assche                    bool noisy, int verbose)
506*44704f69SBart Van Assche {
507*44704f69SBart Van Assche     return sg_ll_mode_select6_v2(sg_fd, pf, false, sp, paramp, param_len,
508*44704f69SBart Van Assche                                  noisy, verbose);
509*44704f69SBart Van Assche }
510*44704f69SBart Van Assche 
511*44704f69SBart Van Assche /* Invokes a SCSI MODE SELECT (10) command.  Return of 0 -> success,
512*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors,
513*44704f69SBart Van Assche  * v2 adds rtd (revert to defaults) bit (spc5r11).  */
514*44704f69SBart Van Assche int
sg_ll_mode_select10_v2(int sg_fd,bool pf,bool rtd,bool sp,void * paramp,int param_len,bool noisy,int verbose)515*44704f69SBart Van Assche sg_ll_mode_select10_v2(int sg_fd, bool pf, bool rtd, bool sp, void * paramp,
516*44704f69SBart Van Assche                        int param_len, bool noisy, int verbose)
517*44704f69SBart Van Assche {
518*44704f69SBart Van Assche     static const char * const cdb_s = "mode select(10)";
519*44704f69SBart Van Assche     int res, ret, sense_cat;
520*44704f69SBart Van Assche     uint8_t modes_cdb[MODE_SELECT10_CMDLEN] =
521*44704f69SBart Van Assche         {MODE_SELECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
522*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
523*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
524*44704f69SBart Van Assche 
525*44704f69SBart Van Assche     modes_cdb[1] = (uint8_t)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0));
526*44704f69SBart Van Assche     if (rtd)
527*44704f69SBart Van Assche         modes_cdb[1] |= 0x2;
528*44704f69SBart Van Assche     sg_put_unaligned_be16((int16_t)param_len, modes_cdb + 7);
529*44704f69SBart Van Assche     if (param_len > 0xffff) {
530*44704f69SBart Van Assche         pr2ws("%s: param_len too big\n", cdb_s);
531*44704f69SBart Van Assche         return -1;
532*44704f69SBart Van Assche     }
533*44704f69SBart Van Assche     if (verbose) {
534*44704f69SBart Van Assche         char b[128];
535*44704f69SBart Van Assche 
536*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
537*44704f69SBart Van Assche               sg_get_command_str(modes_cdb, MODE_SELECT10_CMDLEN, false,
538*44704f69SBart Van Assche                                  sizeof(b), b));
539*44704f69SBart Van Assche     }
540*44704f69SBart Van Assche     if (verbose > 1) {
541*44704f69SBart Van Assche         pr2ws("    %s parameter list\n", cdb_s);
542*44704f69SBart Van Assche         hex2stderr((const uint8_t *)paramp, param_len, -1);
543*44704f69SBart Van Assche     }
544*44704f69SBart Van Assche 
545*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
546*44704f69SBart Van Assche         return -1;
547*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb));
548*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
549*44704f69SBart Van Assche     set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len);
550*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
551*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
552*44704f69SBart Van Assche     if (-1 == ret) {
553*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
554*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
555*44704f69SBart Van Assche         else
556*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
557*44704f69SBart Van Assche     } else if (-2 == ret) {
558*44704f69SBart Van Assche         switch (sense_cat) {
559*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
560*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
561*44704f69SBart Van Assche             ret = 0;
562*44704f69SBart Van Assche             break;
563*44704f69SBart Van Assche         default:
564*44704f69SBart Van Assche             ret = sense_cat;
565*44704f69SBart Van Assche             break;
566*44704f69SBart Van Assche         }
567*44704f69SBart Van Assche     } else
568*44704f69SBart Van Assche         ret = 0;
569*44704f69SBart Van Assche 
570*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
571*44704f69SBart Van Assche     return ret;
572*44704f69SBart Van Assche }
573*44704f69SBart Van Assche 
574*44704f69SBart Van Assche int
sg_ll_mode_select10(int sg_fd,bool pf,bool sp,void * paramp,int param_len,bool noisy,int verbose)575*44704f69SBart Van Assche sg_ll_mode_select10(int sg_fd, bool pf, bool sp, void * paramp,
576*44704f69SBart Van Assche                        int param_len, bool noisy, int verbose)
577*44704f69SBart Van Assche {
578*44704f69SBart Van Assche     return sg_ll_mode_select10_v2(sg_fd, pf, false, sp, paramp, param_len,
579*44704f69SBart Van Assche                                   noisy, verbose);
580*44704f69SBart Van Assche }
581*44704f69SBart Van Assche 
582*44704f69SBart Van Assche /* MODE SENSE commands yield a response that has header then zero or more
583*44704f69SBart Van Assche  * block descriptors followed by mode pages. In most cases users are
584*44704f69SBart Van Assche  * interested in the first mode page. This function returns the (byte)
585*44704f69SBart Van Assche  * offset of the start of the first mode page. Set mode_sense_6 to true for
586*44704f69SBart Van Assche  * MODE SENSE (6) and false for MODE SENSE (10). Returns >= 0 is successful
587*44704f69SBart Van Assche  * or -1 if failure. If there is a failure a message is written to err_buff
588*44704f69SBart Van Assche  * if it is non-NULL and err_buff_len > 0. */
589*44704f69SBart Van Assche int
sg_mode_page_offset(const uint8_t * resp,int resp_len,bool mode_sense_6,char * err_buff,int err_buff_len)590*44704f69SBart Van Assche sg_mode_page_offset(const uint8_t * resp, int resp_len,
591*44704f69SBart Van Assche                     bool mode_sense_6, char * err_buff, int err_buff_len)
592*44704f69SBart Van Assche {
593*44704f69SBart Van Assche     int bd_len, calc_len, offset;
594*44704f69SBart Van Assche     bool err_buff_ok = ((err_buff_len > 0) && err_buff);
595*44704f69SBart Van Assche 
596*44704f69SBart Van Assche     if ((NULL == resp) || (resp_len < 4))
597*44704f69SBart Van Assche             goto too_short;
598*44704f69SBart Van Assche     if (mode_sense_6) {
599*44704f69SBart Van Assche         calc_len = resp[0] + 1;
600*44704f69SBart Van Assche         bd_len = resp[3];
601*44704f69SBart Van Assche         offset = bd_len + MODE6_RESP_HDR_LEN;
602*44704f69SBart Van Assche     } else {    /* Mode sense(10) */
603*44704f69SBart Van Assche         if (resp_len < 8)
604*44704f69SBart Van Assche             goto too_short;
605*44704f69SBart Van Assche         calc_len = sg_get_unaligned_be16(resp) + 2;
606*44704f69SBart Van Assche         bd_len = sg_get_unaligned_be16(resp + 6);
607*44704f69SBart Van Assche         /* LongLBA doesn't change this calculation */
608*44704f69SBart Van Assche         offset = bd_len + MODE10_RESP_HDR_LEN;
609*44704f69SBart Van Assche     }
610*44704f69SBart Van Assche     if ((offset + 2) > calc_len) {
611*44704f69SBart Van Assche         if (err_buff_ok)
612*44704f69SBart Van Assche             snprintf(err_buff, err_buff_len, "calculated response "
613*44704f69SBart Van Assche                      "length too small, offset=%d calc_len=%d bd_len=%d\n",
614*44704f69SBart Van Assche                      offset, calc_len, bd_len);
615*44704f69SBart Van Assche         offset = -1;
616*44704f69SBart Van Assche     }
617*44704f69SBart Van Assche     return offset;
618*44704f69SBart Van Assche too_short:
619*44704f69SBart Van Assche     if (err_buff_ok)
620*44704f69SBart Van Assche         snprintf(err_buff, err_buff_len, "given MS(%d) response length (%d) "
621*44704f69SBart Van Assche                  "too short\n", (mode_sense_6 ? 6 : 10), resp_len);
622*44704f69SBart Van Assche     return -1;
623*44704f69SBart Van Assche }
624*44704f69SBart Van Assche 
625*44704f69SBart Van Assche /* MODE SENSE commands yield a response that has header then zero or more
626*44704f69SBart Van Assche  * block descriptors followed by mode pages. This functions returns the
627*44704f69SBart Van Assche  * length (in bytes) of those three components. Note that the return value
628*44704f69SBart Van Assche  * can exceed resp_len in which case the MODE SENSE command should be
629*44704f69SBart Van Assche  * re-issued with a larger response buffer. If bd_lenp is non-NULL and if
630*44704f69SBart Van Assche  * successful the block descriptor length (in bytes) is written to *bd_lenp.
631*44704f69SBart Van Assche  * Set mode_sense_6 to true for MODE SENSE (6) and false for MODE SENSE (10)
632*44704f69SBart Van Assche  * responses. Returns -1 if there is an error (e.g. response too short). */
633*44704f69SBart Van Assche int
sg_msense_calc_length(const uint8_t * resp,int resp_len,bool mode_sense_6,int * bd_lenp)634*44704f69SBart Van Assche sg_msense_calc_length(const uint8_t * resp, int resp_len,
635*44704f69SBart Van Assche                       bool mode_sense_6, int * bd_lenp)
636*44704f69SBart Van Assche {
637*44704f69SBart Van Assche     int calc_len;
638*44704f69SBart Van Assche 
639*44704f69SBart Van Assche     if (NULL == resp)
640*44704f69SBart Van Assche         goto an_err;
641*44704f69SBart Van Assche     if (mode_sense_6) {
642*44704f69SBart Van Assche         if (resp_len < 4)
643*44704f69SBart Van Assche             goto an_err;
644*44704f69SBart Van Assche         calc_len = resp[0] + 1;
645*44704f69SBart Van Assche     } else {
646*44704f69SBart Van Assche         if (resp_len < 8)
647*44704f69SBart Van Assche             goto an_err;
648*44704f69SBart Van Assche         calc_len = sg_get_unaligned_be16(resp + 0) + 2;
649*44704f69SBart Van Assche     }
650*44704f69SBart Van Assche     if (bd_lenp)
651*44704f69SBart Van Assche         *bd_lenp = mode_sense_6 ? resp[3] : sg_get_unaligned_be16(resp + 6);
652*44704f69SBart Van Assche     return calc_len;
653*44704f69SBart Van Assche an_err:
654*44704f69SBart Van Assche     if (bd_lenp)
655*44704f69SBart Van Assche         *bd_lenp = 0;
656*44704f69SBart Van Assche     return -1;
657*44704f69SBart Van Assche }
658*44704f69SBart Van Assche 
659*44704f69SBart Van Assche /* Fetches current, changeable, default and/or saveable modes pages as
660*44704f69SBart Van Assche  * indicated by pcontrol_arr for given pg_code and sub_pg_code. If
661*44704f69SBart Van Assche  * mode6==false then use MODE SENSE (10) else use MODE SENSE (6). If
662*44704f69SBart Van Assche  * flexible set and mode data length seems wrong then try and
663*44704f69SBart Van Assche  * fix (compensating hack for bad device or driver). pcontrol_arr
664*44704f69SBart Van Assche  * should have 4 elements for output of current, changeable, default
665*44704f69SBart Van Assche  * and saved values respectively. Each element should be NULL or
666*44704f69SBart Van Assche  * at least mx_mpage_len bytes long.
667*44704f69SBart Van Assche  * Return of 0 -> overall success, various SG_LIB_CAT_* positive values or
668*44704f69SBart Van Assche  * -1 -> other errors.
669*44704f69SBart Van Assche  * If success_mask pointer is not NULL then first zeros it. Then set bits
670*44704f69SBart Van Assche  * 0, 1, 2 and/or 3 if the current, changeable, default and saved values
671*44704f69SBart Van Assche  * respectively have been fetched. If error on current page
672*44704f69SBart Van Assche  * then stops and returns that error; otherwise continues if an error is
673*44704f69SBart Van Assche  * detected but returns the first error encountered.  */
674*44704f69SBart Van Assche int
sg_get_mode_page_controls(int sg_fd,bool mode6,int pg_code,int sub_pg_code,bool dbd,bool flexible,int mx_mpage_len,int * success_mask,void * pcontrol_arr[],int * reported_lenp,int verbose)675*44704f69SBart Van Assche sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code, int sub_pg_code,
676*44704f69SBart Van Assche                           bool dbd, bool flexible, int mx_mpage_len,
677*44704f69SBart Van Assche                           int * success_mask, void * pcontrol_arr[],
678*44704f69SBart Van Assche                           int * reported_lenp, int verbose)
679*44704f69SBart Van Assche {
680*44704f69SBart Van Assche     bool resp_mode6;
681*44704f69SBart Van Assche     int k, n, res, offset, calc_len, xfer_len;
682*44704f69SBart Van Assche     int resid = 0;
683*44704f69SBart Van Assche     const int msense10_hlen = MODE10_RESP_HDR_LEN;
684*44704f69SBart Van Assche     uint8_t buff[MODE_RESP_ARB_LEN];
685*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
686*44704f69SBart Van Assche     int first_err = 0;
687*44704f69SBart Van Assche 
688*44704f69SBart Van Assche     if (success_mask)
689*44704f69SBart Van Assche         *success_mask = 0;
690*44704f69SBart Van Assche     if (reported_lenp)
691*44704f69SBart Van Assche         *reported_lenp = 0;
692*44704f69SBart Van Assche     if (mx_mpage_len < 4)
693*44704f69SBart Van Assche         return 0;
694*44704f69SBart Van Assche     memset(ebuff, 0, sizeof(ebuff));
695*44704f69SBart Van Assche     /* first try to find length of current page response */
696*44704f69SBart Van Assche     memset(buff, 0, msense10_hlen);
697*44704f69SBart Van Assche     if (mode6)  /* want first 8 bytes just in case */
698*44704f69SBart Van Assche         res = sg_ll_mode_sense6(sg_fd, dbd, 0 /* pc */, pg_code,
699*44704f69SBart Van Assche                                 sub_pg_code, buff, msense10_hlen, true,
700*44704f69SBart Van Assche                                 verbose);
701*44704f69SBart Van Assche     else        /* MODE SENSE(10) obviously */
702*44704f69SBart Van Assche         res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, dbd,
703*44704f69SBart Van Assche                                     0 /* pc */, pg_code, sub_pg_code, buff,
704*44704f69SBart Van Assche                                     msense10_hlen, 0, &resid, true, verbose);
705*44704f69SBart Van Assche     if (0 != res)
706*44704f69SBart Van Assche         return res;
707*44704f69SBart Van Assche     n = buff[0];
708*44704f69SBart Van Assche     if (reported_lenp) {
709*44704f69SBart Van Assche         int m;
710*44704f69SBart Van Assche 
711*44704f69SBart Van Assche         m = sg_msense_calc_length(buff, msense10_hlen, mode6, NULL) - resid;
712*44704f69SBart Van Assche         if (m < 0)      /* Grrr, this should not happen */
713*44704f69SBart Van Assche             m = 0;
714*44704f69SBart Van Assche         *reported_lenp = m;
715*44704f69SBart Van Assche     }
716*44704f69SBart Van Assche     resp_mode6 = mode6;
717*44704f69SBart Van Assche     if (flexible) {
718*44704f69SBart Van Assche         if (mode6 && (n < 3)) {
719*44704f69SBart Van Assche             resp_mode6 = false;
720*44704f69SBart Van Assche             if (verbose)
721*44704f69SBart Van Assche                 pr2ws(">>> msense(6) but resp[0]=%d so try msense(10) "
722*44704f69SBart Van Assche                       "response processing\n", n);
723*44704f69SBart Van Assche         }
724*44704f69SBart Van Assche         if ((! mode6) && (n > 5)) {
725*44704f69SBart Van Assche             if ((n > 11) && (0 == (n % 2)) && (0 == buff[4]) &&
726*44704f69SBart Van Assche                 (0 == buff[5]) && (0 == buff[6])) {
727*44704f69SBart Van Assche                 buff[1] = n;
728*44704f69SBart Van Assche                 buff[0] = 0;
729*44704f69SBart Van Assche                 if (verbose)
730*44704f69SBart Van Assche                     pr2ws(">>> msense(10) but resp[0]=%d and not msense(6) "
731*44704f69SBart Van Assche                           "response so fix length\n", n);
732*44704f69SBart Van Assche             } else
733*44704f69SBart Van Assche                 resp_mode6 = true;
734*44704f69SBart Van Assche         }
735*44704f69SBart Van Assche     }
736*44704f69SBart Van Assche     if (verbose && (resp_mode6 != mode6))
737*44704f69SBart Van Assche         pr2ws(">>> msense(%d) but resp[0]=%d so switch response "
738*44704f69SBart Van Assche               "processing\n", (mode6 ? 6 : 10), buff[0]);
739*44704f69SBart Van Assche     calc_len = sg_msense_calc_length(buff, msense10_hlen, resp_mode6, NULL);
740*44704f69SBart Van Assche     if (calc_len > MODE_RESP_ARB_LEN)
741*44704f69SBart Van Assche         calc_len = MODE_RESP_ARB_LEN;
742*44704f69SBart Van Assche     offset = sg_mode_page_offset(buff, calc_len, resp_mode6, ebuff, EBUFF_SZ);
743*44704f69SBart Van Assche     if (offset < 0) {
744*44704f69SBart Van Assche         if (('\0' != ebuff[0]) && (verbose > 0))
745*44704f69SBart Van Assche             pr2ws("%s: %s\n", __func__, ebuff);
746*44704f69SBart Van Assche         return SG_LIB_CAT_MALFORMED;
747*44704f69SBart Van Assche     }
748*44704f69SBart Van Assche     xfer_len = calc_len - offset;
749*44704f69SBart Van Assche     if (xfer_len > mx_mpage_len)
750*44704f69SBart Van Assche         xfer_len = mx_mpage_len;
751*44704f69SBart Van Assche 
752*44704f69SBart Van Assche     for (k = 0; k < 4; ++k) {
753*44704f69SBart Van Assche         if (NULL == pcontrol_arr[k])
754*44704f69SBart Van Assche             continue;
755*44704f69SBart Van Assche         memset(pcontrol_arr[k], 0, mx_mpage_len);
756*44704f69SBart Van Assche         resid = 0;
757*44704f69SBart Van Assche         if (mode6)
758*44704f69SBart Van Assche             res = sg_ll_mode_sense6(sg_fd, dbd, k /* pc */,
759*44704f69SBart Van Assche                                     pg_code, sub_pg_code, buff,
760*44704f69SBart Van Assche                                     calc_len, true, verbose);
761*44704f69SBart Van Assche         else
762*44704f69SBart Van Assche             res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, dbd,
763*44704f69SBart Van Assche                                         k /* pc */, pg_code, sub_pg_code,
764*44704f69SBart Van Assche                                         buff, calc_len, 0, &resid, true,
765*44704f69SBart Van Assche                                         verbose);
766*44704f69SBart Van Assche         if (res || resid) {
767*44704f69SBart Van Assche             if (0 == first_err) {
768*44704f69SBart Van Assche                 if (res)
769*44704f69SBart Van Assche                     first_err = res;
770*44704f69SBart Van Assche                 else {
771*44704f69SBart Van Assche                     first_err = -49;    /* unexpected resid != 0 */
772*44704f69SBart Van Assche                     if (verbose)
773*44704f69SBart Van Assche                         pr2ws("%s: unexpected resid=%d, page=0x%x, "
774*44704f69SBart Van Assche                               "pcontrol=%d\n", __func__, resid, pg_code, k);
775*44704f69SBart Van Assche                 }
776*44704f69SBart Van Assche             }
777*44704f69SBart Van Assche             if (0 == k)
778*44704f69SBart Van Assche                 break;  /* if problem on current page, it won't improve */
779*44704f69SBart Van Assche             else
780*44704f69SBart Van Assche                 continue;
781*44704f69SBart Van Assche         }
782*44704f69SBart Van Assche         if (xfer_len > 0)
783*44704f69SBart Van Assche             memcpy(pcontrol_arr[k], buff + offset, xfer_len);
784*44704f69SBart Van Assche         if (success_mask)
785*44704f69SBart Van Assche             *success_mask |= (1 << k);
786*44704f69SBart Van Assche     }
787*44704f69SBart Van Assche     return first_err;
788*44704f69SBart Van Assche }
789*44704f69SBart Van Assche 
790*44704f69SBart Van Assche /* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
791*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors. */
792*44704f69SBart Van Assche int
sg_ll_log_sense(int sg_fd,bool ppc,bool sp,int pc,int pg_code,int subpg_code,int paramp,uint8_t * resp,int mx_resp_len,bool noisy,int verbose)793*44704f69SBart Van Assche sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
794*44704f69SBart Van Assche                 int subpg_code, int paramp, uint8_t * resp,
795*44704f69SBart Van Assche                 int mx_resp_len, bool noisy, int verbose)
796*44704f69SBart Van Assche {
797*44704f69SBart Van Assche     return sg_ll_log_sense_v2(sg_fd, ppc, sp, pc, pg_code, subpg_code,
798*44704f69SBart Van Assche                               paramp, resp, mx_resp_len, 0, NULL, noisy,
799*44704f69SBart Van Assche                               verbose);
800*44704f69SBart Van Assche }
801*44704f69SBart Van Assche 
802*44704f69SBart Van Assche /* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
803*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors.
804*44704f69SBart Van Assche  * Adds the ability to set the command abort timeout
805*44704f69SBart Van Assche  * and the ability to report the residual count. If timeout_secs is zero
806*44704f69SBart Van Assche  * or less the default command abort timeout (60 seconds) is used.
807*44704f69SBart Van Assche  * If residp is non-NULL then the residual value is written where residp
808*44704f69SBart Van Assche  * points. A residual value of 0 implies mx_resp_len bytes have be written
809*44704f69SBart Van Assche  * where resp points. If the residual value equals mx_resp_len then no
810*44704f69SBart Van Assche  * bytes have been written. */
811*44704f69SBart Van Assche int
sg_ll_log_sense_v2(int sg_fd,bool ppc,bool sp,int pc,int pg_code,int subpg_code,int paramp,uint8_t * resp,int mx_resp_len,int timeout_secs,int * residp,bool noisy,int verbose)812*44704f69SBart Van Assche sg_ll_log_sense_v2(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
813*44704f69SBart Van Assche                    int subpg_code, int paramp, uint8_t * resp,
814*44704f69SBart Van Assche                    int mx_resp_len, int timeout_secs, int * residp,
815*44704f69SBart Van Assche                    bool noisy, int verbose)
816*44704f69SBart Van Assche {
817*44704f69SBart Van Assche     static const char * const cdb_s = "log sense";
818*44704f69SBart Van Assche     int res, ret, sense_cat, resid;
819*44704f69SBart Van Assche     uint8_t logs_cdb[LOG_SENSE_CMDLEN] =
820*44704f69SBart Van Assche         {LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
821*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
822*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
823*44704f69SBart Van Assche 
824*44704f69SBart Van Assche     if (mx_resp_len > 0xffff) {
825*44704f69SBart Van Assche         pr2ws("mx_resp_len too big\n");
826*44704f69SBart Van Assche         goto gen_err;
827*44704f69SBart Van Assche     }
828*44704f69SBart Van Assche     logs_cdb[1] = (uint8_t)((ppc ? 2 : 0) | (sp ? 1 : 0));
829*44704f69SBart Van Assche     logs_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
830*44704f69SBart Van Assche     logs_cdb[3] = (uint8_t)(subpg_code & 0xff);
831*44704f69SBart Van Assche     sg_put_unaligned_be16((int16_t)paramp, logs_cdb + 5);
832*44704f69SBart Van Assche     sg_put_unaligned_be16((int16_t)mx_resp_len, logs_cdb + 7);
833*44704f69SBart Van Assche     if (verbose) {
834*44704f69SBart Van Assche         char b[128];
835*44704f69SBart Van Assche 
836*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
837*44704f69SBart Van Assche               sg_get_command_str(logs_cdb, LOG_SENSE_CMDLEN, false,
838*44704f69SBart Van Assche                                  sizeof(b), b));
839*44704f69SBart Van Assche     }
840*44704f69SBart Van Assche     if (timeout_secs <= 0)
841*44704f69SBart Van Assche         timeout_secs = DEF_PT_TIMEOUT;
842*44704f69SBart Van Assche 
843*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
844*44704f69SBart Van Assche         goto gen_err;
845*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, logs_cdb, sizeof(logs_cdb));
846*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
847*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, resp, mx_resp_len);
848*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose);
849*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
850*44704f69SBart Van Assche     resid = get_scsi_pt_resid(ptvp);
851*44704f69SBart Van Assche     if (residp)
852*44704f69SBart Van Assche         *residp = resid;
853*44704f69SBart Van Assche     if (-1 == ret) {
854*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
855*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
856*44704f69SBart Van Assche         else
857*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
858*44704f69SBart Van Assche     } else if (-2 == ret) {
859*44704f69SBart Van Assche         switch (sense_cat) {
860*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
861*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
862*44704f69SBart Van Assche             ret = 0;
863*44704f69SBart Van Assche             break;
864*44704f69SBart Van Assche         default:
865*44704f69SBart Van Assche             ret = sense_cat;
866*44704f69SBart Van Assche             break;
867*44704f69SBart Van Assche         }
868*44704f69SBart Van Assche     } else {
869*44704f69SBart Van Assche         if ((mx_resp_len > 3) && (ret < 4)) {
870*44704f69SBart Van Assche             /* resid indicates LOG SENSE response length bad, so zero it */
871*44704f69SBart Van Assche             resp[2] = 0;
872*44704f69SBart Van Assche             resp[3] = 0;
873*44704f69SBart Van Assche         }
874*44704f69SBart Van Assche         ret = 0;
875*44704f69SBart Van Assche     }
876*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
877*44704f69SBart Van Assche 
878*44704f69SBart Van Assche     if (resid > 0) {
879*44704f69SBart Van Assche         if (resid > mx_resp_len) {
880*44704f69SBart Van Assche             pr2ws("%s: resid (%d) should never exceed requested len=%d\n",
881*44704f69SBart Van Assche                   cdb_s, resid, mx_resp_len);
882*44704f69SBart Van Assche             return ret ? ret : SG_LIB_CAT_MALFORMED;
883*44704f69SBart Van Assche         }
884*44704f69SBart Van Assche         /* zero unfilled section of response buffer */
885*44704f69SBart Van Assche         memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid);
886*44704f69SBart Van Assche     }
887*44704f69SBart Van Assche     return ret;
888*44704f69SBart Van Assche gen_err:
889*44704f69SBart Van Assche     if (residp)
890*44704f69SBart Van Assche         *residp = 0;
891*44704f69SBart Van Assche     return -1;
892*44704f69SBart Van Assche }
893*44704f69SBart Van Assche 
894*44704f69SBart Van Assche /* Invokes a SCSI LOG SELECT command. Return of 0 -> success,
895*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
896*44704f69SBart Van Assche int
sg_ll_log_select(int sg_fd,bool pcr,bool sp,int pc,int pg_code,int subpg_code,uint8_t * paramp,int param_len,bool noisy,int verbose)897*44704f69SBart Van Assche sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code,
898*44704f69SBart Van Assche                  int subpg_code, uint8_t * paramp, int param_len,
899*44704f69SBart Van Assche                  bool noisy, int verbose)
900*44704f69SBart Van Assche {
901*44704f69SBart Van Assche     static const char * const cdb_s = "log select";
902*44704f69SBart Van Assche     int res, ret, sense_cat;
903*44704f69SBart Van Assche     uint8_t logs_cdb[LOG_SELECT_CMDLEN] =
904*44704f69SBart Van Assche         {LOG_SELECT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
905*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
906*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
907*44704f69SBart Van Assche 
908*44704f69SBart Van Assche     if (param_len > 0xffff) {
909*44704f69SBart Van Assche         pr2ws("%s: param_len too big\n", cdb_s);
910*44704f69SBart Van Assche         return -1;
911*44704f69SBart Van Assche     }
912*44704f69SBart Van Assche     logs_cdb[1] = (uint8_t)((pcr ? 2 : 0) | (sp ? 1 : 0));
913*44704f69SBart Van Assche     logs_cdb[2] = (uint8_t)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
914*44704f69SBart Van Assche     logs_cdb[3] = (uint8_t)(subpg_code & 0xff);
915*44704f69SBart Van Assche     sg_put_unaligned_be16((int16_t)param_len, logs_cdb + 7);
916*44704f69SBart Van Assche     if (verbose) {
917*44704f69SBart Van Assche         char b[128];
918*44704f69SBart Van Assche 
919*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
920*44704f69SBart Van Assche               sg_get_command_str(logs_cdb, LOG_SELECT_CMDLEN, false,
921*44704f69SBart Van Assche                                  sizeof(b), b));
922*44704f69SBart Van Assche     }
923*44704f69SBart Van Assche     if ((verbose > 1) && (param_len > 0)) {
924*44704f69SBart Van Assche         pr2ws("    %s parameter list\n", cdb_s);
925*44704f69SBart Van Assche         hex2stderr(paramp, param_len, -1);
926*44704f69SBart Van Assche     }
927*44704f69SBart Van Assche 
928*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
929*44704f69SBart Van Assche         return -1;
930*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, logs_cdb, sizeof(logs_cdb));
931*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
932*44704f69SBart Van Assche     set_scsi_pt_data_out(ptvp, paramp, param_len);
933*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
934*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
935*44704f69SBart Van Assche     if (-1 == ret) {
936*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
937*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
938*44704f69SBart Van Assche         else
939*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
940*44704f69SBart Van Assche     } else if (-2 == ret) {
941*44704f69SBart Van Assche         switch (sense_cat) {
942*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
943*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
944*44704f69SBart Van Assche             ret = 0;
945*44704f69SBart Van Assche             break;
946*44704f69SBart Van Assche         default:
947*44704f69SBart Van Assche             ret = sense_cat;
948*44704f69SBart Van Assche             break;
949*44704f69SBart Van Assche         }
950*44704f69SBart Van Assche     } else
951*44704f69SBart Van Assche         ret = 0;
952*44704f69SBart Van Assche 
953*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
954*44704f69SBart Van Assche     return ret;
955*44704f69SBart Van Assche }
956*44704f69SBart Van Assche 
957*44704f69SBart Van Assche /* Invokes a SCSI START STOP UNIT command (SBC + MMC).
958*44704f69SBart Van Assche  * Return of 0 -> success,
959*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors.
960*44704f69SBart Van Assche  * SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and
961*44704f69SBart Van Assche  * format_layer_number(mmc) fields. They also overlap on the noflush(sbc)
962*44704f69SBart Van Assche  * and fl(mmc) one bit field. This is the cause of the awkardly named
963*44704f69SBart Van Assche  * pc_mod__fl_num and noflush__fl arguments to this function.
964*44704f69SBart Van Assche  *  */
965*44704f69SBart Van Assche static int
sg_ll_start_stop_unit_com(struct sg_pt_base * ptvp,int sg_fd,bool immed,int pc_mod__fl_num,int power_cond,bool noflush__fl,bool loej,bool start,bool noisy,int verbose)966*44704f69SBart Van Assche sg_ll_start_stop_unit_com(struct sg_pt_base * ptvp, int sg_fd, bool immed,
967*44704f69SBart Van Assche                           int pc_mod__fl_num, int power_cond, bool noflush__fl,
968*44704f69SBart Van Assche                           bool loej, bool start, bool noisy, int verbose)
969*44704f69SBart Van Assche {
970*44704f69SBart Van Assche     static const char * const cdb_s = "start stop unit";
971*44704f69SBart Van Assche     bool ptvp_given = false;
972*44704f69SBart Van Assche     bool local_sense = true;
973*44704f69SBart Van Assche     bool local_cdb = true;
974*44704f69SBart Van Assche     int res, ret, sense_cat;
975*44704f69SBart Van Assche     uint8_t ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0};
976*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
977*44704f69SBart Van Assche 
978*44704f69SBart Van Assche     if (immed)
979*44704f69SBart Van Assche         ssuBlk[1] = 0x1;
980*44704f69SBart Van Assche     ssuBlk[3] = pc_mod__fl_num & 0xf;  /* bits 2 and 3 are reserved in MMC */
981*44704f69SBart Van Assche     ssuBlk[4] = ((power_cond & 0xf) << 4);
982*44704f69SBart Van Assche     if (noflush__fl)
983*44704f69SBart Van Assche         ssuBlk[4] |= 0x4;
984*44704f69SBart Van Assche     if (loej)
985*44704f69SBart Van Assche         ssuBlk[4] |= 0x2;
986*44704f69SBart Van Assche     if (start)
987*44704f69SBart Van Assche         ssuBlk[4] |= 0x1;
988*44704f69SBart Van Assche     if (verbose) {
989*44704f69SBart Van Assche         char b[128];
990*44704f69SBart Van Assche 
991*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
992*44704f69SBart Van Assche               sg_get_command_str(ssuBlk, sizeof(ssuBlk), false,
993*44704f69SBart Van Assche                                  sizeof(b), b));
994*44704f69SBart Van Assche     }
995*44704f69SBart Van Assche     if (ptvp) {
996*44704f69SBart Van Assche         ptvp_given = true;
997*44704f69SBart Van Assche         partial_clear_scsi_pt_obj(ptvp);
998*44704f69SBart Van Assche         if (get_scsi_pt_cdb_buf(ptvp))
999*44704f69SBart Van Assche             local_cdb = false; /* N.B. Ignores locally built cdb */
1000*44704f69SBart Van Assche         else
1001*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk));
1002*44704f69SBart Van Assche         if (get_scsi_pt_sense_buf(ptvp))
1003*44704f69SBart Van Assche             local_sense = false;
1004*44704f69SBart Van Assche         else
1005*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
1006*44704f69SBart Van Assche     } else {
1007*44704f69SBart Van Assche         ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
1008*44704f69SBart Van Assche         if (NULL == ptvp)
1009*44704f69SBart Van Assche             return sg_convert_errno(ENOMEM);
1010*44704f69SBart Van Assche         set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk));
1011*44704f69SBart Van Assche         set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
1012*44704f69SBart Van Assche     }
1013*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, -1, START_PT_TIMEOUT, verbose);
1014*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
1015*44704f69SBart Van Assche     if (-1 == ret) {
1016*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
1017*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
1018*44704f69SBart Van Assche         else
1019*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
1020*44704f69SBart Van Assche     } else if (-2 == ret) {
1021*44704f69SBart Van Assche         switch (sense_cat) {
1022*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
1023*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
1024*44704f69SBart Van Assche             ret = 0;
1025*44704f69SBart Van Assche             break;
1026*44704f69SBart Van Assche         default:
1027*44704f69SBart Van Assche             ret = sense_cat;
1028*44704f69SBart Van Assche             break;
1029*44704f69SBart Van Assche         }
1030*44704f69SBart Van Assche     } else
1031*44704f69SBart Van Assche             ret = 0;
1032*44704f69SBart Van Assche     if (ptvp_given) {
1033*44704f69SBart Van Assche         if (local_sense)    /* stop caller trying to access local sense */
1034*44704f69SBart Van Assche             set_scsi_pt_sense(ptvp, NULL, 0);
1035*44704f69SBart Van Assche         if (local_cdb)
1036*44704f69SBart Van Assche             set_scsi_pt_cdb(ptvp, NULL, 0);
1037*44704f69SBart Van Assche     } else {
1038*44704f69SBart Van Assche         if (ptvp)
1039*44704f69SBart Van Assche             destruct_scsi_pt_obj(ptvp);
1040*44704f69SBart Van Assche     }
1041*44704f69SBart Van Assche     return ret;
1042*44704f69SBart Van Assche }
1043*44704f69SBart Van Assche 
1044*44704f69SBart Van Assche int
sg_ll_start_stop_unit(int sg_fd,bool immed,int pc_mod__fl_num,int power_cond,bool noflush__fl,bool loej,bool start,bool noisy,int verbose)1045*44704f69SBart Van Assche sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num,
1046*44704f69SBart Van Assche                       int power_cond, bool noflush__fl, bool loej, bool start,
1047*44704f69SBart Van Assche                       bool noisy, int verbose)
1048*44704f69SBart Van Assche {
1049*44704f69SBart Van Assche     return sg_ll_start_stop_unit_com(NULL, sg_fd, immed, pc_mod__fl_num,
1050*44704f69SBart Van Assche                                      power_cond, noflush__fl, loej, start,
1051*44704f69SBart Van Assche                                      noisy, verbose);
1052*44704f69SBart Van Assche }
1053*44704f69SBart Van Assche 
1054*44704f69SBart Van Assche int
sg_ll_start_stop_unit_pt(struct sg_pt_base * ptvp,bool immed,int pc_mod__fl_num,int power_cond,bool noflush__fl,bool loej,bool start,bool noisy,int verbose)1055*44704f69SBart Van Assche sg_ll_start_stop_unit_pt(struct sg_pt_base * ptvp, bool immed,
1056*44704f69SBart Van Assche                          int pc_mod__fl_num, int power_cond, bool noflush__fl,
1057*44704f69SBart Van Assche                          bool loej, bool start, bool noisy, int verbose)
1058*44704f69SBart Van Assche {
1059*44704f69SBart Van Assche     return sg_ll_start_stop_unit_com(ptvp, -1, immed, pc_mod__fl_num,
1060*44704f69SBart Van Assche                                      power_cond, noflush__fl, loej, start,
1061*44704f69SBart Van Assche                                      noisy, verbose);
1062*44704f69SBart Van Assche }
1063*44704f69SBart Van Assche 
1064*44704f69SBart Van Assche /* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command
1065*44704f69SBart Van Assche  * [was in SPC-3 but displaced from SPC-4 into SBC-3, MMC-5, SSC-3]
1066*44704f69SBart Van Assche  * prevent==0 allows removal, prevent==1 prevents removal ...
1067*44704f69SBart Van Assche  * Return of 0 -> success,
1068*44704f69SBart Van Assche  * various SG_LIB_CAT_* positive values or -1 -> other errors */
1069*44704f69SBart Van Assche int
sg_ll_prevent_allow(int sg_fd,int prevent,bool noisy,int verbose)1070*44704f69SBart Van Assche sg_ll_prevent_allow(int sg_fd, int prevent, bool noisy, int verbose)
1071*44704f69SBart Van Assche {
1072*44704f69SBart Van Assche     static const char * const cdb_s = "prevent allow medium removal";
1073*44704f69SBart Van Assche     int res, ret, sense_cat;
1074*44704f69SBart Van Assche     uint8_t p_cdb[PREVENT_ALLOW_CMDLEN] =
1075*44704f69SBart Van Assche                 {PREVENT_ALLOW_CMD, 0, 0, 0, 0, 0};
1076*44704f69SBart Van Assche     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
1077*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
1078*44704f69SBart Van Assche 
1079*44704f69SBart Van Assche     if ((prevent < 0) || (prevent > 3)) {
1080*44704f69SBart Van Assche         pr2ws("prevent argument should be 0, 1, 2 or 3\n");
1081*44704f69SBart Van Assche         return -1;
1082*44704f69SBart Van Assche     }
1083*44704f69SBart Van Assche     p_cdb[4] |= (prevent & 0x3);
1084*44704f69SBart Van Assche     if (verbose) {
1085*44704f69SBart Van Assche         char b[128];
1086*44704f69SBart Van Assche 
1087*44704f69SBart Van Assche         pr2ws("    %s cdb: %s\n", cdb_s,
1088*44704f69SBart Van Assche               sg_get_command_str(p_cdb, PREVENT_ALLOW_CMDLEN, false,
1089*44704f69SBart Van Assche                                  sizeof(b), b));
1090*44704f69SBart Van Assche     }
1091*44704f69SBart Van Assche 
1092*44704f69SBart Van Assche     if (NULL == ((ptvp = create_pt_obj(cdb_s))))
1093*44704f69SBart Van Assche         return -1;
1094*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, p_cdb, sizeof(p_cdb));
1095*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
1096*44704f69SBart Van Assche     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
1097*44704f69SBart Van Assche     ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
1098*44704f69SBart Van Assche     if (-1 == ret) {
1099*44704f69SBart Van Assche         if (get_scsi_pt_transport_err(ptvp))
1100*44704f69SBart Van Assche             ret = SG_LIB_TRANSPORT_ERROR;
1101*44704f69SBart Van Assche         else
1102*44704f69SBart Van Assche             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
1103*44704f69SBart Van Assche     } else if (-2 == ret) {
1104*44704f69SBart Van Assche         switch (sense_cat) {
1105*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
1106*44704f69SBart Van Assche         case SG_LIB_CAT_NO_SENSE:
1107*44704f69SBart Van Assche             ret = 0;
1108*44704f69SBart Van Assche             break;
1109*44704f69SBart Van Assche         default:
1110*44704f69SBart Van Assche             ret = sense_cat;
1111*44704f69SBart Van Assche             break;
1112*44704f69SBart Van Assche         }
1113*44704f69SBart Van Assche     } else
1114*44704f69SBart Van Assche             ret = 0;
1115*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
1116*44704f69SBart Van Assche     return ret;
1117*44704f69SBart Van Assche }
1118