1 /*
2 * Copyright (c) 2008-2021 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 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <string.h>
14 #include <unistd.h>
15 #define __STDC_FORMAT_MACROS 1
16 #include <inttypes.h>
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "sg_lib.h"
23 #include "sg_cmds_basic.h"
24 #include "sg_cmds_mmc.h"
25 #include "sg_pt.h"
26 #include "sg_unaligned.h"
27 #include "sg_pr2serr.h"
28
29
30 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
31
32 #define DEF_PT_TIMEOUT 60 /* 60 seconds */
33
34 #define GET_CONFIG_CMD 0x46
35 #define GET_CONFIG_CMD_LEN 10
36 #define GET_PERFORMANCE_CMD 0xac
37 #define GET_PERFORMANCE_CMD_LEN 12
38 #define SET_CD_SPEED_CMD 0xbb
39 #define SET_CD_SPEED_CMDLEN 12
40 #define SET_STREAMING_CMD 0xb6
41 #define SET_STREAMING_CMDLEN 12
42
43
44 static struct sg_pt_base *
create_pt_obj(const char * cname)45 create_pt_obj(const char * cname)
46 {
47 struct sg_pt_base * ptvp = construct_scsi_pt_obj();
48 if (NULL == ptvp)
49 pr2ws("%s: out of memory\n", cname);
50 return ptvp;
51 }
52
53 /* Invokes a SCSI SET CD SPEED command (MMC).
54 * Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
55 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
56 * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
57 * -1 -> other failure */
58 int
sg_ll_set_cd_speed(int sg_fd,int rot_control,int drv_read_speed,int drv_write_speed,bool noisy,int verbose)59 sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
60 int drv_write_speed, bool noisy, int verbose)
61 {
62 static const char * const cdb_s = "set cd speed";
63 int res, ret, sense_cat;
64 uint8_t scsCmdBlk[SET_CD_SPEED_CMDLEN] = {SET_CD_SPEED_CMD, 0,
65 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};
66 uint8_t sense_b[SENSE_BUFF_LEN];
67 struct sg_pt_base * ptvp;
68
69 scsCmdBlk[1] |= (rot_control & 0x3);
70 sg_put_unaligned_be16((uint16_t)drv_read_speed, scsCmdBlk + 2);
71 sg_put_unaligned_be16((uint16_t)drv_write_speed, scsCmdBlk + 4);
72
73 if (verbose) {
74 int k;
75
76 pr2ws(" %s cdb: ", cdb_s);
77 for (k = 0; k < SET_CD_SPEED_CMDLEN; ++k)
78 pr2ws("%02x ", scsCmdBlk[k]);
79 pr2ws("\n");
80 }
81 if (NULL == ((ptvp = create_pt_obj(cdb_s))))
82 return -1;
83 set_scsi_pt_cdb(ptvp, scsCmdBlk, sizeof(scsCmdBlk));
84 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
85 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
86 ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
87 if (-1 == ret) {
88 if (get_scsi_pt_transport_err(ptvp))
89 ret = SG_LIB_TRANSPORT_ERROR;
90 else
91 ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
92 } else if (-2 == ret) {
93 switch (sense_cat) {
94 case SG_LIB_CAT_NOT_READY:
95 case SG_LIB_CAT_UNIT_ATTENTION:
96 case SG_LIB_CAT_INVALID_OP:
97 case SG_LIB_CAT_ILLEGAL_REQ:
98 case SG_LIB_CAT_ABORTED_COMMAND:
99 ret = sense_cat;
100 break;
101 case SG_LIB_CAT_RECOVERED:
102 case SG_LIB_CAT_NO_SENSE:
103 ret = 0;
104 break;
105 default:
106 ret = -1;
107 break;
108 }
109 } else
110 ret = 0;
111
112 destruct_scsi_pt_obj(ptvp);
113 return ret;
114 }
115
116 /* Invokes a SCSI GET CONFIGURATION command (MMC-3,4,5).
117 * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
118 * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
119 * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
120 int
sg_ll_get_config(int sg_fd,int rt,int starting,void * resp,int mx_resp_len,bool noisy,int verbose)121 sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
122 int mx_resp_len, bool noisy, int verbose)
123 {
124 static const char * const cdb_s = "get configuration";
125 int res, ret, sense_cat;
126 uint8_t gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0,
127 0, 0, 0, 0, 0, 0};
128 uint8_t sense_b[SENSE_BUFF_LEN];
129 struct sg_pt_base * ptvp;
130
131 if ((rt < 0) || (rt > 3)) {
132 pr2ws("Bad rt value: %d\n", rt);
133 return -1;
134 }
135 gcCmdBlk[1] = (rt & 0x3);
136 if ((starting < 0) || (starting > 0xffff)) {
137 pr2ws("Bad starting field number: 0x%x\n", starting);
138 return -1;
139 }
140 sg_put_unaligned_be16((uint16_t)starting, gcCmdBlk + 2);
141 if ((mx_resp_len < 0) || (mx_resp_len > 0xffff)) {
142 pr2ws("Bad mx_resp_len: 0x%x\n", starting);
143 return -1;
144 }
145 sg_put_unaligned_be16((uint16_t)mx_resp_len, gcCmdBlk + 7);
146
147 if (verbose) {
148 int k;
149
150 pr2ws(" %s cdb: ", cdb_s);
151 for (k = 0; k < GET_CONFIG_CMD_LEN; ++k)
152 pr2ws("%02x ", gcCmdBlk[k]);
153 pr2ws("\n");
154 }
155
156 if (NULL == ((ptvp = create_pt_obj(cdb_s))))
157 return -1;
158 set_scsi_pt_cdb(ptvp, gcCmdBlk, sizeof(gcCmdBlk));
159 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
160 set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
161 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
162 ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
163 if (-1 == ret) {
164 if (get_scsi_pt_transport_err(ptvp))
165 ret = SG_LIB_TRANSPORT_ERROR;
166 else
167 ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
168 } else if (-2 == ret) {
169 switch (sense_cat) {
170 case SG_LIB_CAT_INVALID_OP:
171 case SG_LIB_CAT_ILLEGAL_REQ:
172 case SG_LIB_CAT_UNIT_ATTENTION:
173 case SG_LIB_CAT_ABORTED_COMMAND:
174 ret = sense_cat;
175 break;
176 case SG_LIB_CAT_RECOVERED:
177 case SG_LIB_CAT_NO_SENSE:
178 ret = 0;
179 break;
180 default:
181 ret = -1;
182 break;
183 }
184 } else {
185 if ((verbose > 2) && (ret > 3)) {
186 uint8_t * bp;
187 int len;
188
189 bp = (uint8_t *)resp;
190 len = sg_get_unaligned_be32(bp + 0);
191 if (len < 0)
192 len = 0;
193 len = (ret < len) ? ret : len;
194 pr2ws(" %s: response:\n", cdb_s);
195 if (3 == verbose) {
196 pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : ""));
197 hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len),
198 -1);
199 } else {
200 pr2ws(":\n");
201 hex2stderr((const uint8_t *)resp, len, 0);
202 }
203 }
204 ret = 0;
205 }
206 destruct_scsi_pt_obj(ptvp);
207 return ret;
208 }
209
210 /* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
211 * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
212 * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
213 * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
214 int
sg_ll_get_performance(int sg_fd,int data_type,unsigned int starting_lba,int max_num_desc,int ttype,void * resp,int mx_resp_len,bool noisy,int verbose)215 sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
216 int max_num_desc, int ttype, void * resp,
217 int mx_resp_len, bool noisy, int verbose)
218 {
219 static const char * const cdb_s = "get performance";
220 int res, ret, sense_cat;
221 uint8_t gpCmdBlk[GET_PERFORMANCE_CMD_LEN] = {GET_PERFORMANCE_CMD, 0,
222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
223 uint8_t sense_b[SENSE_BUFF_LEN];
224 struct sg_pt_base * ptvp;
225
226 if ((data_type < 0) || (data_type > 0x1f)) {
227 pr2ws("Bad data_type value: %d\n", data_type);
228 return -1;
229 }
230 gpCmdBlk[1] = (data_type & 0x1f);
231 sg_put_unaligned_be32((uint32_t)starting_lba, gpCmdBlk + 2);
232 if ((max_num_desc < 0) || (max_num_desc > 0xffff)) {
233 pr2ws("Bad max_num_desc: 0x%x\n", max_num_desc);
234 return -1;
235 }
236 sg_put_unaligned_be16((uint16_t)max_num_desc, gpCmdBlk + 8);
237 if ((ttype < 0) || (ttype > 0xff)) {
238 pr2ws("Bad type: 0x%x\n", ttype);
239 return -1;
240 }
241 gpCmdBlk[10] = (uint8_t)ttype;
242
243 if (verbose) {
244 int k;
245
246 pr2ws(" %s cdb: ", cdb_s);
247 for (k = 0; k < GET_PERFORMANCE_CMD_LEN; ++k)
248 pr2ws("%02x ", gpCmdBlk[k]);
249 pr2ws("\n");
250 }
251
252 if (NULL == ((ptvp = create_pt_obj(cdb_s))))
253 return -1;
254 set_scsi_pt_cdb(ptvp, gpCmdBlk, sizeof(gpCmdBlk));
255 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
256 set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
257 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
258 ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
259 if (-1 == ret) {
260 if (get_scsi_pt_transport_err(ptvp))
261 ret = SG_LIB_TRANSPORT_ERROR;
262 else
263 ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
264 } else if (-2 == ret) {
265 switch (sense_cat) {
266 case SG_LIB_CAT_INVALID_OP:
267 case SG_LIB_CAT_ILLEGAL_REQ:
268 case SG_LIB_CAT_UNIT_ATTENTION:
269 case SG_LIB_CAT_ABORTED_COMMAND:
270 ret = sense_cat;
271 break;
272 case SG_LIB_CAT_RECOVERED:
273 case SG_LIB_CAT_NO_SENSE:
274 ret = 0;
275 break;
276 default:
277 ret = -1;
278 break;
279 }
280 } else {
281 if ((verbose > 2) && (ret > 3)) {
282 uint8_t * bp;
283 int len;
284
285 bp = (uint8_t *)resp;
286 len = sg_get_unaligned_be32(bp + 0);
287 if (len < 0)
288 len = 0;
289 len = (ret < len) ? ret : len;
290 pr2ws(" %s: response", cdb_s);
291 if (3 == verbose) {
292 pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : ""));
293 hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len),
294 -1);
295 } else {
296 pr2ws(":\n");
297 hex2stderr((const uint8_t *)resp, len, 0);
298 }
299 }
300 ret = 0;
301 }
302 destruct_scsi_pt_obj(ptvp);
303 return ret;
304 }
305
306 /* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
307 * SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
308 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
309 * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
310 * -1 -> other failure */
311 int
sg_ll_set_streaming(int sg_fd,int type,void * paramp,int param_len,bool noisy,int verbose)312 sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
313 bool noisy, int verbose)
314 {
315 static const char * const cdb_s = "set streaming";
316 int res, ret, sense_cat;
317 uint8_t ssCmdBlk[SET_STREAMING_CMDLEN] =
318 {SET_STREAMING_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
319 uint8_t sense_b[SENSE_BUFF_LEN];
320 struct sg_pt_base * ptvp;
321
322 ssCmdBlk[8] = type;
323 sg_put_unaligned_be16((uint16_t)param_len, ssCmdBlk + 9);
324 if (verbose) {
325 int k;
326
327 pr2ws(" %s cdb: ", cdb_s);
328 for (k = 0; k < SET_STREAMING_CMDLEN; ++k)
329 pr2ws("%02x ", ssCmdBlk[k]);
330 pr2ws("\n");
331 if ((verbose > 1) && paramp && param_len) {
332 pr2ws(" %s parameter list:\n", cdb_s);
333 hex2stderr((const uint8_t *)paramp, param_len, -1);
334 }
335 }
336
337 if (NULL == ((ptvp = create_pt_obj(cdb_s))))
338 return -1;
339 set_scsi_pt_cdb(ptvp, ssCmdBlk, sizeof(ssCmdBlk));
340 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
341 set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len);
342 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
343 ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
344 if (-1 == ret) {
345 if (get_scsi_pt_transport_err(ptvp))
346 ret = SG_LIB_TRANSPORT_ERROR;
347 else
348 ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
349 } else if (-2 == ret) {
350 switch (sense_cat) {
351 case SG_LIB_CAT_NOT_READY:
352 case SG_LIB_CAT_INVALID_OP:
353 case SG_LIB_CAT_ILLEGAL_REQ:
354 case SG_LIB_CAT_UNIT_ATTENTION:
355 case SG_LIB_CAT_ABORTED_COMMAND:
356 ret = sense_cat;
357 break;
358 case SG_LIB_CAT_RECOVERED:
359 case SG_LIB_CAT_NO_SENSE:
360 ret = 0;
361 break;
362 default:
363 ret = -1;
364 break;
365 }
366 } else
367 ret = 0;
368 destruct_scsi_pt_obj(ptvp);
369 return ret;
370 }
371