• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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