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