1 /* 2 * Copyright (c) 2006-2018 Luben Tuikov and 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 <unistd.h> 9 #include <fcntl.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <stdarg.h> 13 #include <stdbool.h> 14 #include <ctype.h> 15 #include <string.h> 16 #include <getopt.h> 17 #define __STDC_FORMAT_MACROS 1 18 #include <inttypes.h> 19 20 #ifdef HAVE_CONFIG_H 21 #include "config.h" 22 #endif 23 #include "sg_lib.h" 24 #include "sg_cmds_basic.h" 25 #include "sg_cmds_extra.h" 26 #include "sg_unaligned.h" 27 #include "sg_pr2serr.h" 28 29 #ifdef SG_LIB_WIN32 30 #ifdef SG_LIB_WIN32_DIRECT 31 #include "sg_pt.h" /* needed for scsi_pt_win32_direct() */ 32 #endif 33 #endif 34 35 /* 36 * This utility issues the SCSI WRITE BUFFER command to the given device. 37 */ 38 39 static const char * version_str = "1.24 20180111"; /* spc5r18 */ 40 41 #define ME "sg_write_buffer: " 42 #define DEF_XFER_LEN (8 * 1024 * 1024) 43 #define EBUFF_SZ 256 44 45 #define WRITE_BUFFER_CMD 0x3b 46 #define WRITE_BUFFER_CMDLEN 10 47 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ 48 #define DEF_PT_TIMEOUT 300 /* 300 seconds, 5 minutes */ 49 50 static struct option long_options[] = { 51 {"bpw", required_argument, 0, 'b'}, 52 {"dry-run", no_argument, 0, 'd'}, 53 {"dry_run", no_argument, 0, 'd'}, 54 {"help", no_argument, 0, 'h'}, 55 {"id", required_argument, 0, 'i'}, 56 {"in", required_argument, 0, 'I'}, 57 {"length", required_argument, 0, 'l'}, 58 {"mode", required_argument, 0, 'm'}, 59 {"offset", required_argument, 0, 'o'}, 60 {"read-stdin", no_argument, 0, 'r'}, 61 {"read_stdin", no_argument, 0, 'r'}, 62 {"raw", no_argument, 0, 'r'}, 63 {"skip", required_argument, 0, 's'}, 64 {"specific", required_argument, 0, 'S'}, 65 {"timeout", required_argument, 0, 't' }, 66 {"verbose", no_argument, 0, 'v'}, 67 {"version", no_argument, 0, 'V'}, 68 {0, 0, 0, 0}, 69 }; 70 71 72 static void 73 usage() 74 { 75 pr2serr("Usage: " 76 "sg_write_buffer [--bpw=CS] [--dry-run] [--help] [--id=ID] " 77 "[--in=FILE]\n" 78 " [--length=LEN] [--mode=MO] " 79 "[--offset=OFF]\n" 80 " [--read-stdin] [--skip=SKIP] " 81 "[--specific=MS]\n" 82 " [--timeout=TO] [--verbose] [--version] " 83 "DEVICE\n" 84 " where:\n" 85 " --bpw=CS|-b CS CS is chunk size: bytes per write " 86 "buffer\n" 87 " command (def: 0 -> as many as " 88 "possible)\n" 89 " --dry-run|-d skip WRITE BUFFER commands, do " 90 "everything else\n" 91 " --help|-h print out usage message then exit\n" 92 " --id=ID|-i ID buffer identifier (0 (default) to " 93 "255)\n" 94 " --in=FILE|-I FILE read from FILE ('-I -' read " 95 "from stdin)\n" 96 " --length=LEN|-l LEN length in bytes to write; may be " 97 "deduced from\n" 98 " FILE\n" 99 " --mode=MO|-m MO write buffer mode, MO is number or " 100 "acronym\n" 101 " (def: 0 -> 'combined header and " 102 "data' (obs))\n" 103 " --offset=OFF|-o OFF buffer offset (unit: bytes, def: 0)\n" 104 " --read-stdin|-r read from stdin (same as '-I -')\n" 105 " --skip=SKIP|-s SKIP bytes in file FILE to skip before " 106 "reading\n" 107 " --specific=MS|-S MS mode specific value; 3 bit field " 108 "(0 to 7)\n" 109 " --timeout=TO|-t TO command timeout in seconds (def: " 110 "300)\n" 111 " --verbose|-v increase verbosity\n" 112 " --version|-V print version string and exit\n\n" 113 "Performs one or more SCSI WRITE BUFFER commands. Use '-m xxx' " 114 "to list\navailable modes. A chunk size of 4 KB ('--bpw=4k') " 115 "seems to work well.\nExample: sg_write_buffer -b 4k -I xxx.lod " 116 "-m 7 /dev/sg3\n" 117 ); 118 119 } 120 121 #define MODE_HEADER_DATA 0 122 #define MODE_VENDOR 1 123 #define MODE_DATA 2 124 #define MODE_DNLD_MC 4 125 #define MODE_DNLD_MC_SAVE 5 126 #define MODE_DNLD_MC_OFFS 6 127 #define MODE_DNLD_MC_OFFS_SAVE 7 128 #define MODE_ECHO_BUFFER 0x0A 129 #define MODE_DNLD_MC_EV_OFFS_DEFER 0x0D 130 #define MODE_DNLD_MC_OFFS_DEFER 0x0E 131 #define MODE_ACTIVATE_MC 0x0F 132 #define MODE_EN_EX_ECHO 0x1A 133 #define MODE_DIS_EX 0x1B 134 #define MODE_DNLD_ERR_HISTORY 0x1C 135 136 137 struct mode_s { 138 const char *mode_string; 139 int mode; 140 const char *comment; 141 }; 142 143 static struct mode_s mode_arr[] = { 144 {"hd", MODE_HEADER_DATA, "combined header and data " 145 "(obsolete)"}, 146 {"vendor", MODE_VENDOR, "vendor specific"}, 147 {"data", MODE_DATA, "data"}, 148 {"dmc", MODE_DNLD_MC, "download microcode and activate"}, 149 {"dmc_save", MODE_DNLD_MC_SAVE, "download microcode, save and " 150 "activate"}, 151 {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets " 152 "and activate"}, 153 {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with " 154 "offsets, save and\n\t\t\t\tactivate"}, 155 {"echo", MODE_ECHO_BUFFER, "write data to echo buffer"}, 156 {"dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download " 157 "microcode with offsets, select\n\t\t\t\tactivation event, " 158 "save and defer activation"}, 159 {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode " 160 "with offsets, save and\n\t\t\t\tdefer activation"}, 161 {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"}, 162 {"en_ex", MODE_EN_EX_ECHO, "enable expander communications " 163 "protocol and\n\t\t\t\techo buffer (obsolete)"}, 164 {"dis_ex", MODE_DIS_EX, "disable expander communications " 165 "protocol\n\t\t\t\t(obsolete)"}, 166 {"deh", MODE_DNLD_ERR_HISTORY, "download application client " 167 "error history "}, 168 {NULL, 0, NULL}, 169 }; 170 171 static void 172 print_modes(void) 173 { 174 const struct mode_s * mp; 175 176 pr2serr("The modes parameter argument can be numeric (hex or decimal)\n" 177 "or symbolic:\n"); 178 for (mp = mode_arr; mp->mode_string; ++mp) { 179 pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode, 180 mp->mode_string, mp->comment); 181 } 182 pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred " 183 "microcode after\nsuccessful dmc_offs_defer and " 184 "dmc_offs_ev_defer mode downloads.\n"); 185 } 186 187 188 int 189 main(int argc, char * argv[]) 190 { 191 bool bpw_then_activate = false; 192 bool dry_run = false; 193 bool got_stdin = false; 194 bool wb_len_given = false; 195 int sg_fd, infd, res, c, len, k, n; 196 int bpw = 0; 197 int do_help = 0; 198 int ret = 0; 199 int verbose = 0; 200 int wb_id = 0; 201 int wb_len = 0; 202 int wb_mode = 0; 203 int wb_offset = 0; 204 int wb_skip = 0; 205 int wb_timeout = DEF_PT_TIMEOUT; 206 int wb_mspec = 0; 207 const char * device_name = NULL; 208 const char * file_name = NULL; 209 unsigned char * dop = NULL; 210 char * cp; 211 const struct mode_s * mp; 212 char ebuff[EBUFF_SZ]; 213 214 while (1) { 215 int option_index = 0; 216 217 c = getopt_long(argc, argv, "b:dhi:I:l:m:o:rs:S:t:vV", long_options, 218 &option_index); 219 if (c == -1) 220 break; 221 222 switch (c) { 223 case 'b': 224 bpw = sg_get_num(optarg); 225 if (bpw < 0) { 226 pr2serr("argument to '--bpw' should be in a positive " 227 "number\n"); 228 return SG_LIB_SYNTAX_ERROR; 229 } 230 if ((cp = strchr(optarg, ','))) { 231 if (0 == strncmp("act", cp + 1, 3)) 232 bpw_then_activate = true; 233 } 234 break; 235 case 'd': 236 dry_run = true; 237 break; 238 case 'h': 239 case '?': 240 ++do_help; 241 break; 242 case 'i': 243 wb_id = sg_get_num(optarg); 244 if ((wb_id < 0) || (wb_id > 255)) { 245 pr2serr("argument to '--id' should be in the range 0 to " 246 "255\n"); 247 return SG_LIB_SYNTAX_ERROR; 248 } 249 break; 250 case 'I': 251 file_name = optarg; 252 break; 253 case 'l': 254 wb_len = sg_get_num(optarg); 255 if (wb_len < 0) { 256 pr2serr("bad argument to '--length'\n"); 257 return SG_LIB_SYNTAX_ERROR; 258 } 259 wb_len_given = true; 260 break; 261 case 'm': 262 if (isdigit(*optarg)) { 263 wb_mode = sg_get_num(optarg); 264 if ((wb_mode < 0) || (wb_mode > 31)) { 265 pr2serr("argument to '--mode' should be in the range 0 " 266 "to 31\n"); 267 return SG_LIB_SYNTAX_ERROR; 268 } 269 } else { 270 len = strlen(optarg); 271 for (mp = mode_arr; mp->mode_string; ++mp) { 272 if (0 == strncmp(mp->mode_string, optarg, len)) { 273 wb_mode = mp->mode; 274 break; 275 } 276 } 277 if (! mp->mode_string) { 278 print_modes(); 279 return SG_LIB_SYNTAX_ERROR; 280 } 281 } 282 break; 283 case 'o': 284 wb_offset = sg_get_num(optarg); 285 if (wb_offset < 0) { 286 pr2serr("bad argument to '--offset'\n"); 287 return SG_LIB_SYNTAX_ERROR; 288 } 289 break; 290 case 'r': /* --read-stdin and --raw (previous name) */ 291 file_name = "-"; 292 break; 293 case 's': 294 wb_skip = sg_get_num(optarg); 295 if (wb_skip < 0) { 296 pr2serr("bad argument to '--skip'\n"); 297 return SG_LIB_SYNTAX_ERROR; 298 } 299 break; 300 case 'S': 301 wb_mspec = sg_get_num(optarg); 302 if ((wb_mspec < 0) || (wb_mspec > 7)) { 303 pr2serr("expected argument to '--specific' to be 0 to 7\n"); 304 return SG_LIB_SYNTAX_ERROR; 305 } 306 break; 307 case 't': 308 wb_timeout = sg_get_num(optarg); 309 if (wb_timeout < 0) { 310 pr2serr("Invalid argument to '--timeout'\n"); 311 return SG_LIB_SYNTAX_ERROR; 312 } 313 break; 314 case 'v': 315 ++verbose; 316 break; 317 case 'V': 318 pr2serr(ME "version: %s\n", version_str); 319 return 0; 320 default: 321 pr2serr("unrecognised option code 0x%x ??\n", c); 322 usage(); 323 return SG_LIB_SYNTAX_ERROR; 324 } 325 } 326 if (do_help) { 327 if (do_help > 1) { 328 usage(); 329 pr2serr("\n"); 330 print_modes(); 331 } else 332 usage(); 333 return 0; 334 } 335 if (optind < argc) { 336 if (NULL == device_name) { 337 device_name = argv[optind]; 338 ++optind; 339 } 340 if (optind < argc) { 341 for (; optind < argc; ++optind) 342 pr2serr("Unexpected extra argument: %s\n", argv[optind]); 343 usage(); 344 return SG_LIB_SYNTAX_ERROR; 345 } 346 } 347 348 if (NULL == device_name) { 349 pr2serr("missing device name!\n"); 350 usage(); 351 return SG_LIB_SYNTAX_ERROR; 352 } 353 354 if ((wb_len > 0) && (bpw > wb_len)) { 355 pr2serr("trim chunk size (CS) to be the same as LEN\n"); 356 bpw = wb_len; 357 } 358 359 #ifdef SG_LIB_WIN32 360 #ifdef SG_LIB_WIN32_DIRECT 361 if (verbose > 4) 362 pr2serr("Initial win32 SPT interface state: %s\n", 363 scsi_pt_win32_spt_state() ? "direct" : "indirect"); 364 scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */); 365 #endif 366 #endif 367 368 sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose); 369 if (sg_fd < 0) { 370 pr2serr(ME "open error: %s: %s\n", device_name, 371 safe_strerror(-sg_fd)); 372 return SG_LIB_FILE_ERROR; 373 } 374 if (file_name || (wb_len > 0)) { 375 if (0 == wb_len) 376 wb_len = DEF_XFER_LEN; 377 if (NULL == (dop = (unsigned char *)malloc(wb_len))) { 378 pr2serr(ME "out of memory\n"); 379 ret = SG_LIB_SYNTAX_ERROR; 380 goto err_out; 381 } 382 memset(dop, 0xff, wb_len); 383 if (file_name) { 384 got_stdin = (0 == strcmp(file_name, "-")); 385 if (got_stdin) { 386 if (wb_skip > 0) { 387 pr2serr("Can't skip on stdin\n"); 388 ret = SG_LIB_FILE_ERROR; 389 goto err_out; 390 } 391 infd = STDIN_FILENO; 392 } else { 393 if ((infd = open(file_name, O_RDONLY)) < 0) { 394 snprintf(ebuff, EBUFF_SZ, 395 ME "could not open %s for reading", file_name); 396 perror(ebuff); 397 ret = SG_LIB_FILE_ERROR; 398 goto err_out; 399 } else if (sg_set_binary_mode(infd) < 0) 400 perror("sg_set_binary_mode"); 401 if (wb_skip > 0) { 402 if (lseek(infd, wb_skip, SEEK_SET) < 0) { 403 snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to " 404 "required position on %s", file_name); 405 perror(ebuff); 406 close(infd); 407 ret = SG_LIB_FILE_ERROR; 408 goto err_out; 409 } 410 } 411 } 412 res = read(infd, dop, wb_len); 413 if (res < 0) { 414 snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s", 415 file_name); 416 perror(ebuff); 417 if (! got_stdin) 418 close(infd); 419 ret = SG_LIB_FILE_ERROR; 420 goto err_out; 421 } 422 if (res < wb_len) { 423 if (wb_len_given) { 424 pr2serr("tried to read %d bytes from %s, got %d bytes\n", 425 wb_len, file_name, res); 426 pr2serr("pad with 0xff bytes and continue\n"); 427 } else { 428 if (verbose) { 429 pr2serr("tried to read %d bytes from %s, got %d " 430 "bytes\n", wb_len, file_name, res); 431 pr2serr("will write %d bytes", res); 432 if ((bpw > 0) && (bpw < wb_len)) 433 pr2serr(", %d bytes per WRITE BUFFER command\n", 434 bpw); 435 else 436 pr2serr("\n"); 437 } 438 wb_len = res; 439 } 440 } 441 if (! got_stdin) 442 close(infd); 443 } 444 } 445 446 res = 0; 447 if (bpw > 0) { 448 for (k = 0; k < wb_len; k += n) { 449 n = wb_len - k; 450 if (n > bpw) 451 n = bpw; 452 if (verbose) 453 pr2serr("sending write buffer, mode=0x%x, mspec=%d, id=%d, " 454 " offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id, 455 wb_offset + k, n); 456 if (dry_run) { 457 if (verbose) 458 pr2serr("skipping WRITE BUFFER command due to " 459 "--dry-run\n"); 460 res = 0; 461 } else 462 res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id, 463 wb_offset + k, dop + k, n, 464 wb_timeout, true, verbose); 465 if (res) 466 break; 467 } 468 if (bpw_then_activate) { 469 if (verbose) 470 pr2serr("sending Activate deferred microcode [0xf]\n"); 471 if (dry_run) { 472 if (verbose) 473 pr2serr("skipping WRITE BUFFER(ACTIVATE) command due to " 474 "--dry-run\n"); 475 res = 0; 476 } else 477 res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC, 478 0 /* buffer_id */, 479 0 /* buffer_offset */, 0, 480 NULL, 0, wb_timeout, true, 481 verbose); 482 } 483 } else { 484 if (verbose) 485 pr2serr("sending single write buffer, mode=0x%x, mpsec=%d, " 486 "id=%d, offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id, 487 wb_offset, wb_len); 488 if (dry_run) { 489 if (verbose) 490 pr2serr("skipping WRITE BUFFER(all in one) command due to " 491 "--dry-run\n"); 492 res = 0; 493 } else 494 res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id, 495 wb_offset, dop, wb_len, wb_timeout, 496 true, verbose); 497 } 498 if (0 != res) { 499 char b[80]; 500 501 ret = res; 502 sg_get_category_sense_str(res, sizeof(b), b, verbose); 503 pr2serr("Write buffer failed: %s\n", b); 504 } 505 506 err_out: 507 if (dop) 508 free(dop); 509 res = sg_cmds_close_device(sg_fd); 510 if (res < 0) { 511 pr2serr("close error: %s\n", safe_strerror(-res)); 512 if (0 == ret) 513 return SG_LIB_FILE_ERROR; 514 } 515 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; 516 } 517