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