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