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