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