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