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 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 * 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 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 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 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 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