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