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