• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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