xref: /aosp_15_r20/external/sg3_utils/src/sg_wr_mode.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche  * Copyright (c) 2004-2021 Douglas Gilbert.
3*44704f69SBart Van Assche  * All rights reserved.
4*44704f69SBart Van Assche  * Use of this source code is governed by a BSD-style
5*44704f69SBart Van Assche  * license that can be found in the BSD_LICENSE file.
6*44704f69SBart Van Assche  *
7*44704f69SBart Van Assche  * SPDX-License-Identifier: BSD-2-Clause
8*44704f69SBart Van Assche  */
9*44704f69SBart Van Assche 
10*44704f69SBart Van Assche #include <unistd.h>
11*44704f69SBart Van Assche #include <fcntl.h>
12*44704f69SBart Van Assche #include <stdio.h>
13*44704f69SBart Van Assche #include <stdlib.h>
14*44704f69SBart Van Assche #include <stdarg.h>
15*44704f69SBart Van Assche #include <stdbool.h>
16*44704f69SBart Van Assche #include <string.h>
17*44704f69SBart Van Assche #include <getopt.h>
18*44704f69SBart Van Assche #include <ctype.h>
19*44704f69SBart Van Assche 
20*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
21*44704f69SBart Van Assche #include "config.h"
22*44704f69SBart Van Assche #endif
23*44704f69SBart Van Assche 
24*44704f69SBart Van Assche #include "sg_lib.h"
25*44704f69SBart Van Assche #include "sg_cmds_basic.h"
26*44704f69SBart Van Assche #include "sg_unaligned.h"
27*44704f69SBart Van Assche #include "sg_pr2serr.h"
28*44704f69SBart Van Assche 
29*44704f69SBart Van Assche /* A utility program originally written for the Linux OS SCSI subsystem.
30*44704f69SBart Van Assche  *
31*44704f69SBart Van Assche  * This program writes the given mode page contents to the corresponding
32*44704f69SBart Van Assche  * mode page on the given device.
33*44704f69SBart Van Assche  */
34*44704f69SBart Van Assche 
35*44704f69SBart Van Assche static const char * version_str = "1.27 20210610";
36*44704f69SBart Van Assche 
37*44704f69SBart Van Assche #define ME "sg_wr_mode: "
38*44704f69SBart Van Assche 
39*44704f69SBart Van Assche #define MX_ALLOC_LEN 2048
40*44704f69SBart Van Assche #define SHORT_ALLOC_LEN 252
41*44704f69SBart Van Assche 
42*44704f69SBart Van Assche #define EBUFF_SZ 256
43*44704f69SBart Van Assche 
44*44704f69SBart Van Assche 
45*44704f69SBart Van Assche static struct option long_options[] = {
46*44704f69SBart Van Assche         {"contents", required_argument, 0, 'c'},
47*44704f69SBart Van Assche         {"dbd", no_argument, 0, 'd'},
48*44704f69SBart Van Assche         {"force", no_argument, 0, 'f'},
49*44704f69SBart Van Assche         {"help", no_argument, 0, 'h'},
50*44704f69SBart Van Assche         {"len", required_argument, 0, 'l'},
51*44704f69SBart Van Assche         {"mask", required_argument, 0, 'm'},
52*44704f69SBart Van Assche         {"page", required_argument, 0, 'p'},
53*44704f69SBart Van Assche         {"rtd", no_argument, 0, 'R'},
54*44704f69SBart Van Assche         {"save", no_argument, 0, 's'},
55*44704f69SBart Van Assche         {"six", no_argument, 0, '6'},
56*44704f69SBart Van Assche         {"verbose", no_argument, 0, 'v'},
57*44704f69SBart Van Assche         {"version", no_argument, 0, 'V'},
58*44704f69SBart Van Assche         {0, 0, 0, 0},
59*44704f69SBart Van Assche };
60*44704f69SBart Van Assche 
61*44704f69SBart Van Assche 
62*44704f69SBart Van Assche static void
usage()63*44704f69SBart Van Assche usage()
64*44704f69SBart Van Assche {
65*44704f69SBart Van Assche     pr2serr("Usage: sg_wr_mode [--contents=H,H...] [--dbd] [--force] "
66*44704f69SBart Van Assche             "[--help]\n"
67*44704f69SBart Van Assche             "                  [--len=10|6] [--mask=M,M...] "
68*44704f69SBart Van Assche             "[--page=PG_H[,SPG_H]]\n"
69*44704f69SBart Van Assche             "                  [--rtd] [--save] [--six] [--verbose] "
70*44704f69SBart Van Assche             "[--version]\n"
71*44704f69SBart Van Assche             "                  DEVICE\n"
72*44704f69SBart Van Assche             "  where:\n"
73*44704f69SBart Van Assche             "    --contents=H,H... | -c H,H...    comma separated string "
74*44704f69SBart Van Assche             "of hex numbers\n"
75*44704f69SBart Van Assche             "                                     that is mode page contents "
76*44704f69SBart Van Assche             "to write\n"
77*44704f69SBart Van Assche             "    --contents=- | -c -   read stdin for mode page contents"
78*44704f69SBart Van Assche             " to write\n"
79*44704f69SBart Van Assche             "    --dbd | -d            disable block descriptors (DBD bit"
80*44704f69SBart Van Assche             " in cdb)\n"
81*44704f69SBart Van Assche             "    --force | -f          force the contents to be written\n"
82*44704f69SBart Van Assche             "    --help | -h           print out usage message\n"
83*44704f69SBart Van Assche             "    --len=10|6 | -l 10|6    use 10 byte (def) or 6 byte "
84*44704f69SBart Van Assche             "variants of\n"
85*44704f69SBart Van Assche             "                            SCSI MODE SENSE/SELECT commands\n"
86*44704f69SBart Van Assche             "    --mask=M,M... | -m M,M...   comma separated "
87*44704f69SBart Van Assche             "string of hex\n"
88*44704f69SBart Van Assche             "                                numbers that mask contents"
89*44704f69SBart Van Assche             " to write\n"
90*44704f69SBart Van Assche             "    --page=PG_H | -p PG_H     page_code to be written (in hex)\n"
91*44704f69SBart Van Assche             "    --page=PG_H,SPG_H | -p PG_H,SPG_H    page and subpage code "
92*44704f69SBart Van Assche             "to be\n"
93*44704f69SBart Van Assche             "                                         written (in hex)\n"
94*44704f69SBart Van Assche             "    --rtd | -R            set RTD bit (revert to defaults) in "
95*44704f69SBart Van Assche             "cdb\n"
96*44704f69SBart Van Assche             "    --save | -s           set 'save page' (SP) bit; default "
97*44704f69SBart Van Assche             "don't so\n"
98*44704f69SBart Van Assche             "                          only 'current' values changed\n"
99*44704f69SBart Van Assche             "    --six | -6            do SCSI MODE SENSE/SELECT(6) "
100*44704f69SBart Van Assche             "commands\n"
101*44704f69SBart Van Assche             "    --verbose | -v        increase verbosity\n"
102*44704f69SBart Van Assche             "    --version | -V        print version string and exit\n\n"
103*44704f69SBart Van Assche             "writes given mode page with SCSI MODE SELECT (10 or 6) "
104*44704f69SBart Van Assche             "command\n");
105*44704f69SBart Van Assche }
106*44704f69SBart Van Assche 
107*44704f69SBart Van Assche 
108*44704f69SBart Van Assche /* Read hex numbers from command line or stdin. On the command line can
109*44704f69SBart Van Assche  * either be comma or space separated list. Space separated list need to be
110*44704f69SBart Van Assche  * quoted. For stdin (indicated by *inp=='-') there should be either
111*44704f69SBart Van Assche  * one entry per line, a comma separated list or space separated list.
112*44704f69SBart Van Assche  * Returns 0 if ok, or sg3_utils error code if error. */
113*44704f69SBart Van Assche static int
build_mode_page(const char * inp,uint8_t * mp_arr,int * mp_arr_len,int max_arr_len)114*44704f69SBart Van Assche build_mode_page(const char * inp, uint8_t * mp_arr, int * mp_arr_len,
115*44704f69SBart Van Assche                 int max_arr_len)
116*44704f69SBart Van Assche {
117*44704f69SBart Van Assche     int in_len, k, j, m;
118*44704f69SBart Van Assche     unsigned int h;
119*44704f69SBart Van Assche     const char * lcp;
120*44704f69SBart Van Assche     char * cp;
121*44704f69SBart Van Assche     char * c2p;
122*44704f69SBart Van Assche 
123*44704f69SBart Van Assche     if ((NULL == inp) || (NULL == mp_arr) ||
124*44704f69SBart Van Assche         (NULL == mp_arr_len))
125*44704f69SBart Van Assche         return SG_LIB_LOGIC_ERROR;
126*44704f69SBart Van Assche     lcp = inp;
127*44704f69SBart Van Assche     in_len = strlen(inp);
128*44704f69SBart Van Assche     if (0 == in_len)
129*44704f69SBart Van Assche         *mp_arr_len = 0;
130*44704f69SBart Van Assche     if ('-' == inp[0]) {        /* read from stdin */
131*44704f69SBart Van Assche         bool split_line;
132*44704f69SBart Van Assche         int off = 0;
133*44704f69SBart Van Assche         char carry_over[4];
134*44704f69SBart Van Assche         char line[512];
135*44704f69SBart Van Assche 
136*44704f69SBart Van Assche         carry_over[0] = 0;
137*44704f69SBart Van Assche         for (j = 0; j < 512; ++j) {
138*44704f69SBart Van Assche             if (NULL == fgets(line, sizeof(line), stdin))
139*44704f69SBart Van Assche                 break;
140*44704f69SBart Van Assche             in_len = strlen(line);
141*44704f69SBart Van Assche             if (in_len > 0) {
142*44704f69SBart Van Assche                 if ('\n' == line[in_len - 1]) {
143*44704f69SBart Van Assche                     --in_len;
144*44704f69SBart Van Assche                     line[in_len] = '\0';
145*44704f69SBart Van Assche                     split_line = false;
146*44704f69SBart Van Assche                 } else
147*44704f69SBart Van Assche                     split_line = true;
148*44704f69SBart Van Assche             }
149*44704f69SBart Van Assche             if (in_len < 1) {
150*44704f69SBart Van Assche                 carry_over[0] = 0;
151*44704f69SBart Van Assche                 continue;
152*44704f69SBart Van Assche             }
153*44704f69SBart Van Assche             if (carry_over[0]) {
154*44704f69SBart Van Assche                 if (isxdigit((uint8_t)line[0])) {
155*44704f69SBart Van Assche                     carry_over[1] = line[0];
156*44704f69SBart Van Assche                     carry_over[2] = '\0';
157*44704f69SBart Van Assche                     if (1 == sscanf(carry_over, "%x", &h))
158*44704f69SBart Van Assche                         mp_arr[off - 1] = h;       /* back up and overwrite */
159*44704f69SBart Van Assche                     else {
160*44704f69SBart Van Assche                         pr2serr("%s: carry_over error ['%s'] around line "
161*44704f69SBart Van Assche                                 "%d\n", __func__, carry_over, j + 1);
162*44704f69SBart Van Assche                         return SG_LIB_SYNTAX_ERROR;
163*44704f69SBart Van Assche                     }
164*44704f69SBart Van Assche                     lcp = line + 1;
165*44704f69SBart Van Assche                     --in_len;
166*44704f69SBart Van Assche                 } else
167*44704f69SBart Van Assche                     lcp = line;
168*44704f69SBart Van Assche                 carry_over[0] = 0;
169*44704f69SBart Van Assche             } else
170*44704f69SBart Van Assche                 lcp = line;
171*44704f69SBart Van Assche             m = strspn(lcp, " \t");
172*44704f69SBart Van Assche             if (m == in_len)
173*44704f69SBart Van Assche                 continue;
174*44704f69SBart Van Assche             lcp += m;
175*44704f69SBart Van Assche             in_len -= m;
176*44704f69SBart Van Assche             if ('#' == *lcp)
177*44704f69SBart Van Assche                 continue;
178*44704f69SBart Van Assche             k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
179*44704f69SBart Van Assche             if ((k < in_len) && ('#' != lcp[k])) {
180*44704f69SBart Van Assche                 pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
181*44704f69SBart Van Assche                         j + 1, m + k + 1);
182*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
183*44704f69SBart Van Assche             }
184*44704f69SBart Van Assche             for (k = 0; k < 1024; ++k) {
185*44704f69SBart Van Assche                 if (1 == sscanf(lcp, "%x", &h)) {
186*44704f69SBart Van Assche                     if (h > 0xff) {
187*44704f69SBart Van Assche                         pr2serr("%s: hex number larger than 0xff in line %d, "
188*44704f69SBart Van Assche                                 "pos %d\n", __func__, j + 1,
189*44704f69SBart Van Assche                                 (int)(lcp - line + 1));
190*44704f69SBart Van Assche                         return SG_LIB_SYNTAX_ERROR;
191*44704f69SBart Van Assche                     }
192*44704f69SBart Van Assche                     if (split_line && (1 == strlen(lcp))) {
193*44704f69SBart Van Assche                         /* single trailing hex digit might be a split pair */
194*44704f69SBart Van Assche                         carry_over[0] = *lcp;
195*44704f69SBart Van Assche                     }
196*44704f69SBart Van Assche                     if ((off + k) >= max_arr_len) {
197*44704f69SBart Van Assche                         pr2serr("%s: array length exceeded\n", __func__);
198*44704f69SBart Van Assche                         return SG_LIB_SYNTAX_ERROR;
199*44704f69SBart Van Assche                     }
200*44704f69SBart Van Assche                     mp_arr[off + k] = h;
201*44704f69SBart Van Assche                     lcp = strpbrk(lcp, " ,\t");
202*44704f69SBart Van Assche                     if (NULL == lcp)
203*44704f69SBart Van Assche                         break;
204*44704f69SBart Van Assche                     lcp += strspn(lcp, " ,\t");
205*44704f69SBart Van Assche                     if ('\0' == *lcp)
206*44704f69SBart Van Assche                         break;
207*44704f69SBart Van Assche                 } else {
208*44704f69SBart Van Assche                     if ('#' == *lcp) {
209*44704f69SBart Van Assche                         --k;
210*44704f69SBart Van Assche                         break;
211*44704f69SBart Van Assche                     }
212*44704f69SBart Van Assche                     pr2serr("%s: error in line %d, at pos %d\n", __func__,
213*44704f69SBart Van Assche                             j + 1, (int)(lcp - line + 1));
214*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
215*44704f69SBart Van Assche                 }
216*44704f69SBart Van Assche             }
217*44704f69SBart Van Assche             off += (k + 1);
218*44704f69SBart Van Assche         }
219*44704f69SBart Van Assche         *mp_arr_len = off;
220*44704f69SBart Van Assche     } else {        /* hex string on command line */
221*44704f69SBart Van Assche         k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
222*44704f69SBart Van Assche         if (in_len != k) {
223*44704f69SBart Van Assche             pr2serr("%s: error at pos %d\n", __func__, k + 1);
224*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
225*44704f69SBart Van Assche         }
226*44704f69SBart Van Assche         for (k = 0; k < max_arr_len; ++k) {
227*44704f69SBart Van Assche             if (1 == sscanf(lcp, "%x", &h)) {
228*44704f69SBart Van Assche                 if (h > 0xff) {
229*44704f69SBart Van Assche                     pr2serr("%s: hex number larger than 0xff at pos %d\n",
230*44704f69SBart Van Assche                             __func__, (int)(lcp - inp + 1));
231*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
232*44704f69SBart Van Assche                 }
233*44704f69SBart Van Assche                 mp_arr[k] = h;
234*44704f69SBart Van Assche                 cp = (char *)strchr(lcp, ',');
235*44704f69SBart Van Assche                 c2p = (char *)strchr(lcp, ' ');
236*44704f69SBart Van Assche                 if (NULL == cp)
237*44704f69SBart Van Assche                     cp = c2p;
238*44704f69SBart Van Assche                 if (NULL == cp)
239*44704f69SBart Van Assche                     break;
240*44704f69SBart Van Assche                 if (c2p && (c2p < cp))
241*44704f69SBart Van Assche                     cp = c2p;
242*44704f69SBart Van Assche                 lcp = cp + 1;
243*44704f69SBart Van Assche             } else {
244*44704f69SBart Van Assche                 pr2serr("%s: error at pos %d\n", __func__,
245*44704f69SBart Van Assche                         (int)(lcp - inp + 1));
246*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
247*44704f69SBart Van Assche             }
248*44704f69SBart Van Assche         }
249*44704f69SBart Van Assche         *mp_arr_len = k + 1;
250*44704f69SBart Van Assche         if (k == max_arr_len) {
251*44704f69SBart Van Assche             pr2serr("%s: array length exceeded\n", __func__);
252*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
253*44704f69SBart Van Assche         }
254*44704f69SBart Van Assche     }
255*44704f69SBart Van Assche     return 0;
256*44704f69SBart Van Assche }
257*44704f69SBart Van Assche 
258*44704f69SBart Van Assche /* Read hex numbers from command line (comma separated list).
259*44704f69SBart Van Assche  * Can also be (single) space separated list but needs to be quoted on the
260*44704f69SBart Van Assche  * command line. Returns 0 if ok, or 1 if error. */
261*44704f69SBart Van Assche static int
build_mask(const char * inp,uint8_t * mask_arr,int * mask_arr_len,int max_arr_len)262*44704f69SBart Van Assche build_mask(const char * inp, uint8_t * mask_arr, int * mask_arr_len,
263*44704f69SBart Van Assche            int max_arr_len)
264*44704f69SBart Van Assche {
265*44704f69SBart Van Assche     int in_len, k;
266*44704f69SBart Van Assche     unsigned int h;
267*44704f69SBart Van Assche     const char * lcp;
268*44704f69SBart Van Assche     char * cp;
269*44704f69SBart Van Assche     char * c2p;
270*44704f69SBart Van Assche 
271*44704f69SBart Van Assche     if ((NULL == inp) || (NULL == mask_arr) ||
272*44704f69SBart Van Assche         (NULL == mask_arr_len))
273*44704f69SBart Van Assche         return 1;
274*44704f69SBart Van Assche     lcp = inp;
275*44704f69SBart Van Assche     in_len = strlen(inp);
276*44704f69SBart Van Assche     if (0 == in_len)
277*44704f69SBart Van Assche         *mask_arr_len = 0;
278*44704f69SBart Van Assche     if ('-' == inp[0]) {        /* read from stdin */
279*44704f69SBart Van Assche         pr2serr("'--mask' does not accept input from stdin\n");
280*44704f69SBart Van Assche         return 1;
281*44704f69SBart Van Assche     } else {        /* hex string on command line */
282*44704f69SBart Van Assche         k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
283*44704f69SBart Van Assche         if (in_len != k) {
284*44704f69SBart Van Assche             pr2serr("%s: error at pos %d\n", __func__, k + 1);
285*44704f69SBart Van Assche             return 1;
286*44704f69SBart Van Assche         }
287*44704f69SBart Van Assche         for (k = 0; k < max_arr_len; ++k) {
288*44704f69SBart Van Assche             if (1 == sscanf(lcp, "%x", &h)) {
289*44704f69SBart Van Assche                 if (h > 0xff) {
290*44704f69SBart Van Assche                     pr2serr("%s: hex number larger than 0xff at pos %d\n",
291*44704f69SBart Van Assche                             __func__, (int)(lcp - inp + 1));
292*44704f69SBart Van Assche                     return 1;
293*44704f69SBart Van Assche                 }
294*44704f69SBart Van Assche                 mask_arr[k] = h;
295*44704f69SBart Van Assche                 cp = (char *)strchr(lcp, ',');
296*44704f69SBart Van Assche                 c2p = (char *)strchr(lcp, ' ');
297*44704f69SBart Van Assche                 if (NULL == cp)
298*44704f69SBart Van Assche                     cp = c2p;
299*44704f69SBart Van Assche                 if (NULL == cp)
300*44704f69SBart Van Assche                     break;
301*44704f69SBart Van Assche                 if (c2p && (c2p < cp))
302*44704f69SBart Van Assche                     cp = c2p;
303*44704f69SBart Van Assche                 lcp = cp + 1;
304*44704f69SBart Van Assche             } else {
305*44704f69SBart Van Assche                 pr2serr("%s: error at pos %d\n", __func__,
306*44704f69SBart Van Assche                         (int)(lcp - inp + 1));
307*44704f69SBart Van Assche                 return 1;
308*44704f69SBart Van Assche             }
309*44704f69SBart Van Assche         }
310*44704f69SBart Van Assche         *mask_arr_len = k + 1;
311*44704f69SBart Van Assche         if (k == max_arr_len) {
312*44704f69SBart Van Assche             pr2serr("%s: array length exceeded\n", __func__);
313*44704f69SBart Van Assche             return 1;
314*44704f69SBart Van Assche         }
315*44704f69SBart Van Assche     }
316*44704f69SBart Van Assche     return 0;
317*44704f69SBart Van Assche }
318*44704f69SBart Van Assche 
319*44704f69SBart Van Assche 
320*44704f69SBart Van Assche int
main(int argc,char * argv[])321*44704f69SBart Van Assche main(int argc, char * argv[])
322*44704f69SBart Van Assche {
323*44704f69SBart Van Assche     bool dbd = false;
324*44704f69SBart Van Assche     bool force = false;
325*44704f69SBart Van Assche     bool got_contents = false;
326*44704f69SBart Van Assche     bool got_mask = false;
327*44704f69SBart Van Assche     bool mode_6 = false;        /* so default is mode_10 */
328*44704f69SBart Van Assche     bool rtd = false;   /* added in spc5r11 */
329*44704f69SBart Van Assche     bool save = false;
330*44704f69SBart Van Assche     bool verbose_given = false;
331*44704f69SBart Van Assche     bool version_given = false;
332*44704f69SBart Van Assche     int res, c, num, alloc_len, off, pdt, k, md_len, hdr_len, bd_len;
333*44704f69SBart Van Assche     int mask_in_len;
334*44704f69SBart Van Assche     int sg_fd = -1;
335*44704f69SBart Van Assche     int pg_code = -1;
336*44704f69SBart Van Assche     int sub_pg_code = 0;
337*44704f69SBart Van Assche     int verbose = 0;
338*44704f69SBart Van Assche     int read_in_len = 0;
339*44704f69SBart Van Assche     int ret = 0;
340*44704f69SBart Van Assche     unsigned u, uu;
341*44704f69SBart Van Assche     const char * device_name = NULL;
342*44704f69SBart Van Assche     uint8_t read_in[MX_ALLOC_LEN];
343*44704f69SBart Van Assche     uint8_t mask_in[MX_ALLOC_LEN];
344*44704f69SBart Van Assche     uint8_t ref_md[MX_ALLOC_LEN];
345*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
346*44704f69SBart Van Assche     char errStr[128];
347*44704f69SBart Van Assche     char b[80];
348*44704f69SBart Van Assche     struct sg_simple_inquiry_resp inq_data;
349*44704f69SBart Van Assche 
350*44704f69SBart Van Assche     while (1) {
351*44704f69SBart Van Assche         int option_index = 0;
352*44704f69SBart Van Assche 
353*44704f69SBart Van Assche         c = getopt_long(argc, argv, "6c:dfhl:m:p:RsvV", long_options,
354*44704f69SBart Van Assche                         &option_index);
355*44704f69SBart Van Assche         if (c == -1)
356*44704f69SBart Van Assche             break;
357*44704f69SBart Van Assche 
358*44704f69SBart Van Assche         switch (c) {
359*44704f69SBart Van Assche         case '6':
360*44704f69SBart Van Assche             mode_6 = true;
361*44704f69SBart Van Assche             break;
362*44704f69SBart Van Assche         case 'c':
363*44704f69SBart Van Assche             memset(read_in, 0, sizeof(read_in));
364*44704f69SBart Van Assche             if ((ret = build_mode_page(optarg, read_in, &read_in_len,
365*44704f69SBart Van Assche                                        sizeof(read_in)))) {
366*44704f69SBart Van Assche                 pr2serr("bad argument to '--contents='\n");
367*44704f69SBart Van Assche                 return ret;
368*44704f69SBart Van Assche             }
369*44704f69SBart Van Assche             got_contents = true;
370*44704f69SBart Van Assche             break;
371*44704f69SBart Van Assche         case 'd':
372*44704f69SBart Van Assche             dbd = true;
373*44704f69SBart Van Assche             break;
374*44704f69SBart Van Assche         case 'f':
375*44704f69SBart Van Assche             force = true;
376*44704f69SBart Van Assche             break;
377*44704f69SBart Van Assche         case 'h':
378*44704f69SBart Van Assche         case '?':
379*44704f69SBart Van Assche             usage();
380*44704f69SBart Van Assche             return 0;
381*44704f69SBart Van Assche         case 'l':
382*44704f69SBart Van Assche             num = sscanf(optarg, "%d", &res);
383*44704f69SBart Van Assche             if ((1 == num) && ((6 == res) || (10 == res)))
384*44704f69SBart Van Assche                 mode_6 = (6 == res);
385*44704f69SBart Van Assche             else {
386*44704f69SBart Van Assche                 pr2serr("length (of cdb) must be 6 or 10\n");
387*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
388*44704f69SBart Van Assche             }
389*44704f69SBart Van Assche             break;
390*44704f69SBart Van Assche         case 'm':
391*44704f69SBart Van Assche             memset(mask_in, 0xff, sizeof(mask_in));
392*44704f69SBart Van Assche             if (0 != build_mask(optarg, mask_in, &mask_in_len,
393*44704f69SBart Van Assche                                 sizeof(mask_in))) {
394*44704f69SBart Van Assche                 pr2serr("bad argument to '--mask'\n");
395*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
396*44704f69SBart Van Assche             }
397*44704f69SBart Van Assche             got_mask = true;
398*44704f69SBart Van Assche             break;
399*44704f69SBart Van Assche         case 'p':
400*44704f69SBart Van Assche            if (NULL == strchr(optarg, ',')) {
401*44704f69SBart Van Assche                 num = sscanf(optarg, "%x", &u);
402*44704f69SBart Van Assche                 if ((1 != num) || (u > 62)) {
403*44704f69SBart Van Assche                     pr2serr("Bad hex page code value after '--page' "
404*44704f69SBart Van Assche                             "switch\n");
405*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
406*44704f69SBart Van Assche                 }
407*44704f69SBart Van Assche                 pg_code = u;
408*44704f69SBart Van Assche             } else if (2 == sscanf(optarg, "%x,%x", &u, &uu)) {
409*44704f69SBart Van Assche                 if (uu > 254) {
410*44704f69SBart Van Assche                     pr2serr("Bad hex sub page code value after '--page' "
411*44704f69SBart Van Assche                             "switch\n");
412*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
413*44704f69SBart Van Assche                 }
414*44704f69SBart Van Assche                 pg_code = u;
415*44704f69SBart Van Assche                 sub_pg_code = uu;
416*44704f69SBart Van Assche             } else {
417*44704f69SBart Van Assche                 pr2serr("Bad hex page code, subpage code sequence after "
418*44704f69SBart Van Assche                         "'--page' switch\n");
419*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
420*44704f69SBart Van Assche             }
421*44704f69SBart Van Assche             break;
422*44704f69SBart Van Assche         case 'R':
423*44704f69SBart Van Assche             rtd = true;
424*44704f69SBart Van Assche             break;
425*44704f69SBart Van Assche         case 's':
426*44704f69SBart Van Assche             save = true;
427*44704f69SBart Van Assche             break;
428*44704f69SBart Van Assche         case 'v':
429*44704f69SBart Van Assche             verbose_given = true;
430*44704f69SBart Van Assche             ++verbose;
431*44704f69SBart Van Assche             break;
432*44704f69SBart Van Assche         case 'V':
433*44704f69SBart Van Assche             version_given = true;
434*44704f69SBart Van Assche             break;
435*44704f69SBart Van Assche         default:
436*44704f69SBart Van Assche             pr2serr("unrecognised option code 0x%x ??\n", c);
437*44704f69SBart Van Assche             usage();
438*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
439*44704f69SBart Van Assche         }
440*44704f69SBart Van Assche     }
441*44704f69SBart Van Assche     if (optind < argc) {
442*44704f69SBart Van Assche         if (NULL == device_name) {
443*44704f69SBart Van Assche             device_name = argv[optind];
444*44704f69SBart Van Assche             ++optind;
445*44704f69SBart Van Assche         }
446*44704f69SBart Van Assche         if (optind < argc) {
447*44704f69SBart Van Assche             for (; optind < argc; ++optind)
448*44704f69SBart Van Assche                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
449*44704f69SBart Van Assche             usage();
450*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
451*44704f69SBart Van Assche         }
452*44704f69SBart Van Assche     }
453*44704f69SBart Van Assche 
454*44704f69SBart Van Assche #ifdef DEBUG
455*44704f69SBart Van Assche     pr2serr("In DEBUG mode, ");
456*44704f69SBart Van Assche     if (verbose_given && version_given) {
457*44704f69SBart Van Assche         pr2serr("but override: '-vV' given, zero verbose and continue\n");
458*44704f69SBart Van Assche         verbose_given = false;
459*44704f69SBart Van Assche         version_given = false;
460*44704f69SBart Van Assche         verbose = 0;
461*44704f69SBart Van Assche     } else if (! verbose_given) {
462*44704f69SBart Van Assche         pr2serr("set '-vv'\n");
463*44704f69SBart Van Assche         verbose = 2;
464*44704f69SBart Van Assche     } else
465*44704f69SBart Van Assche         pr2serr("keep verbose=%d\n", verbose);
466*44704f69SBart Van Assche #else
467*44704f69SBart Van Assche     if (verbose_given && version_given)
468*44704f69SBart Van Assche         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
469*44704f69SBart Van Assche #endif
470*44704f69SBart Van Assche     if (version_given) {
471*44704f69SBart Van Assche         pr2serr(ME "version: %s\n", version_str);
472*44704f69SBart Van Assche         return 0;
473*44704f69SBart Van Assche     }
474*44704f69SBart Van Assche 
475*44704f69SBart Van Assche     if (NULL == device_name) {
476*44704f69SBart Van Assche         pr2serr("missing device name!\n\n");
477*44704f69SBart Van Assche         usage();
478*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
479*44704f69SBart Van Assche     }
480*44704f69SBart Van Assche     if ((pg_code < 0) && (! rtd)) {
481*44704f69SBart Van Assche         pr2serr("need page code (see '--page=')\n\n");
482*44704f69SBart Van Assche         usage();
483*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
484*44704f69SBart Van Assche     }
485*44704f69SBart Van Assche     if (got_mask && force) {
486*44704f69SBart Van Assche         pr2serr("cannot use both '--force' and '--mask'\n\n");
487*44704f69SBart Van Assche         usage();
488*44704f69SBart Van Assche         return SG_LIB_CONTRADICT;
489*44704f69SBart Van Assche     }
490*44704f69SBart Van Assche 
491*44704f69SBart Van Assche     sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose);
492*44704f69SBart Van Assche     if (sg_fd < 0) {
493*44704f69SBart Van Assche         if (verbose)
494*44704f69SBart Van Assche             pr2serr(ME "open error: %s: %s\n", device_name,
495*44704f69SBart Van Assche                     safe_strerror(-sg_fd));
496*44704f69SBart Van Assche         ret = sg_convert_errno(-sg_fd);
497*44704f69SBart Van Assche         goto fini;
498*44704f69SBart Van Assche     }
499*44704f69SBart Van Assche     if (rtd)
500*44704f69SBart Van Assche         goto revert_to_defaults;
501*44704f69SBart Van Assche 
502*44704f69SBart Van Assche     if (0 == sg_simple_inquiry(sg_fd, &inq_data, false, verbose))
503*44704f69SBart Van Assche         pdt = inq_data.peripheral_type;
504*44704f69SBart Van Assche     else
505*44704f69SBart Van Assche         pdt = PDT_UNKNOWN;
506*44704f69SBart Van Assche 
507*44704f69SBart Van Assche     /* do MODE SENSE to fetch current values */
508*44704f69SBart Van Assche     memset(ref_md, 0, MX_ALLOC_LEN);
509*44704f69SBart Van Assche     snprintf(errStr, sizeof(errStr), "MODE SENSE (%d): ", mode_6 ? 6 : 10);
510*44704f69SBart Van Assche     alloc_len = mode_6 ? SHORT_ALLOC_LEN : MX_ALLOC_LEN;
511*44704f69SBart Van Assche     if (mode_6)
512*44704f69SBart Van Assche         res = sg_ll_mode_sense6(sg_fd, dbd, 0 /*current */, pg_code,
513*44704f69SBart Van Assche                                 sub_pg_code, ref_md, alloc_len, true,
514*44704f69SBart Van Assche                                 verbose);
515*44704f69SBart Van Assche      else
516*44704f69SBart Van Assche         res = sg_ll_mode_sense10(sg_fd, false /* llbaa */, dbd,
517*44704f69SBart Van Assche                                  0 /* current */, pg_code, sub_pg_code,
518*44704f69SBart Van Assche                                  ref_md, alloc_len, true, verbose);
519*44704f69SBart Van Assche     ret = res;
520*44704f69SBart Van Assche     if (res) {
521*44704f69SBart Van Assche         if (SG_LIB_CAT_INVALID_OP == res)
522*44704f69SBart Van Assche             pr2serr("%snot supported, try '--len=%d'\n", errStr,
523*44704f69SBart Van Assche                     (mode_6 ? 10 : 6));
524*44704f69SBart Van Assche         else {
525*44704f69SBart Van Assche             sg_get_category_sense_str(res, sizeof(b), b, verbose);
526*44704f69SBart Van Assche             pr2serr("%s%s\n", errStr, b);
527*44704f69SBart Van Assche         }
528*44704f69SBart Van Assche         goto fini;
529*44704f69SBart Van Assche     }
530*44704f69SBart Van Assche     off = sg_mode_page_offset(ref_md, alloc_len, mode_6, ebuff, EBUFF_SZ);
531*44704f69SBart Van Assche     if (off < 0) {
532*44704f69SBart Van Assche         pr2serr("%s%s\n", errStr, ebuff);
533*44704f69SBart Van Assche         goto fini;
534*44704f69SBart Van Assche     }
535*44704f69SBart Van Assche     md_len = sg_msense_calc_length(ref_md, alloc_len, mode_6, &bd_len);
536*44704f69SBart Van Assche     if (md_len < 0) {
537*44704f69SBart Van Assche         pr2serr("%ssg_msense_calc_length() failed\n", errStr);
538*44704f69SBart Van Assche         goto fini;
539*44704f69SBart Van Assche     }
540*44704f69SBart Van Assche     hdr_len = mode_6 ? 4 : 8;
541*44704f69SBart Van Assche     if (got_contents) {
542*44704f69SBart Van Assche         if (read_in_len < 2) {
543*44704f69SBart Van Assche             pr2serr("contents length=%d too short\n", read_in_len);
544*44704f69SBart Van Assche             goto fini;
545*44704f69SBart Van Assche         }
546*44704f69SBart Van Assche         ref_md[0] = 0;  /* mode data length reserved for mode select */
547*44704f69SBart Van Assche         if (! mode_6)
548*44704f69SBart Van Assche             ref_md[1] = 0;    /* mode data length reserved for mode select */
549*44704f69SBart Van Assche         if (0 == pdt)   /* for disks mask out DPOFUA bit */
550*44704f69SBart Van Assche             ref_md[mode_6 ? 2 : 3] &= 0xef;
551*44704f69SBart Van Assche         if (md_len > alloc_len) {
552*44704f69SBart Van Assche             pr2serr("mode data length=%d exceeds allocation length=%d\n",
553*44704f69SBart Van Assche                     md_len, alloc_len);
554*44704f69SBart Van Assche             goto fini;
555*44704f69SBart Van Assche         }
556*44704f69SBart Van Assche         if (got_mask) {
557*44704f69SBart Van Assche             for (k = 0; k < (md_len - off); ++k) {
558*44704f69SBart Van Assche                 if ((0x0 == mask_in[k]) || (k > read_in_len))
559*44704f69SBart Van Assche                    read_in[k] = ref_md[off + k];
560*44704f69SBart Van Assche                 else if (mask_in[k] < 0xff) {
561*44704f69SBart Van Assche                    c = (ref_md[off + k] & (0xff & ~mask_in[k]));
562*44704f69SBart Van Assche                    read_in[k] = (c | (read_in[k] & mask_in[k]));
563*44704f69SBart Van Assche                 }
564*44704f69SBart Van Assche             }
565*44704f69SBart Van Assche             read_in_len = md_len - off;
566*44704f69SBart Van Assche         }
567*44704f69SBart Van Assche         if (! force) {
568*44704f69SBart Van Assche             if ((! (ref_md[off] & 0x80)) && save) {
569*44704f69SBart Van Assche                 pr2serr("PS bit in existing mode page indicates that it is "
570*44704f69SBart Van Assche                         "not saveable\n    but '--save' option given\n");
571*44704f69SBart Van Assche                 goto fini;
572*44704f69SBart Van Assche             }
573*44704f69SBart Van Assche             read_in[0] &= 0x7f; /* mask out PS bit, reserved in mode select */
574*44704f69SBart Van Assche             if ((md_len - off) != read_in_len) {
575*44704f69SBart Van Assche                 pr2serr("contents length=%d but reference mode page "
576*44704f69SBart Van Assche                         "length=%d\n", read_in_len, md_len - off);
577*44704f69SBart Van Assche                 goto fini;
578*44704f69SBart Van Assche             }
579*44704f69SBart Van Assche             if (pg_code != (read_in[0] & 0x3f)) {
580*44704f69SBart Van Assche                 pr2serr("contents page_code=0x%x but reference "
581*44704f69SBart Van Assche                         "page_code=0x%x\n", (read_in[0] & 0x3f), pg_code);
582*44704f69SBart Van Assche                 goto fini;
583*44704f69SBart Van Assche             }
584*44704f69SBart Van Assche             if ((read_in[0] & 0x40) != (ref_md[off] & 0x40)) {
585*44704f69SBart Van Assche                 pr2serr("contents flags subpage but reference page does not "
586*44704f69SBart Van Assche                         "(or vice versa)\n");
587*44704f69SBart Van Assche                 goto fini;
588*44704f69SBart Van Assche             }
589*44704f69SBart Van Assche             if ((read_in[0] & 0x40) && (read_in[1] != sub_pg_code)) {
590*44704f69SBart Van Assche                 pr2serr("contents subpage_code=0x%x but reference "
591*44704f69SBart Van Assche                         "sub_page_code=0x%x\n", read_in[1], sub_pg_code);
592*44704f69SBart Van Assche                 goto fini;
593*44704f69SBart Van Assche             }
594*44704f69SBart Van Assche         } else
595*44704f69SBart Van Assche             md_len = off + read_in_len; /* force length */
596*44704f69SBart Van Assche 
597*44704f69SBart Van Assche         memcpy(ref_md + off, read_in, read_in_len);
598*44704f69SBart Van Assche         if (mode_6)
599*44704f69SBart Van Assche             res = sg_ll_mode_select6_v2(sg_fd, true /* PF */, rtd, save,
600*44704f69SBart Van Assche                                         ref_md, md_len, true, verbose);
601*44704f69SBart Van Assche         else
602*44704f69SBart Van Assche             res = sg_ll_mode_select10_v2(sg_fd, true /* PF */, rtd, save,
603*44704f69SBart Van Assche                                          ref_md, md_len, true, verbose);
604*44704f69SBart Van Assche         ret = res;
605*44704f69SBart Van Assche         if (res)
606*44704f69SBart Van Assche             goto fini;
607*44704f69SBart Van Assche     } else {
608*44704f69SBart Van Assche         printf(">>> No contents given, so show current mode page data:\n");
609*44704f69SBart Van Assche         printf("  header:\n");
610*44704f69SBart Van Assche         hex2stdout(ref_md, hdr_len, -1);
611*44704f69SBart Van Assche         if (bd_len) {
612*44704f69SBart Van Assche             printf("  block descriptor(s):\n");
613*44704f69SBart Van Assche             hex2stdout(ref_md + hdr_len, bd_len, -1);
614*44704f69SBart Van Assche         } else
615*44704f69SBart Van Assche             printf("  << no block descriptors >>\n");
616*44704f69SBart Van Assche         printf("  mode page:\n");
617*44704f69SBart Van Assche         hex2stdout(ref_md + off, md_len - off, -1);
618*44704f69SBart Van Assche     }
619*44704f69SBart Van Assche     ret = 0;
620*44704f69SBart Van Assche     goto fini;
621*44704f69SBart Van Assche 
622*44704f69SBart Van Assche revert_to_defaults:
623*44704f69SBart Van Assche     if (verbose)
624*44704f69SBart Van Assche         pr2serr("Doing MODE SELECT(%d) with revert to defaults (RTD) set "
625*44704f69SBart Van Assche                 "and SP=%d\n", mode_6 ? 6 : 10, !! save);
626*44704f69SBart Van Assche     if (mode_6)
627*44704f69SBart Van Assche         res = sg_ll_mode_select6_v2(sg_fd, false /* PF */, true /* rtd */,
628*44704f69SBart Van Assche                                     save, NULL, 0, true, verbose);
629*44704f69SBart Van Assche     else
630*44704f69SBart Van Assche         res = sg_ll_mode_select10_v2(sg_fd, false /* PF */, true /* rtd */,
631*44704f69SBart Van Assche                                      save, NULL, 0, true, verbose);
632*44704f69SBart Van Assche     ret = res;
633*44704f69SBart Van Assche fini:
634*44704f69SBart Van Assche     if (sg_fd >= 0) {
635*44704f69SBart Van Assche         res = sg_cmds_close_device(sg_fd);
636*44704f69SBart Van Assche         if (res < 0) {
637*44704f69SBart Van Assche             pr2serr("close error: %s\n", safe_strerror(-res));
638*44704f69SBart Van Assche             if (0 == ret)
639*44704f69SBart Van Assche                 ret = sg_convert_errno(-res);
640*44704f69SBart Van Assche         }
641*44704f69SBart Van Assche     }
642*44704f69SBart Van Assche     if (0 == verbose) {
643*44704f69SBart Van Assche         if (! sg_if_can2stderr("sg_wr_mode failed: ", ret))
644*44704f69SBart Van Assche             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
645*44704f69SBart Van Assche                     "more information\n");
646*44704f69SBart Van Assche     }
647*44704f69SBart Van Assche     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
648*44704f69SBart Van Assche }
649