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
usage()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
print_modes(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
main(int argc,char * argv[])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