xref: /aosp_15_r20/external/sg3_utils/src/sg_rbuf.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /* A utility program originally written for the Linux OS SCSI subsystem.
2*44704f69SBart Van Assche  *  Copyright (C) 1999-2022 D. Gilbert
3*44704f69SBart Van Assche  *  This program is free software; you can redistribute it and/or modify
4*44704f69SBart Van Assche  *  it under the terms of the GNU General Public License as published by
5*44704f69SBart Van Assche  *  the Free Software Foundation; either version 2, or (at your option)
6*44704f69SBart Van Assche  *  any later version.
7*44704f69SBart Van Assche  *
8*44704f69SBart Van Assche  * SPDX-License-Identifier: GPL-2.0-or-later
9*44704f69SBart Van Assche  *
10*44704f69SBart Van Assche  * This program uses the SCSI command READ BUFFER on the given
11*44704f69SBart Van Assche  * device, first to find out how big it is and then to read that
12*44704f69SBart Van Assche  * buffer (data mode, buffer id 0).
13*44704f69SBart Van Assche  */
14*44704f69SBart Van Assche 
15*44704f69SBart Van Assche 
16*44704f69SBart Van Assche #define _XOPEN_SOURCE 600
17*44704f69SBart Van Assche #ifndef _GNU_SOURCE
18*44704f69SBart Van Assche #define _GNU_SOURCE 1
19*44704f69SBart Van Assche #endif
20*44704f69SBart Van Assche 
21*44704f69SBart Van Assche #include <unistd.h>
22*44704f69SBart Van Assche #include <fcntl.h>
23*44704f69SBart Van Assche #include <stdio.h>
24*44704f69SBart Van Assche #include <stdlib.h>
25*44704f69SBart Van Assche #include <stdarg.h>
26*44704f69SBart Van Assche #include <stdbool.h>
27*44704f69SBart Van Assche #include <string.h>
28*44704f69SBart Van Assche #include <errno.h>
29*44704f69SBart Van Assche #include <getopt.h>
30*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
31*44704f69SBart Van Assche #include <inttypes.h>
32*44704f69SBart Van Assche #include <sys/ioctl.h>
33*44704f69SBart Van Assche #include <sys/types.h>
34*44704f69SBart Van Assche #include <sys/stat.h>
35*44704f69SBart Van Assche #include <sys/mman.h>
36*44704f69SBart Van Assche #include <sys/time.h>
37*44704f69SBart Van Assche 
38*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
39*44704f69SBart Van Assche #include "config.h"
40*44704f69SBart Van Assche #endif
41*44704f69SBart Van Assche #include "sg_lib.h"
42*44704f69SBart Van Assche #include "sg_io_linux.h"
43*44704f69SBart Van Assche #include "sg_unaligned.h"
44*44704f69SBart Van Assche #include "sg_pr2serr.h"
45*44704f69SBart Van Assche 
46*44704f69SBart Van Assche #define RB_MODE_DESC 3
47*44704f69SBart Van Assche #define RB_MODE_DATA 2
48*44704f69SBart Van Assche #define RB_MODE_ECHO_DESC 0xb
49*44704f69SBart Van Assche #define RB_MODE_ECHO_DATA 0xa
50*44704f69SBart Van Assche #define RB_DESC_LEN 4
51*44704f69SBart Van Assche #define RB_DEF_SIZE (200*1024*1024)
52*44704f69SBart Van Assche #define RB_OPCODE 0x3C
53*44704f69SBart Van Assche #define RB_CMD_LEN 10
54*44704f69SBart Van Assche 
55*44704f69SBart Van Assche #ifndef SG_FLAG_MMAP_IO
56*44704f69SBart Van Assche #define SG_FLAG_MMAP_IO 4
57*44704f69SBart Van Assche #endif
58*44704f69SBart Van Assche 
59*44704f69SBart Van Assche 
60*44704f69SBart Van Assche static const char * version_str = "5.09 20220425";
61*44704f69SBart Van Assche 
62*44704f69SBart Van Assche static struct option long_options[] = {
63*44704f69SBart Van Assche         {"buffer", required_argument, 0, 'b'},
64*44704f69SBart Van Assche         {"dio", no_argument, 0, 'd'},
65*44704f69SBart Van Assche         {"echo", no_argument, 0, 'e'},
66*44704f69SBart Van Assche         {"help", no_argument, 0, 'h'},
67*44704f69SBart Van Assche         {"mmap", no_argument, 0, 'm'},
68*44704f69SBart Van Assche         {"new", no_argument, 0, 'N'},
69*44704f69SBart Van Assche         {"old", no_argument, 0, 'O'},
70*44704f69SBart Van Assche         {"quick", no_argument, 0, 'q'},
71*44704f69SBart Van Assche         {"size", required_argument, 0, 's'},
72*44704f69SBart Van Assche         {"time", no_argument, 0, 't'},
73*44704f69SBart Van Assche         {"verbose", no_argument, 0, 'v'},
74*44704f69SBart Van Assche         {"version", no_argument, 0, 'V'},
75*44704f69SBart Van Assche         {0, 0, 0, 0},
76*44704f69SBart Van Assche };
77*44704f69SBart Van Assche 
78*44704f69SBart Van Assche struct opts_t {
79*44704f69SBart Van Assche     bool do_dio;
80*44704f69SBart Van Assche     bool do_echo;
81*44704f69SBart Van Assche     bool do_mmap;
82*44704f69SBart Van Assche     bool do_quick;
83*44704f69SBart Van Assche     bool do_time;
84*44704f69SBart Van Assche     bool verbose_given;
85*44704f69SBart Van Assche     bool version_given;
86*44704f69SBart Van Assche     bool opt_new;
87*44704f69SBart Van Assche     int do_buffer;
88*44704f69SBart Van Assche     int do_help;
89*44704f69SBart Van Assche     int verbose;
90*44704f69SBart Van Assche     int64_t do_size;
91*44704f69SBart Van Assche     const char * device_name;
92*44704f69SBart Van Assche };
93*44704f69SBart Van Assche 
94*44704f69SBart Van Assche 
95*44704f69SBart Van Assche static void
usage()96*44704f69SBart Van Assche usage()
97*44704f69SBart Van Assche {
98*44704f69SBart Van Assche     pr2serr("Usage: sg_rbuf [--buffer=EACH] [--dio] [--echo] "
99*44704f69SBart Van Assche             "[--help] [--mmap]\n"
100*44704f69SBart Van Assche             "               [--quick] [--size=OVERALL] [--time] [--verbose] "
101*44704f69SBart Van Assche             "[--version]\n"
102*44704f69SBart Van Assche             "               SG_DEVICE\n");
103*44704f69SBart Van Assche     pr2serr("  where:\n"
104*44704f69SBart Van Assche             "    --buffer=EACH|-b EACH    buffer size to use (in bytes)\n"
105*44704f69SBart Van Assche             "    --dio|-d        requests dio ('-q' overrides it)\n"
106*44704f69SBart Van Assche             "    --echo|-e       use echo buffer (def: use data mode)\n"
107*44704f69SBart Van Assche             "    --help|-h       print usage message then exit\n"
108*44704f69SBart Van Assche             "    --mmap|-m       requests mmap-ed IO (overrides -q, -d)\n"
109*44704f69SBart Van Assche             "    --quick|-q      quick, don't xfer to user space\n");
110*44704f69SBart Van Assche     pr2serr("    --size=OVERALL|-s OVERALL    total size to read (in bytes)\n"
111*44704f69SBart Van Assche             "                    default: 200 MiB\n"
112*44704f69SBart Van Assche             "    --time|-t       time the data transfer\n"
113*44704f69SBart Van Assche             "    --verbose|-v    increase verbosity (more debug)\n"
114*44704f69SBart Van Assche             "    --old|-O        use old interface (use as first option)\n"
115*44704f69SBart Van Assche             "    --version|-V    print version string then exit\n\n"
116*44704f69SBart Van Assche             "Use SCSI READ BUFFER command (data or echo buffer mode, buffer "
117*44704f69SBart Van Assche             "id 0)\nrepeatedly. This utility only works with Linux sg "
118*44704f69SBart Van Assche             "devices.\n");
119*44704f69SBart Van Assche }
120*44704f69SBart Van Assche 
121*44704f69SBart Van Assche static void
usage_old()122*44704f69SBart Van Assche usage_old()
123*44704f69SBart Van Assche {
124*44704f69SBart Van Assche     printf("Usage: sg_rbuf [-b=EACH_KIB] [-d] [-m] [-q] [-s=OVERALL_MIB] "
125*44704f69SBart Van Assche            "[-t] [-v] [-V]\n               SG_DEVICE\n");
126*44704f69SBart Van Assche     printf("  where:\n");
127*44704f69SBart Van Assche     printf("    -b=EACH_KIB    num is buffer size to use (in KiB)\n");
128*44704f69SBart Van Assche     printf("    -d       requests dio ('-q' overrides it)\n");
129*44704f69SBart Van Assche     printf("    -e       use echo buffer (def: use data mode)\n");
130*44704f69SBart Van Assche     printf("    -m       requests mmap-ed IO (overrides -q, -d)\n");
131*44704f69SBart Van Assche     printf("    -q       quick, don't xfer to user space\n");
132*44704f69SBart Van Assche     printf("    -s=OVERALL_MIB    num is total size to read (in MiB) "
133*44704f69SBart Van Assche            "(default: 200 MiB)\n");
134*44704f69SBart Van Assche     printf("             maximum total size is 4000 MiB\n");
135*44704f69SBart Van Assche     printf("    -t       time the data transfer\n");
136*44704f69SBart Van Assche     printf("    -v       increase verbosity (more debug)\n");
137*44704f69SBart Van Assche     printf("    -N|--new use new interface\n");
138*44704f69SBart Van Assche     printf("    -V       print version string then exit\n\n");
139*44704f69SBart Van Assche     printf("Use SCSI READ BUFFER command (data or echo buffer mode, buffer "
140*44704f69SBart Van Assche            "id 0)\nrepeatedly. This utility only works with Linux sg "
141*44704f69SBart Van Assche             "devices.\n");
142*44704f69SBart Van Assche }
143*44704f69SBart Van Assche 
144*44704f69SBart Van Assche static void
usage_for(const struct opts_t * op)145*44704f69SBart Van Assche usage_for(const struct opts_t * op)
146*44704f69SBart Van Assche {
147*44704f69SBart Van Assche     if (op->opt_new)
148*44704f69SBart Van Assche         usage();
149*44704f69SBart Van Assche     else
150*44704f69SBart Van Assche         usage_old();
151*44704f69SBart Van Assche }
152*44704f69SBart Van Assche 
153*44704f69SBart Van Assche static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])154*44704f69SBart Van Assche new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
155*44704f69SBart Van Assche {
156*44704f69SBart Van Assche     int c, n;
157*44704f69SBart Van Assche     int64_t nn;
158*44704f69SBart Van Assche 
159*44704f69SBart Van Assche     while (1) {
160*44704f69SBart Van Assche         int option_index = 0;
161*44704f69SBart Van Assche 
162*44704f69SBart Van Assche         c = getopt_long(argc, argv, "b:dehmNOqs:tvV", long_options,
163*44704f69SBart Van Assche                         &option_index);
164*44704f69SBart Van Assche         if (c == -1)
165*44704f69SBart Van Assche             break;
166*44704f69SBart Van Assche 
167*44704f69SBart Van Assche         switch (c) {
168*44704f69SBart Van Assche         case 'b':
169*44704f69SBart Van Assche             n = sg_get_num(optarg);
170*44704f69SBart Van Assche             if (n < 0) {
171*44704f69SBart Van Assche                 pr2serr("bad argument to '--buffer'\n");
172*44704f69SBart Van Assche                 usage_for(op);
173*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
174*44704f69SBart Van Assche             }
175*44704f69SBart Van Assche             op->do_buffer = n;
176*44704f69SBart Van Assche             break;
177*44704f69SBart Van Assche         case 'd':
178*44704f69SBart Van Assche             op->do_dio = true;
179*44704f69SBart Van Assche             break;
180*44704f69SBart Van Assche         case 'e':
181*44704f69SBart Van Assche             op->do_echo = true;
182*44704f69SBart Van Assche             break;
183*44704f69SBart Van Assche         case 'h':
184*44704f69SBart Van Assche         case '?':
185*44704f69SBart Van Assche             ++op->do_help;
186*44704f69SBart Van Assche             break;
187*44704f69SBart Van Assche         case 'm':
188*44704f69SBart Van Assche             op->do_mmap = true;
189*44704f69SBart Van Assche             break;
190*44704f69SBart Van Assche         case 'N':
191*44704f69SBart Van Assche             break;      /* ignore */
192*44704f69SBart Van Assche         case 'O':
193*44704f69SBart Van Assche             op->opt_new = false;
194*44704f69SBart Van Assche             return 0;
195*44704f69SBart Van Assche         case 'q':
196*44704f69SBart Van Assche             op->do_quick = true;
197*44704f69SBart Van Assche             break;
198*44704f69SBart Van Assche         case 's':
199*44704f69SBart Van Assche            nn = sg_get_llnum(optarg);
200*44704f69SBart Van Assche            if (nn < 0) {
201*44704f69SBart Van Assche                 pr2serr("bad argument to '--size'\n");
202*44704f69SBart Van Assche                 usage_for(op);
203*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
204*44704f69SBart Van Assche             }
205*44704f69SBart Van Assche             op->do_size = nn;
206*44704f69SBart Van Assche             break;
207*44704f69SBart Van Assche         case 't':
208*44704f69SBart Van Assche             op->do_time = true;
209*44704f69SBart Van Assche             break;
210*44704f69SBart Van Assche         case 'v':
211*44704f69SBart Van Assche             op->verbose_given = true;
212*44704f69SBart Van Assche             ++op->verbose;
213*44704f69SBart Van Assche             break;
214*44704f69SBart Van Assche         case 'V':
215*44704f69SBart Van Assche             op->version_given = true;
216*44704f69SBart Van Assche             break;
217*44704f69SBart Van Assche         default:
218*44704f69SBart Van Assche             pr2serr("unrecognised option code %c [0x%x]\n", c, c);
219*44704f69SBart Van Assche             if (op->do_help)
220*44704f69SBart Van Assche                 break;
221*44704f69SBart Van Assche             usage_for(op);
222*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
223*44704f69SBart Van Assche         }
224*44704f69SBart Van Assche     }
225*44704f69SBart Van Assche     if (optind < argc) {
226*44704f69SBart Van Assche         if (NULL == op->device_name) {
227*44704f69SBart Van Assche             op->device_name = argv[optind];
228*44704f69SBart Van Assche             ++optind;
229*44704f69SBart Van Assche         }
230*44704f69SBart Van Assche         if (optind < argc) {
231*44704f69SBart Van Assche             for (; optind < argc; ++optind)
232*44704f69SBart Van Assche                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
233*44704f69SBart Van Assche             usage_for(op);
234*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
235*44704f69SBart Van Assche         }
236*44704f69SBart Van Assche     }
237*44704f69SBart Van Assche     return 0;
238*44704f69SBart Van Assche }
239*44704f69SBart Van Assche 
240*44704f69SBart Van Assche static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])241*44704f69SBart Van Assche old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
242*44704f69SBart Van Assche {
243*44704f69SBart Van Assche     bool jmp_out;
244*44704f69SBart Van Assche     int k, plen, num;
245*44704f69SBart Van Assche     int64_t nn;
246*44704f69SBart Van Assche     const char * cp;
247*44704f69SBart Van Assche 
248*44704f69SBart Van Assche     for (k = 1; k < argc; ++k) {
249*44704f69SBart Van Assche         cp = argv[k];
250*44704f69SBart Van Assche         plen = strlen(cp);
251*44704f69SBart Van Assche         if (plen <= 0)
252*44704f69SBart Van Assche             continue;
253*44704f69SBart Van Assche         if ('-' == *cp) {
254*44704f69SBart Van Assche             for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
255*44704f69SBart Van Assche                 switch (*cp) {
256*44704f69SBart Van Assche                 case 'd':
257*44704f69SBart Van Assche                     op->do_dio = true;
258*44704f69SBart Van Assche                     break;
259*44704f69SBart Van Assche                 case 'e':
260*44704f69SBart Van Assche                     op->do_echo = true;
261*44704f69SBart Van Assche                     break;
262*44704f69SBart Van Assche                 case 'h':
263*44704f69SBart Van Assche                 case '?':
264*44704f69SBart Van Assche                     ++op->do_help;
265*44704f69SBart Van Assche                     break;
266*44704f69SBart Van Assche                 case 'm':
267*44704f69SBart Van Assche                     op->do_mmap = true;
268*44704f69SBart Van Assche                     break;
269*44704f69SBart Van Assche                 case 'N':
270*44704f69SBart Van Assche                     op->opt_new = true;
271*44704f69SBart Van Assche                     return 0;
272*44704f69SBart Van Assche                 case 'O':
273*44704f69SBart Van Assche                     break;
274*44704f69SBart Van Assche                 case 'q':
275*44704f69SBart Van Assche                     op->do_quick = true;
276*44704f69SBart Van Assche                     break;
277*44704f69SBart Van Assche                 case 't':
278*44704f69SBart Van Assche                     op->do_time = true;
279*44704f69SBart Van Assche                     break;
280*44704f69SBart Van Assche                 case 'v':
281*44704f69SBart Van Assche                     op->verbose_given = true;
282*44704f69SBart Van Assche                     ++op->verbose;
283*44704f69SBart Van Assche                     break;
284*44704f69SBart Van Assche                 case 'V':
285*44704f69SBart Van Assche                     op->version_given = true;
286*44704f69SBart Van Assche                     break;
287*44704f69SBart Van Assche                 default:
288*44704f69SBart Van Assche                     jmp_out = true;
289*44704f69SBart Van Assche                     break;
290*44704f69SBart Van Assche                 }
291*44704f69SBart Van Assche                 if (jmp_out)
292*44704f69SBart Van Assche                     break;
293*44704f69SBart Van Assche             }
294*44704f69SBart Van Assche             if (plen <= 0)
295*44704f69SBart Van Assche                 continue;
296*44704f69SBart Van Assche             if (0 == strncmp("b=", cp, 2)) {
297*44704f69SBart Van Assche                 num = sscanf(cp + 2, "%d", &op->do_buffer);
298*44704f69SBart Van Assche                 if ((1 != num) || (op->do_buffer <= 0)) {
299*44704f69SBart Van Assche                     printf("Couldn't decode number after 'b=' option\n");
300*44704f69SBart Van Assche                     usage_for(op);
301*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
302*44704f69SBart Van Assche                 }
303*44704f69SBart Van Assche                 op->do_buffer *= 1024;
304*44704f69SBart Van Assche             }
305*44704f69SBart Van Assche             else if (0 == strncmp("s=", cp, 2)) {
306*44704f69SBart Van Assche                 nn = sg_get_llnum(optarg);
307*44704f69SBart Van Assche                 if (nn < 0) {
308*44704f69SBart Van Assche                     printf("Couldn't decode number after 's=' option\n");
309*44704f69SBart Van Assche                     usage_for(op);
310*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
311*44704f69SBart Van Assche                 }
312*44704f69SBart Van Assche                 op->do_size = nn;
313*44704f69SBart Van Assche                 op->do_size *= 1024 * 1024;
314*44704f69SBart Van Assche             } else if (0 == strncmp("-old", cp, 4))
315*44704f69SBart Van Assche                 ;
316*44704f69SBart Van Assche             else if (jmp_out) {
317*44704f69SBart Van Assche                 pr2serr("Unrecognized option: %s\n", cp);
318*44704f69SBart Van Assche                 usage_for(op);
319*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
320*44704f69SBart Van Assche             }
321*44704f69SBart Van Assche         } else if (0 == op->device_name)
322*44704f69SBart Van Assche             op->device_name = cp;
323*44704f69SBart Van Assche         else {
324*44704f69SBart Van Assche             pr2serr("too many arguments, got: %s, not expecting: %s\n",
325*44704f69SBart Van Assche                     op->device_name, cp);
326*44704f69SBart Van Assche             usage_for(op);
327*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
328*44704f69SBart Van Assche         }
329*44704f69SBart Van Assche     }
330*44704f69SBart Van Assche     return 0;
331*44704f69SBart Van Assche }
332*44704f69SBart Van Assche 
333*44704f69SBart Van Assche static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])334*44704f69SBart Van Assche parse_cmd_line(struct opts_t * op, int argc, char * argv[])
335*44704f69SBart Van Assche {
336*44704f69SBart Van Assche     int res;
337*44704f69SBart Van Assche     char * cp;
338*44704f69SBart Van Assche 
339*44704f69SBart Van Assche     cp = getenv("SG3_UTILS_OLD_OPTS");
340*44704f69SBart Van Assche     if (cp) {
341*44704f69SBart Van Assche         op->opt_new = false;
342*44704f69SBart Van Assche         res = old_parse_cmd_line(op, argc, argv);
343*44704f69SBart Van Assche         if ((0 == res) && op->opt_new)
344*44704f69SBart Van Assche             res = new_parse_cmd_line(op, argc, argv);
345*44704f69SBart Van Assche     } else {
346*44704f69SBart Van Assche         op->opt_new = true;
347*44704f69SBart Van Assche         res = new_parse_cmd_line(op, argc, argv);
348*44704f69SBart Van Assche         if ((0 == res) && (! op->opt_new))
349*44704f69SBart Van Assche             res = old_parse_cmd_line(op, argc, argv);
350*44704f69SBart Van Assche     }
351*44704f69SBart Van Assche     return res;
352*44704f69SBart Van Assche }
353*44704f69SBart Van Assche 
354*44704f69SBart Van Assche 
355*44704f69SBart Van Assche int
main(int argc,char * argv[])356*44704f69SBart Van Assche main(int argc, char * argv[])
357*44704f69SBart Van Assche {
358*44704f69SBart Van Assche #ifdef DEBUG
359*44704f69SBart Van Assche     bool clear = true;
360*44704f69SBart Van Assche #endif
361*44704f69SBart Van Assche     bool dio_incomplete = false;
362*44704f69SBart Van Assche     int sg_fd, res, err;
363*44704f69SBart Van Assche     int buf_capacity = 0;
364*44704f69SBart Van Assche     int buf_size = 0;
365*44704f69SBart Van Assche     size_t psz;
366*44704f69SBart Van Assche     unsigned int k, num;
367*44704f69SBart Van Assche     int64_t total_size = RB_DEF_SIZE;
368*44704f69SBart Van Assche     struct opts_t * op;
369*44704f69SBart Van Assche     uint8_t * rbBuff = NULL;
370*44704f69SBart Van Assche     void * rawp = NULL;
371*44704f69SBart Van Assche     uint8_t sense_buffer[32] SG_C_CPP_ZERO_INIT;
372*44704f69SBart Van Assche     uint8_t rb_cdb [RB_CMD_LEN] SG_C_CPP_ZERO_INIT;
373*44704f69SBart Van Assche     struct sg_io_hdr io_hdr;
374*44704f69SBart Van Assche     struct timeval start_tm, end_tm;
375*44704f69SBart Van Assche     struct opts_t opts;
376*44704f69SBart Van Assche 
377*44704f69SBart Van Assche #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
378*44704f69SBart Van Assche     psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
379*44704f69SBart Van Assche #else
380*44704f69SBart Van Assche     psz = 4096;     /* give up, pick likely figure */
381*44704f69SBart Van Assche #endif
382*44704f69SBart Van Assche     op = &opts;
383*44704f69SBart Van Assche     memset(op, 0, sizeof(opts));
384*44704f69SBart Van Assche     res = parse_cmd_line(op, argc, argv);
385*44704f69SBart Van Assche     if (res)
386*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
387*44704f69SBart Van Assche     if (op->do_help) {
388*44704f69SBart Van Assche         usage_for(op);
389*44704f69SBart Van Assche         return 0;
390*44704f69SBart Van Assche     }
391*44704f69SBart Van Assche #ifdef DEBUG
392*44704f69SBart Van Assche     pr2serr("In DEBUG mode, ");
393*44704f69SBart Van Assche     if (op->verbose_given && op->version_given) {
394*44704f69SBart Van Assche         pr2serr("but override: '-vV' given, zero verbose and continue\n");
395*44704f69SBart Van Assche         op->verbose_given = false;
396*44704f69SBart Van Assche         op->version_given = false;
397*44704f69SBart Van Assche         op->verbose = 0;
398*44704f69SBart Van Assche     } else if (! op->verbose_given) {
399*44704f69SBart Van Assche         pr2serr("set '-vv'\n");
400*44704f69SBart Van Assche         op->verbose = 2;
401*44704f69SBart Van Assche     } else
402*44704f69SBart Van Assche         pr2serr("keep verbose=%d\n", op->verbose);
403*44704f69SBart Van Assche #else
404*44704f69SBart Van Assche     if (op->verbose_given && op->version_given)
405*44704f69SBart Van Assche         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
406*44704f69SBart Van Assche #endif
407*44704f69SBart Van Assche     if (op->version_given) {
408*44704f69SBart Van Assche         pr2serr("Version string: %s\n", version_str);
409*44704f69SBart Van Assche         return 0;
410*44704f69SBart Van Assche     }
411*44704f69SBart Van Assche 
412*44704f69SBart Van Assche     if (NULL == op->device_name) {
413*44704f69SBart Van Assche         pr2serr("No DEVICE argument given\n\n");
414*44704f69SBart Van Assche         usage_for(op);
415*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
416*44704f69SBart Van Assche     }
417*44704f69SBart Van Assche 
418*44704f69SBart Van Assche     if (op->do_buffer > 0)
419*44704f69SBart Van Assche         buf_size = op->do_buffer;
420*44704f69SBart Van Assche     if (op->do_size > 0)
421*44704f69SBart Van Assche         total_size = op->do_size;
422*44704f69SBart Van Assche 
423*44704f69SBart Van Assche     sg_fd = open(op->device_name, O_RDONLY | O_NONBLOCK);
424*44704f69SBart Van Assche     if (sg_fd < 0) {
425*44704f69SBart Van Assche         err = errno;
426*44704f69SBart Van Assche         perror("device open error");
427*44704f69SBart Van Assche         return sg_convert_errno(err);
428*44704f69SBart Van Assche     }
429*44704f69SBart Van Assche     if (op->do_mmap) {
430*44704f69SBart Van Assche         op->do_dio = false;
431*44704f69SBart Van Assche         op->do_quick = false;
432*44704f69SBart Van Assche     }
433*44704f69SBart Van Assche     if (NULL == (rawp = malloc(512))) {
434*44704f69SBart Van Assche         printf("out of memory (query)\n");
435*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
436*44704f69SBart Van Assche     }
437*44704f69SBart Van Assche     rbBuff = (uint8_t *)rawp;
438*44704f69SBart Van Assche 
439*44704f69SBart Van Assche     rb_cdb[0] = RB_OPCODE;
440*44704f69SBart Van Assche     rb_cdb[1] = op->do_echo ? RB_MODE_ECHO_DESC : RB_MODE_DESC;
441*44704f69SBart Van Assche     rb_cdb[8] = RB_DESC_LEN;
442*44704f69SBart Van Assche     memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
443*44704f69SBart Van Assche     io_hdr.interface_id = 'S';
444*44704f69SBart Van Assche     io_hdr.cmd_len = sizeof(rb_cdb);
445*44704f69SBart Van Assche     io_hdr.mx_sb_len = sizeof(sense_buffer);
446*44704f69SBart Van Assche     io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
447*44704f69SBart Van Assche     io_hdr.dxfer_len = RB_DESC_LEN;
448*44704f69SBart Van Assche     io_hdr.dxferp = rbBuff;
449*44704f69SBart Van Assche     io_hdr.cmdp = rb_cdb;
450*44704f69SBart Van Assche     io_hdr.sbp = sense_buffer;
451*44704f69SBart Van Assche     io_hdr.timeout = 60000;     /* 60000 millisecs == 60 seconds */
452*44704f69SBart Van Assche     if (op->verbose) {
453*44704f69SBart Van Assche         char b[128];
454*44704f69SBart Van Assche 
455*44704f69SBart Van Assche         pr2serr("    Read buffer (%sdescriptor) cdb: %s\n",
456*44704f69SBart Van Assche                 (op->do_echo ? "echo " : ""),
457*44704f69SBart Van Assche                 sg_get_command_str(rb_cdb, RB_CMD_LEN, false, sizeof(b), b));
458*44704f69SBart Van Assche     }
459*44704f69SBart Van Assche 
460*44704f69SBart Van Assche     /* do normal IO to find RB size (not dio or mmap-ed at this stage) */
461*44704f69SBart Van Assche     if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
462*44704f69SBart Van Assche         perror("SG_IO READ BUFFER descriptor error");
463*44704f69SBart Van Assche         if (rawp)
464*44704f69SBart Van Assche             free(rawp);
465*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
466*44704f69SBart Van Assche     }
467*44704f69SBart Van Assche 
468*44704f69SBart Van Assche     if (op->verbose > 2)
469*44704f69SBart Van Assche         pr2serr("      duration=%u ms\n", io_hdr.duration);
470*44704f69SBart Van Assche     /* now for the error processing */
471*44704f69SBart Van Assche     res = sg_err_category3(&io_hdr);
472*44704f69SBart Van Assche     switch (res) {
473*44704f69SBart Van Assche     case SG_LIB_CAT_RECOVERED:
474*44704f69SBart Van Assche         sg_chk_n_print3("READ BUFFER descriptor, continuing", &io_hdr,
475*44704f69SBart Van Assche                         op->verbose > 1);
476*44704f69SBart Van Assche #if defined(__GNUC__)
477*44704f69SBart Van Assche #if (__GNUC__ >= 7)
478*44704f69SBart Van Assche         __attribute__((fallthrough));
479*44704f69SBart Van Assche         /* FALL THROUGH */
480*44704f69SBart Van Assche #endif
481*44704f69SBart Van Assche #endif
482*44704f69SBart Van Assche     case SG_LIB_CAT_CLEAN:
483*44704f69SBart Van Assche         break;
484*44704f69SBart Van Assche     default: /* won't bother decoding other categories */
485*44704f69SBart Van Assche         sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr,
486*44704f69SBart Van Assche                         op->verbose > 1);
487*44704f69SBart Van Assche         if (rawp) free(rawp);
488*44704f69SBart Van Assche         return (res >= 0) ? res : SG_LIB_CAT_OTHER;
489*44704f69SBart Van Assche     }
490*44704f69SBart Van Assche 
491*44704f69SBart Van Assche     if (op->do_echo) {
492*44704f69SBart Van Assche         buf_capacity = 0x1fff & sg_get_unaligned_be16(rbBuff + 2);
493*44704f69SBart Van Assche         printf("READ BUFFER reports: echo buffer capacity=%d\n",
494*44704f69SBart Van Assche                buf_capacity);
495*44704f69SBart Van Assche     } else {
496*44704f69SBart Van Assche         buf_capacity = sg_get_unaligned_be24(rbBuff + 1);
497*44704f69SBart Van Assche         printf("READ BUFFER reports: buffer capacity=%d, offset "
498*44704f69SBart Van Assche                "boundary=%d\n", buf_capacity, (int)rbBuff[0]);
499*44704f69SBart Van Assche     }
500*44704f69SBart Van Assche 
501*44704f69SBart Van Assche     if (0 == buf_size)
502*44704f69SBart Van Assche         buf_size = buf_capacity;
503*44704f69SBart Van Assche     else if (buf_size > buf_capacity) {
504*44704f69SBart Van Assche         printf("Requested buffer size=%d exceeds reported capacity=%d\n",
505*44704f69SBart Van Assche                buf_size, buf_capacity);
506*44704f69SBart Van Assche         if (rawp) free(rawp);
507*44704f69SBart Van Assche         return SG_LIB_CAT_MALFORMED;
508*44704f69SBart Van Assche     }
509*44704f69SBart Van Assche     if (rawp) {
510*44704f69SBart Van Assche         free(rawp);
511*44704f69SBart Van Assche         rawp = NULL;
512*44704f69SBart Van Assche     }
513*44704f69SBart Van Assche 
514*44704f69SBart Van Assche     if (! op->do_dio) {
515*44704f69SBart Van Assche         k = buf_size;
516*44704f69SBart Van Assche         if (op->do_mmap && (0 != (k % psz)))
517*44704f69SBart Van Assche             k = ((k / psz) + 1) * psz;  /* round up to page size */
518*44704f69SBart Van Assche         res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, &k);
519*44704f69SBart Van Assche         if (res < 0)
520*44704f69SBart Van Assche             perror("SG_SET_RESERVED_SIZE error");
521*44704f69SBart Van Assche     }
522*44704f69SBart Van Assche 
523*44704f69SBart Van Assche     if (op->do_mmap) {
524*44704f69SBart Van Assche         rbBuff = (uint8_t *)mmap(NULL, buf_size, PROT_READ, MAP_SHARED,
525*44704f69SBart Van Assche                                        sg_fd, 0);
526*44704f69SBart Van Assche         if (MAP_FAILED == rbBuff) {
527*44704f69SBart Van Assche             if (ENOMEM == errno) {
528*44704f69SBart Van Assche                 pr2serr("mmap() out of memory, try a smaller buffer size "
529*44704f69SBart Van Assche                         "than %d bytes\n", buf_size);
530*44704f69SBart Van Assche                 if (op->opt_new)
531*44704f69SBart Van Assche                     pr2serr("    [with '--buffer=EACH' where EACH is in "
532*44704f69SBart Van Assche                             "bytes]\n");
533*44704f69SBart Van Assche                 else
534*44704f69SBart Van Assche                     pr2serr("    [with '-b=EACH' where EACH is in KiB]\n");
535*44704f69SBart Van Assche             } else
536*44704f69SBart Van Assche                 perror("error using mmap()");
537*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
538*44704f69SBart Van Assche         }
539*44704f69SBart Van Assche     }
540*44704f69SBart Van Assche     else { /* non mmap-ed IO */
541*44704f69SBart Van Assche         rawp = (uint8_t *)malloc(buf_size + (op->do_dio ? psz : 0));
542*44704f69SBart Van Assche         if (NULL == rawp) {
543*44704f69SBart Van Assche             printf("out of memory (data)\n");
544*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
545*44704f69SBart Van Assche         }
546*44704f69SBart Van Assche         /* perhaps use posix_memalign() instead */
547*44704f69SBart Van Assche         if (op->do_dio)    /* align to page boundary */
548*44704f69SBart Van Assche             rbBuff= (uint8_t *)(((sg_uintptr_t)rawp + psz - 1) &
549*44704f69SBart Van Assche                                       (~(psz - 1)));
550*44704f69SBart Van Assche         else
551*44704f69SBart Van Assche             rbBuff = (uint8_t *)rawp;
552*44704f69SBart Van Assche     }
553*44704f69SBart Van Assche 
554*44704f69SBart Van Assche     num = total_size / buf_size;
555*44704f69SBart Van Assche     if (op->do_time) {
556*44704f69SBart Van Assche         start_tm.tv_sec = 0;
557*44704f69SBart Van Assche         start_tm.tv_usec = 0;
558*44704f69SBart Van Assche         gettimeofday(&start_tm, NULL);
559*44704f69SBart Van Assche     }
560*44704f69SBart Van Assche     /* main data reading loop */
561*44704f69SBart Van Assche     for (k = 0; k < num; ++k) {
562*44704f69SBart Van Assche         memset(rb_cdb, 0, RB_CMD_LEN);
563*44704f69SBart Van Assche         rb_cdb[0] = RB_OPCODE;
564*44704f69SBart Van Assche         rb_cdb[1] = op->do_echo ? RB_MODE_ECHO_DATA : RB_MODE_DATA;
565*44704f69SBart Van Assche         sg_put_unaligned_be24((uint32_t)buf_size, rb_cdb + 6);
566*44704f69SBart Van Assche #ifdef DEBUG
567*44704f69SBart Van Assche         memset(rbBuff, 0, buf_size);
568*44704f69SBart Van Assche #endif
569*44704f69SBart Van Assche 
570*44704f69SBart Van Assche         memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
571*44704f69SBart Van Assche         io_hdr.interface_id = 'S';
572*44704f69SBart Van Assche         io_hdr.cmd_len = sizeof(rb_cdb);
573*44704f69SBart Van Assche         io_hdr.mx_sb_len = sizeof(sense_buffer);
574*44704f69SBart Van Assche         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
575*44704f69SBart Van Assche         io_hdr.dxfer_len = buf_size;
576*44704f69SBart Van Assche         if (! op->do_mmap)
577*44704f69SBart Van Assche             io_hdr.dxferp = rbBuff;
578*44704f69SBart Van Assche         io_hdr.cmdp = rb_cdb;
579*44704f69SBart Van Assche         io_hdr.sbp = sense_buffer;
580*44704f69SBart Van Assche         io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
581*44704f69SBart Van Assche         io_hdr.pack_id = k;
582*44704f69SBart Van Assche         if (op->do_mmap)
583*44704f69SBart Van Assche             io_hdr.flags |= SG_FLAG_MMAP_IO;
584*44704f69SBart Van Assche         else if (op->do_dio)
585*44704f69SBart Van Assche             io_hdr.flags |= SG_FLAG_DIRECT_IO;
586*44704f69SBart Van Assche         else if (op->do_quick)
587*44704f69SBart Van Assche             io_hdr.flags |= SG_FLAG_NO_DXFER;
588*44704f69SBart Van Assche         if (op->verbose > 1) {
589*44704f69SBart Van Assche             char b[128];
590*44704f69SBart Van Assche 
591*44704f69SBart Van Assche             pr2serr("    Read buffer (%sdata) cdb: %s\n",
592*44704f69SBart Van Assche                     (op->do_echo ? "echo " : ""),
593*44704f69SBart Van Assche                     sg_get_command_str(rb_cdb, RB_CMD_LEN, false,
594*44704f69SBart Van Assche                                        sizeof(b), b));
595*44704f69SBart Van Assche         }
596*44704f69SBart Van Assche         if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
597*44704f69SBart Van Assche             if (ENOMEM == errno) {
598*44704f69SBart Van Assche                 pr2serr("SG_IO data: out of memory, try a smaller buffer "
599*44704f69SBart Van Assche                         "size than %d bytes\n", buf_size);
600*44704f69SBart Van Assche                 if (op->opt_new)
601*44704f69SBart Van Assche                     pr2serr("    [with '--buffer=EACH' where EACH is in "
602*44704f69SBart Van Assche                             "bytes]\n");
603*44704f69SBart Van Assche                 else
604*44704f69SBart Van Assche                     pr2serr("    [with '-b=EACH' where EACH is in KiB]\n");
605*44704f69SBart Van Assche             } else
606*44704f69SBart Van Assche                 perror("SG_IO READ BUFFER data error");
607*44704f69SBart Van Assche             if (rawp) free(rawp);
608*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
609*44704f69SBart Van Assche         }
610*44704f69SBart Van Assche 
611*44704f69SBart Van Assche         if (op->verbose > 2)
612*44704f69SBart Van Assche             pr2serr("      duration=%u ms\n", io_hdr.duration);
613*44704f69SBart Van Assche         /* now for the error processing */
614*44704f69SBart Van Assche         res = sg_err_category3(&io_hdr);
615*44704f69SBart Van Assche         switch (res) {
616*44704f69SBart Van Assche         case SG_LIB_CAT_CLEAN:
617*44704f69SBart Van Assche             break;
618*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
619*44704f69SBart Van Assche             sg_chk_n_print3("READ BUFFER data, continuing", &io_hdr,
620*44704f69SBart Van Assche                             op->verbose > 1);
621*44704f69SBart Van Assche             break;
622*44704f69SBart Van Assche         default: /* won't bother decoding other categories */
623*44704f69SBart Van Assche             sg_chk_n_print3("READ BUFFER data error", &io_hdr,
624*44704f69SBart Van Assche                             op->verbose > 1);
625*44704f69SBart Van Assche             if (rawp) free(rawp);
626*44704f69SBart Van Assche             return (res >= 0) ? res : SG_LIB_CAT_OTHER;
627*44704f69SBart Van Assche         }
628*44704f69SBart Van Assche         if (op->do_dio &&
629*44704f69SBart Van Assche             ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
630*44704f69SBart Van Assche             dio_incomplete = true;  /* flag that dio not done (completely) */
631*44704f69SBart Van Assche 
632*44704f69SBart Van Assche #ifdef DEBUG
633*44704f69SBart Van Assche         if (clear) {
634*44704f69SBart Van Assche             int j;
635*44704f69SBart Van Assche 
636*44704f69SBart Van Assche             for (j = 0; j < buf_size; ++j) {
637*44704f69SBart Van Assche                 if (rbBuff[j] != 0) {
638*44704f69SBart Van Assche                     clear = false;
639*44704f69SBart Van Assche                     break;
640*44704f69SBart Van Assche                 }
641*44704f69SBart Van Assche             }
642*44704f69SBart Van Assche         }
643*44704f69SBart Van Assche #endif
644*44704f69SBart Van Assche     }
645*44704f69SBart Van Assche     if (op->do_time && (start_tm.tv_sec || start_tm.tv_usec)) {
646*44704f69SBart Van Assche         struct timeval res_tm;
647*44704f69SBart Van Assche         double a, b;
648*44704f69SBart Van Assche 
649*44704f69SBart Van Assche         gettimeofday(&end_tm, NULL);
650*44704f69SBart Van Assche         res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
651*44704f69SBart Van Assche         res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
652*44704f69SBart Van Assche         if (res_tm.tv_usec < 0) {
653*44704f69SBart Van Assche             --res_tm.tv_sec;
654*44704f69SBart Van Assche             res_tm.tv_usec += 1000000;
655*44704f69SBart Van Assche         }
656*44704f69SBart Van Assche         a = res_tm.tv_sec;
657*44704f69SBart Van Assche         a += (0.000001 * res_tm.tv_usec);
658*44704f69SBart Van Assche         b = (double)buf_size * num;
659*44704f69SBart Van Assche         printf("time to read data from buffer was %d.%06d secs",
660*44704f69SBart Van Assche                (int)res_tm.tv_sec, (int)res_tm.tv_usec);
661*44704f69SBart Van Assche         if (a > 0.00001) {
662*44704f69SBart Van Assche             if (b > 511)
663*44704f69SBart Van Assche                 printf(", %.2f MB/sec", b / (a * 1000000.0));
664*44704f69SBart Van Assche             printf(", %.2f IOPS", num / a);
665*44704f69SBart Van Assche         }
666*44704f69SBart Van Assche         printf("\n");
667*44704f69SBart Van Assche     }
668*44704f69SBart Van Assche     if (dio_incomplete)
669*44704f69SBart Van Assche         printf(">> direct IO requested but not done\n");
670*44704f69SBart Van Assche     printf("Read %" PRId64 " MiB (actual: %" PRId64 " bytes), buffer "
671*44704f69SBart Van Assche            "size=%d KiB (%d bytes)\n", (total_size / (1024 * 1024)),
672*44704f69SBart Van Assche            (int64_t)num * buf_size, buf_size / 1024, buf_size);
673*44704f69SBart Van Assche 
674*44704f69SBart Van Assche     if (rawp) free(rawp);
675*44704f69SBart Van Assche     res = close(sg_fd);
676*44704f69SBart Van Assche     if (res < 0) {
677*44704f69SBart Van Assche         err = errno;
678*44704f69SBart Van Assche         perror("close error");
679*44704f69SBart Van Assche         return sg_convert_errno(err);
680*44704f69SBart Van Assche     }
681*44704f69SBart Van Assche #ifdef DEBUG
682*44704f69SBart Van Assche     if (clear)
683*44704f69SBart Van Assche         printf("read buffer always zero\n");
684*44704f69SBart Van Assche     else
685*44704f69SBart Van Assche         printf("read buffer non-zero\n");
686*44704f69SBart Van Assche #endif
687*44704f69SBart Van Assche     return res;
688*44704f69SBart Van Assche }
689