xref: /aosp_15_r20/external/sg3_utils/src/sg_read_block_limits.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2009-2022 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 <stdbool.h>
16 #include <string.h>
17 #include <getopt.h>
18 #define __STDC_FORMAT_MACROS 1
19 #include <inttypes.h>
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "sg_lib.h"
26 #include "sg_cmds_basic.h"
27 #include "sg_cmds_extra.h"
28 #include "sg_unaligned.h"
29 #include "sg_pr2serr.h"
30 
31 /* A utility program originally written for the Linux OS SCSI subsystem.
32  *
33  *
34  * This program issues the SCSI READ BLOCK LIMITS command (SSC) to the given
35  * SCSI device.
36  */
37 
38 static const char * version_str = "1.09 20221101";
39 
40 #define DEF_READ_BLOCK_LIMITS_LEN 6
41 #define MLIO_READ_BLOCK_LIMITS_LEN 20
42 #define MAX_READ_BLOCK_LIMITS_LEN MLIO_READ_BLOCK_LIMITS_LEN
43 
44 static uint8_t readBlkLmtBuff[MAX_READ_BLOCK_LIMITS_LEN];
45 
46 
47 static struct option long_options[] = {
48         {"help", no_argument, 0, 'h'},
49         {"hex", no_argument, 0, 'H'},
50         {"mloi", no_argument, 0, 'm'},  /* added in ssc4r02.pdf */
51         {"raw", no_argument, 0, 'r'},
52         {"readonly", no_argument, 0, 'R'},
53         {"verbose", no_argument, 0, 'v'},
54         {"version", no_argument, 0, 'V'},
55         {0, 0, 0, 0},
56 };
57 
58 
59 static void
usage()60 usage()
61 {
62     pr2serr("Usage: sg_read_block_limits  [--help] [--hex] [--mloi] "
63             "[--raw]\n"
64             "                             [--readonly] [--verbose] "
65             "[--version]\n"
66             "                             DEVICE\n"
67             "  where:\n"
68             "    --help|-h          print out usage message\n"
69             "    --hex|-H           output response in hexadecimal\n"
70             "    --mloi|-m          output maximum logical object "
71             "identifier\n"
72             "    --raw|-r           output response in binary to stdout\n"
73             "    --readonly|-R      open DEVICE in read-only mode\n"
74             "    --verbose|-v       increase verbosity\n"
75             "    --version|-V       print version string and exit\n\n"
76             "Performs a SCSI READ BLOCK LIMITS command and decode the "
77             "response\n"
78             );
79 }
80 
81 static void
dStrRaw(const char * str,int len)82 dStrRaw(const char * str, int len)
83 {
84     int k;
85 
86     for (k = 0; k < len; ++k)
87         printf("%c", str[k]);
88 }
89 
90 int
main(int argc,char * argv[])91 main(int argc, char * argv[])
92 {
93     bool do_mloi = false;
94     bool do_raw = false;
95     bool readonly = false;
96     bool verbose_given = false;
97     bool version_given = false;
98     int sg_fd, k, m, res, c, max_resp_len;
99     int resid = 0;
100     int actual_len = 0;
101     int do_hex = 0;
102     int verbose = 0;
103     int ret = 0;
104     uint32_t max_block_size;
105     uint64_t mloi;
106     uint16_t min_block_size;
107     uint8_t granularity;
108     const char * device_name = NULL;
109 
110     while (1) {
111         int option_index = 0;
112 
113         c = getopt_long(argc, argv, "hHmrRvV", long_options,
114                         &option_index);
115         if (c == -1)
116             break;
117 
118         switch (c) {
119         case 'h':
120         case '?':
121             usage();
122             return 0;
123         case 'H':
124             ++do_hex;
125             break;
126         case 'm':
127             do_mloi = true;
128             break;
129         case 'r':
130             do_raw = true;
131             break;
132         case 'R':
133             readonly = true;
134             break;
135         case 'v':
136             verbose_given = true;
137             ++verbose;
138             break;
139         case 'V':
140             version_given = true;
141             break;
142         default:
143             pr2serr("invalid option -%c ??\n", c);
144             usage();
145             return SG_LIB_SYNTAX_ERROR;
146         }
147     }
148     if (optind < argc) {
149         if (NULL == device_name) {
150             device_name = argv[optind];
151             ++optind;
152         }
153         if (optind < argc) {
154             for (; optind < argc; ++optind)
155                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
156             usage();
157             return SG_LIB_SYNTAX_ERROR;
158         }
159     }
160 #ifdef DEBUG
161     pr2serr("In DEBUG mode, ");
162     if (verbose_given && version_given) {
163         pr2serr("but override: '-vV' given, zero verbose and continue\n");
164         verbose_given = false;
165         version_given = false;
166         verbose = 0;
167     } else if (! verbose_given) {
168         pr2serr("set '-vv'\n");
169         verbose = 2;
170     } else
171         pr2serr("keep verbose=%d\n", verbose);
172 #else
173     if (verbose_given && version_given)
174         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
175 #endif
176     if (version_given) {
177         printf("version: %s\n", version_str);
178         return 0;
179     }
180 
181     if (NULL == device_name) {
182         pr2serr("missing device name!\n");
183         usage();
184         return SG_LIB_SYNTAX_ERROR;
185     }
186 
187     sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
188     if (sg_fd < 0) {
189         if (verbose)
190             pr2serr("open error: %s: %s\n", device_name,
191                     safe_strerror(-sg_fd));
192         ret = sg_convert_errno(-sg_fd);
193         goto the_end2;
194     }
195 
196     max_resp_len = do_mloi ? MLIO_READ_BLOCK_LIMITS_LEN :
197                              DEF_READ_BLOCK_LIMITS_LEN;
198     memset(readBlkLmtBuff, 0x0, sizeof(readBlkLmtBuff));
199     res = sg_ll_read_block_limits_v2(sg_fd, do_mloi, readBlkLmtBuff,
200                                      max_resp_len, &resid, true, verbose);
201     ret = res;
202     if (0 == res) {
203         actual_len =  max_resp_len - resid;
204         if (do_hex) {
205             int fl = -1;
206 
207             if (1 == do_hex)
208                 fl = 1;
209             else if (2 == do_hex)
210                 fl = 0;
211             hex2stdout(readBlkLmtBuff, actual_len, fl);
212             goto the_end;
213         } else if (do_raw) {
214             dStrRaw((const char *)readBlkLmtBuff, actual_len);
215             goto the_end;
216         }
217 
218         if (do_mloi) {
219             if (actual_len < MLIO_READ_BLOCK_LIMITS_LEN) {
220                 pr2serr("Expected at least %d bytes in response but only "
221                         "%d bytes\n", MLIO_READ_BLOCK_LIMITS_LEN, actual_len);
222                 goto the_end;
223             }
224             printf("Read Block Limits (MLOI=1) results:\n");
225             mloi = sg_get_unaligned_be64(readBlkLmtBuff + 12);
226             printf("    Maximum logical block identifier: %" PRIu64 "\n",
227                    mloi);
228         } else {        /* MLOI=0 (only case before ssc4r02.pdf) */
229             if (actual_len < DEF_READ_BLOCK_LIMITS_LEN) {
230                 pr2serr("Expected at least %d bytes in response but only "
231                         "%d bytes\n", DEF_READ_BLOCK_LIMITS_LEN, actual_len);
232                 goto the_end;
233             }
234             max_block_size = sg_get_unaligned_be32(readBlkLmtBuff + 0);
235             // first byte contains granularity field
236             granularity = (max_block_size >> 24) & 0x1F;
237             max_block_size = max_block_size & 0xFFFFFF;
238             min_block_size = sg_get_unaligned_be16(readBlkLmtBuff + 4);
239             k = min_block_size / 1024;
240             printf("Read Block Limits results:\n");
241             printf("    Minimum block size: %u byte(s)",
242                    (unsigned int)min_block_size);
243             if (k != 0)
244                 printf(", %d KB", k);
245             printf("\n");
246             k = max_block_size / 1024;
247             m = max_block_size / 1048576;
248             printf("    Maximum block size: %u byte(s)",
249                    (unsigned int)max_block_size);
250             if (k != 0)
251                 printf(", %d KB", k);
252             if (m != 0)
253                 printf(", %d MB", m);
254             printf("\n");
255             printf("    Granularity: %u",
256                    (unsigned int)granularity);
257             printf("\n");
258         }
259     } else {    /* error detected */
260         char b[80];
261 
262         sg_get_category_sense_str(res, sizeof(b), b, verbose);
263         pr2serr("Read block limits: %s\n", b);
264         if (0 == verbose)
265             pr2serr("    try '-v' option for more information\n");
266     }
267 
268 the_end:
269     res = sg_cmds_close_device(sg_fd);
270     if (res < 0) {
271         pr2serr("close error: %s\n", safe_strerror(-res));
272         if (0 == ret)
273             ret = sg_convert_errno(-res);
274     }
275 the_end2:
276     if (0 == verbose) {
277         if (! sg_if_can2stderr("sg_read_block_limits failed: ", ret))
278             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
279                     "more information\n");
280     }
281     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
282 }
283