xref: /aosp_15_r20/external/sg3_utils/src/sg_stpg.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche  * Copyright (c) 2004-2022 Hannes Reinecke, Christophe Varoqui, 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 <ctype.h>
18*44704f69SBart Van Assche #include <getopt.h>
19*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
20*44704f69SBart Van Assche 
21*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
22*44704f69SBart Van Assche #include "config.h"
23*44704f69SBart Van Assche #endif
24*44704f69SBart Van Assche #include "sg_lib.h"
25*44704f69SBart Van Assche #include "sg_cmds_basic.h"
26*44704f69SBart Van Assche #include "sg_cmds_extra.h"
27*44704f69SBart Van Assche #include "sg_unaligned.h"
28*44704f69SBart Van Assche #include "sg_pr2serr.h"
29*44704f69SBart Van Assche 
30*44704f69SBart Van Assche /* A utility program originally written for the Linux OS SCSI subsystem.
31*44704f69SBart Van Assche  *
32*44704f69SBart Van Assche  *
33*44704f69SBart Van Assche  * This program issues the SCSI command SET TARGET PORT GROUPS
34*44704f69SBart Van Assche  * to the given SCSI device.
35*44704f69SBart Van Assche  */
36*44704f69SBart Van Assche 
37*44704f69SBart Van Assche static const char * version_str = "1.21 20220118";
38*44704f69SBart Van Assche 
39*44704f69SBart Van Assche #define TGT_GRP_BUFF_LEN 1024
40*44704f69SBart Van Assche #define MX_ALLOC_LEN (0xc000 + 0x80)
41*44704f69SBart Van Assche 
42*44704f69SBart Van Assche #define TPGS_STATE_OPTIMIZED 0x0
43*44704f69SBart Van Assche #define TPGS_STATE_NONOPTIMIZED 0x1
44*44704f69SBart Van Assche #define TPGS_STATE_STANDBY 0x2
45*44704f69SBart Van Assche #define TPGS_STATE_UNAVAILABLE 0x3
46*44704f69SBart Van Assche #define TPGS_STATE_OFFLINE 0xe          /* SPC-4 rev 9 */
47*44704f69SBart Van Assche #define TPGS_STATE_TRANSITIONING 0xf
48*44704f69SBart Van Assche 
49*44704f69SBart Van Assche /* See also table 306 - Target port group descriptor format in SPC-4 rev 36e */
50*44704f69SBart Van Assche #ifdef __cplusplus
51*44704f69SBart Van Assche 
52*44704f69SBart Van Assche // C++ does not support designated initializers
53*44704f69SBart Van Assche static const uint8_t state_sup_mask[] = {
54*44704f69SBart Van Assche     0x1, 0x2, 0x4, 0x8, 0x0, 0x0, 0x0, 0x0,
55*44704f69SBart Van Assche     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x80,
56*44704f69SBart Van Assche };
57*44704f69SBart Van Assche 
58*44704f69SBart Van Assche #else
59*44704f69SBart Van Assche 
60*44704f69SBart Van Assche static const uint8_t state_sup_mask[] = {
61*44704f69SBart Van Assche         [TPGS_STATE_OPTIMIZED]     = 0x01,
62*44704f69SBart Van Assche         [TPGS_STATE_NONOPTIMIZED]  = 0x02,
63*44704f69SBart Van Assche         [TPGS_STATE_STANDBY]       = 0x04,
64*44704f69SBart Van Assche         [TPGS_STATE_UNAVAILABLE]   = 0x08,
65*44704f69SBart Van Assche         [TPGS_STATE_OFFLINE]       = 0x40,
66*44704f69SBart Van Assche         [TPGS_STATE_TRANSITIONING] = 0x80,
67*44704f69SBart Van Assche };
68*44704f69SBart Van Assche 
69*44704f69SBart Van Assche #endif  /* C or C++ ? */
70*44704f69SBart Van Assche 
71*44704f69SBart Van Assche #define VPD_DEVICE_ID  0x83
72*44704f69SBart Van Assche #define DEF_VPD_DEVICE_ID_LEN  252
73*44704f69SBart Van Assche 
74*44704f69SBart Van Assche #define MAX_PORT_LIST_ARR_LEN 16
75*44704f69SBart Van Assche 
76*44704f69SBart Van Assche struct tgtgrp {
77*44704f69SBart Van Assche         int id;
78*44704f69SBart Van Assche         int current;
79*44704f69SBart Van Assche         int valid;
80*44704f69SBart Van Assche };
81*44704f69SBart Van Assche 
82*44704f69SBart Van Assche static struct option long_options[] = {
83*44704f69SBart Van Assche         {"active", no_argument, 0, 'a'},
84*44704f69SBart Van Assche         {"help", no_argument, 0, 'h'},
85*44704f69SBart Van Assche         {"hex", no_argument, 0, 'H'},
86*44704f69SBart Van Assche         {"offline", no_argument, 0, 'l'},
87*44704f69SBart Van Assche         {"optimized", no_argument, 0, 'o'},
88*44704f69SBart Van Assche         {"raw", no_argument, 0, 'r'},
89*44704f69SBart Van Assche         {"standby", no_argument, 0, 's'},
90*44704f69SBart Van Assche         {"state", required_argument, 0, 'S'},
91*44704f69SBart Van Assche         {"tp", required_argument, 0, 't'},
92*44704f69SBart Van Assche         {"unavailable", no_argument, 0, 'u'},
93*44704f69SBart Van Assche         {"verbose", no_argument, 0, 'v'},
94*44704f69SBart Van Assche         {"version", no_argument, 0, 'V'},
95*44704f69SBart Van Assche         {0, 0, 0, 0},
96*44704f69SBart Van Assche };
97*44704f69SBart Van Assche 
98*44704f69SBart Van Assche static void
usage()99*44704f69SBart Van Assche usage()
100*44704f69SBart Van Assche {
101*44704f69SBart Van Assche     pr2serr("Usage: sg_stpg   [--active] [--help] [--hex] [--offline] "
102*44704f69SBart Van Assche             "[--optimized] [--raw]\n"
103*44704f69SBart Van Assche             "                 [--standby] [--state=S,S...] [--tp=P,P...] "
104*44704f69SBart Van Assche             "[--unavailable]\n"
105*44704f69SBart Van Assche             "                 [--verbose] [--version] DEVICE\n"
106*44704f69SBart Van Assche             "  where:\n"
107*44704f69SBart Van Assche             "    --active|-a        set asymm. access state to "
108*44704f69SBart Van Assche             "active/non-optimized\n"
109*44704f69SBart Van Assche             "    --help|-h          print out usage message\n"
110*44704f69SBart Van Assche             "    --hex|-H           print out report response in hex, then "
111*44704f69SBart Van Assche             "exit\n"
112*44704f69SBart Van Assche             "    --offline|-l|-O    set asymm. access state to offline, takes "
113*44704f69SBart Van Assche             "relative\n"
114*44704f69SBart Van Assche             "                       target port id, rather than target port "
115*44704f69SBart Van Assche             "group id\n"
116*44704f69SBart Van Assche             "    --optimized|-o     set asymm. access state to "
117*44704f69SBart Van Assche             "active/optimized\n"
118*44704f69SBart Van Assche             "    --raw|-r           output report response in binary to "
119*44704f69SBart Van Assche             "stdout, then exit\n"
120*44704f69SBart Van Assche             "    --standby|-s       set asymm. access state to standby\n"
121*44704f69SBart Van Assche             "    --state=S,S.. |-S S,S...     list of states (values or "
122*44704f69SBart Van Assche             "acronyms)\n"
123*44704f69SBart Van Assche             "    --tp=P,P.. |-t P,P...        list of target port group "
124*44704f69SBart Van Assche             "identifiers,\n"
125*44704f69SBart Van Assche             "                                 or relative target port "
126*44704f69SBart Van Assche             "identifiers\n"
127*44704f69SBart Van Assche             "    --unavailable|-u   set asymm. access state to unavailable\n"
128*44704f69SBart Van Assche             "    --verbose|-v       increase verbosity\n"
129*44704f69SBart Van Assche             "    --version|-V       print version string and exit\n\n"
130*44704f69SBart Van Assche             "Performs a SCSI SET TARGET PORT GROUPS command\n");
131*44704f69SBart Van Assche }
132*44704f69SBart Van Assche 
133*44704f69SBart Van Assche static void
dStrRaw(const uint8_t * str,int len)134*44704f69SBart Van Assche dStrRaw(const uint8_t * str, int len)
135*44704f69SBart Van Assche {
136*44704f69SBart Van Assche     int k;
137*44704f69SBart Van Assche 
138*44704f69SBart Van Assche     for (k = 0; k < len; ++k)
139*44704f69SBart Van Assche         printf("%c", str[k]);
140*44704f69SBart Van Assche }
141*44704f69SBart Van Assche 
142*44704f69SBart Van Assche static int
decode_target_port(uint8_t * buff,int len,int * d_id,int * d_tpg)143*44704f69SBart Van Assche decode_target_port(uint8_t * buff, int len, int *d_id, int *d_tpg)
144*44704f69SBart Van Assche {
145*44704f69SBart Van Assche     int c_set, assoc, desig_type, i_len, off;
146*44704f69SBart Van Assche     const uint8_t * bp;
147*44704f69SBart Van Assche     const uint8_t * ip;
148*44704f69SBart Van Assche 
149*44704f69SBart Van Assche     *d_id = -1;
150*44704f69SBart Van Assche     *d_tpg = -1;
151*44704f69SBart Van Assche     off = -1;
152*44704f69SBart Van Assche     while (sg_vpd_dev_id_iter(buff, len, &off, -1, -1, -1) == 0) {
153*44704f69SBart Van Assche         bp = buff + off;
154*44704f69SBart Van Assche         i_len = bp[3];
155*44704f69SBart Van Assche         if ((off + i_len + 4) > len) {
156*44704f69SBart Van Assche             pr2serr("    VPD page error: designator length longer than\n     "
157*44704f69SBart Van Assche                     "remaining response length=%d\n", (len - off));
158*44704f69SBart Van Assche             return SG_LIB_CAT_MALFORMED;
159*44704f69SBart Van Assche         }
160*44704f69SBart Van Assche         ip = bp + 4;
161*44704f69SBart Van Assche         c_set = (bp[0] & 0xf);
162*44704f69SBart Van Assche         /* piv = ((bp[1] & 0x80) ? 1 : 0); */
163*44704f69SBart Van Assche         assoc = ((bp[1] >> 4) & 0x3);
164*44704f69SBart Van Assche         desig_type = (bp[1] & 0xf);
165*44704f69SBart Van Assche         switch (desig_type) {
166*44704f69SBart Van Assche         case 4: /* Relative target port */
167*44704f69SBart Van Assche             if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
168*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set, target port "
169*44704f69SBart Van Assche                         "association, length 4>>\n");
170*44704f69SBart Van Assche                 hex2stderr(ip, i_len, 0);
171*44704f69SBart Van Assche                 break;
172*44704f69SBart Van Assche             }
173*44704f69SBart Van Assche             *d_id = sg_get_unaligned_be16(ip + 2);
174*44704f69SBart Van Assche             break;
175*44704f69SBart Van Assche         case 5: /* (primary) Target port group */
176*44704f69SBart Van Assche             if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
177*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set, target port "
178*44704f69SBart Van Assche                         "association, length 4>>\n");
179*44704f69SBart Van Assche                 hex2stderr(ip, i_len, 0);
180*44704f69SBart Van Assche                 break;
181*44704f69SBart Van Assche             }
182*44704f69SBart Van Assche             *d_tpg = sg_get_unaligned_be16(ip + 2);
183*44704f69SBart Van Assche             break;
184*44704f69SBart Van Assche         default:
185*44704f69SBart Van Assche             break;
186*44704f69SBart Van Assche         }
187*44704f69SBart Van Assche     }
188*44704f69SBart Van Assche     if (-1 == *d_id || -1 == *d_tpg) {
189*44704f69SBart Van Assche         pr2serr("VPD page error: no target port group information\n");
190*44704f69SBart Van Assche         return SG_LIB_CAT_MALFORMED;
191*44704f69SBart Van Assche     }
192*44704f69SBart Van Assche     return 0;
193*44704f69SBart Van Assche }
194*44704f69SBart Van Assche 
195*44704f69SBart Van Assche static void
decode_tpgs_state(const int st)196*44704f69SBart Van Assche decode_tpgs_state(const int st)
197*44704f69SBart Van Assche {
198*44704f69SBart Van Assche     switch (st) {
199*44704f69SBart Van Assche     case TPGS_STATE_OPTIMIZED:
200*44704f69SBart Van Assche         printf(" (active/optimized)");
201*44704f69SBart Van Assche         break;
202*44704f69SBart Van Assche     case TPGS_STATE_NONOPTIMIZED:
203*44704f69SBart Van Assche         printf(" (active/non optimized)");
204*44704f69SBart Van Assche         break;
205*44704f69SBart Van Assche     case TPGS_STATE_STANDBY:
206*44704f69SBart Van Assche         printf(" (standby)");
207*44704f69SBart Van Assche         break;
208*44704f69SBart Van Assche     case TPGS_STATE_UNAVAILABLE:
209*44704f69SBart Van Assche         printf(" (unavailable)");
210*44704f69SBart Van Assche         break;
211*44704f69SBart Van Assche     case TPGS_STATE_OFFLINE:
212*44704f69SBart Van Assche         printf(" (offline)");
213*44704f69SBart Van Assche         break;
214*44704f69SBart Van Assche     case TPGS_STATE_TRANSITIONING:
215*44704f69SBart Van Assche         printf(" (transitioning between states)");
216*44704f69SBart Van Assche         break;
217*44704f69SBart Van Assche     default:
218*44704f69SBart Van Assche         printf(" (unknown: 0x%x)", st);
219*44704f69SBart Van Assche         break;
220*44704f69SBart Van Assche     }
221*44704f69SBart Van Assche }
222*44704f69SBart Van Assche 
223*44704f69SBart Van Assche static int
transition_tpgs_states(struct tgtgrp * tgtState,int numgrp,int portgroup,int newstate)224*44704f69SBart Van Assche transition_tpgs_states(struct tgtgrp *tgtState, int numgrp, int portgroup,
225*44704f69SBart Van Assche                        int newstate)
226*44704f69SBart Van Assche {
227*44704f69SBart Van Assche      int i,oldstate;
228*44704f69SBart Van Assche 
229*44704f69SBart Van Assche      for ( i = 0; i < numgrp; i++) {
230*44704f69SBart Van Assche           if (tgtState[i].id == portgroup)
231*44704f69SBart Van Assche                break;
232*44704f69SBart Van Assche      }
233*44704f69SBart Van Assche      if (i == numgrp) {
234*44704f69SBart Van Assche           printf("Portgroup 0x%02x does not exist\n", portgroup);
235*44704f69SBart Van Assche           return 1;
236*44704f69SBart Van Assche      }
237*44704f69SBart Van Assche 
238*44704f69SBart Van Assche      if (!( state_sup_mask[newstate] & tgtState[i].valid )) {
239*44704f69SBart Van Assche           printf("Portgroup 0x%02x: Invalid state 0x%x\n",
240*44704f69SBart Van Assche                  portgroup, newstate);
241*44704f69SBart Van Assche           return 1;
242*44704f69SBart Van Assche      }
243*44704f69SBart Van Assche      oldstate = tgtState[i].current;
244*44704f69SBart Van Assche      tgtState[i].current = newstate;
245*44704f69SBart Van Assche      if (newstate == TPGS_STATE_OPTIMIZED) {
246*44704f69SBart Van Assche           /* Switch with current optimized path */
247*44704f69SBart Van Assche           for ( i = 0; i < numgrp; i++) {
248*44704f69SBart Van Assche                if (tgtState[i].id == portgroup)
249*44704f69SBart Van Assche                     continue;
250*44704f69SBart Van Assche                if (tgtState[i].current == TPGS_STATE_OPTIMIZED)
251*44704f69SBart Van Assche                     tgtState[i].current = oldstate;
252*44704f69SBart Van Assche           }
253*44704f69SBart Van Assche      } else if (oldstate == TPGS_STATE_OPTIMIZED) {
254*44704f69SBart Van Assche           /* Enable next path group */
255*44704f69SBart Van Assche           for ( i = 0; i < numgrp; i++) {
256*44704f69SBart Van Assche                if (tgtState[i].id == portgroup)
257*44704f69SBart Van Assche                     continue;
258*44704f69SBart Van Assche                if (tgtState[i].current == TPGS_STATE_NONOPTIMIZED) {
259*44704f69SBart Van Assche                     tgtState[i].current = TPGS_STATE_OPTIMIZED;
260*44704f69SBart Van Assche                     break;
261*44704f69SBart Van Assche                }
262*44704f69SBart Van Assche           }
263*44704f69SBart Van Assche      }
264*44704f69SBart Van Assche      printf("New target port groups:\n");
265*44704f69SBart Van Assche      for (i = 0; i < numgrp; i++) {
266*44704f69SBart Van Assche             printf("  target port group id : 0x%x\n",
267*44704f69SBart Van Assche                    tgtState[i].id);
268*44704f69SBart Van Assche             printf("    target port group asymmetric access state : ");
269*44704f69SBart Van Assche             printf("0x%02x\n", tgtState[i].current);
270*44704f69SBart Van Assche      }
271*44704f69SBart Van Assche      return 0;
272*44704f69SBart Van Assche }
273*44704f69SBart Van Assche 
274*44704f69SBart Van Assche static void
encode_tpgs_states(uint8_t * buff,struct tgtgrp * tgtState,int numgrp)275*44704f69SBart Van Assche encode_tpgs_states(uint8_t *buff, struct tgtgrp *tgtState, int numgrp)
276*44704f69SBart Van Assche {
277*44704f69SBart Van Assche      int i;
278*44704f69SBart Van Assche      uint8_t *desc;
279*44704f69SBart Van Assche 
280*44704f69SBart Van Assche      for (i = 0, desc = buff + 4; i < numgrp; desc += 4, i++) {
281*44704f69SBart Van Assche           desc[0] = tgtState[i].current & 0x0f;
282*44704f69SBart Van Assche           sg_put_unaligned_be16((uint16_t)tgtState[i].id, desc + 2);
283*44704f69SBart Van Assche      }
284*44704f69SBart Van Assche }
285*44704f69SBart Van Assche 
286*44704f69SBart Van Assche /* Read numbers (up to 32 bits in size) from command line (comma separated
287*44704f69SBart Van Assche  * list). Assumed decimal unless prefixed by '0x', '0X' or contains trailing
288*44704f69SBart Van Assche  * 'h' or 'H' (which indicate hex). Returns 0 if ok, else error code. */
289*44704f69SBart Van Assche static int
build_port_arr(const char * inp,int * port_arr,int * port_arr_len,int max_arr_len)290*44704f69SBart Van Assche build_port_arr(const char * inp, int * port_arr, int * port_arr_len,
291*44704f69SBart Van Assche                int max_arr_len)
292*44704f69SBart Van Assche {
293*44704f69SBart Van Assche     int in_len, k;
294*44704f69SBart Van Assche     const char * lcp;
295*44704f69SBart Van Assche     int v;
296*44704f69SBart Van Assche     char * cp;
297*44704f69SBart Van Assche 
298*44704f69SBart Van Assche     if ((NULL == inp) || (NULL == port_arr) ||
299*44704f69SBart Van Assche         (NULL == port_arr_len))
300*44704f69SBart Van Assche         return SG_LIB_LOGIC_ERROR;
301*44704f69SBart Van Assche     lcp = inp;
302*44704f69SBart Van Assche     in_len = strlen(inp);
303*44704f69SBart Van Assche     if (0 == in_len)
304*44704f69SBart Van Assche         *port_arr_len = 0;
305*44704f69SBart Van Assche     k = strspn(inp, "0123456789aAbBcCdDeEfFhHxX,");
306*44704f69SBart Van Assche     if (in_len != k) {
307*44704f69SBart Van Assche         pr2serr("%s: error at pos %d\n", __func__, k + 1);
308*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
309*44704f69SBart Van Assche     }
310*44704f69SBart Van Assche     for (k = 0; k < max_arr_len; ++k) {
311*44704f69SBart Van Assche         v = sg_get_num_nomult(lcp);
312*44704f69SBart Van Assche         if (-1 != v) {
313*44704f69SBart Van Assche             port_arr[k] = v;
314*44704f69SBart Van Assche             cp = (char *)strchr(lcp, ',');
315*44704f69SBart Van Assche             if (NULL == cp)
316*44704f69SBart Van Assche                 break;
317*44704f69SBart Van Assche             lcp = cp + 1;
318*44704f69SBart Van Assche         } else {
319*44704f69SBart Van Assche             pr2serr("%s: error at pos %d\n", __func__, (int)(lcp - inp + 1));
320*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
321*44704f69SBart Van Assche         }
322*44704f69SBart Van Assche     }
323*44704f69SBart Van Assche     *port_arr_len = k + 1;
324*44704f69SBart Van Assche     if (k == max_arr_len) {
325*44704f69SBart Van Assche         pr2serr("%s: array length exceeded\n", __func__);
326*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
327*44704f69SBart Van Assche     }
328*44704f69SBart Van Assche     return 0;
329*44704f69SBart Van Assche }
330*44704f69SBart Van Assche 
331*44704f69SBart Van Assche /* Read numbers (up to 32 bits in size) from command line (comma separated
332*44704f69SBart Van Assche  * list). Assumed decimal unless prefixed by '0x', '0X' or contains trailing
333*44704f69SBart Van Assche  * 'h' or 'H' (which indicate hex). Also accepts 'ao' for active optimized
334*44704f69SBart Van Assche  * [0], 'an' for active/non-optimized [1], 's' for standby [2], 'u' for
335*44704f69SBart Van Assche  * unavailable [3], 'o' for offline [14]. Returns 0 if ok, else error code. */
336*44704f69SBart Van Assche static int
build_state_arr(const char * inp,int * state_arr,int * state_arr_len,int max_arr_len)337*44704f69SBart Van Assche build_state_arr(const char * inp, int * state_arr, int * state_arr_len,
338*44704f69SBart Van Assche                 int max_arr_len)
339*44704f69SBart Van Assche {
340*44704f69SBart Van Assche     bool try_num;
341*44704f69SBart Van Assche     int in_len, k, v;
342*44704f69SBart Van Assche     const char * lcp;
343*44704f69SBart Van Assche     char * cp;
344*44704f69SBart Van Assche 
345*44704f69SBart Van Assche     if ((NULL == inp) || (NULL == state_arr) ||
346*44704f69SBart Van Assche         (NULL == state_arr_len))
347*44704f69SBart Van Assche         return SG_LIB_LOGIC_ERROR;
348*44704f69SBart Van Assche     lcp = inp;
349*44704f69SBart Van Assche     in_len = strlen(inp);
350*44704f69SBart Van Assche     if (0 == in_len)
351*44704f69SBart Van Assche         *state_arr_len = 0;
352*44704f69SBart Van Assche     k = strspn(inp, "0123456789aAbBcCdDeEfFhHnNoOsSuUxX,");
353*44704f69SBart Van Assche     if (in_len != k) {
354*44704f69SBart Van Assche         pr2serr("%s: error at pos %d\n", __func__, k + 1);
355*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
356*44704f69SBart Van Assche     }
357*44704f69SBart Van Assche     for (k = 0; k < max_arr_len; ++k) {
358*44704f69SBart Van Assche         try_num = true;
359*44704f69SBart Van Assche         if (isalpha((uint8_t)*lcp)) {
360*44704f69SBart Van Assche             try_num = false;
361*44704f69SBart Van Assche             switch (toupper((uint8_t)*lcp)) {
362*44704f69SBart Van Assche             case 'A':
363*44704f69SBart Van Assche                 if ('N' == toupper((uint8_t)*(lcp + 1)))
364*44704f69SBart Van Assche                     state_arr[k] = 1;
365*44704f69SBart Van Assche                 else if ('O' == toupper((uint8_t)*(lcp + 1)))
366*44704f69SBart Van Assche                     state_arr[k] = 0;
367*44704f69SBart Van Assche                 else
368*44704f69SBart Van Assche                     try_num = true;
369*44704f69SBart Van Assche                 break;
370*44704f69SBart Van Assche             case 'O':
371*44704f69SBart Van Assche                 state_arr[k] = 14;
372*44704f69SBart Van Assche                 break;
373*44704f69SBart Van Assche             case 'S':
374*44704f69SBart Van Assche                 state_arr[k] = 2;
375*44704f69SBart Van Assche                 break;
376*44704f69SBart Van Assche             case 'U':
377*44704f69SBart Van Assche                 state_arr[k] = 3;
378*44704f69SBart Van Assche                 break;
379*44704f69SBart Van Assche             default:
380*44704f69SBart Van Assche                 pr2serr("%s: expected 'ao', 'an', 'o', 's' or 'u' at pos "
381*44704f69SBart Van Assche                         "%d\n", __func__, (int)(lcp - inp + 1));
382*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
383*44704f69SBart Van Assche             }
384*44704f69SBart Van Assche         }
385*44704f69SBart Van Assche         if (try_num) {
386*44704f69SBart Van Assche             v = sg_get_num_nomult(lcp);
387*44704f69SBart Van Assche             if (((v >= 0) && (v <= 3)) || (14 ==v))
388*44704f69SBart Van Assche                 state_arr[k] = v;
389*44704f69SBart Van Assche             else if (-1 == v) {
390*44704f69SBart Van Assche                 pr2serr("%s: error at pos %d\n", __func__,
391*44704f69SBart Van Assche                         (int)(lcp - inp + 1));
392*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
393*44704f69SBart Van Assche             } else {
394*44704f69SBart Van Assche                 pr2serr("%s: expect 0,1,2,3 or 14\n", __func__);
395*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
396*44704f69SBart Van Assche             }
397*44704f69SBart Van Assche         }
398*44704f69SBart Van Assche         cp = (char *)strchr(lcp, ',');
399*44704f69SBart Van Assche         if (NULL == cp)
400*44704f69SBart Van Assche             break;
401*44704f69SBart Van Assche         lcp = cp + 1;
402*44704f69SBart Van Assche     }
403*44704f69SBart Van Assche     *state_arr_len = k + 1;
404*44704f69SBart Van Assche     if (k == max_arr_len) {
405*44704f69SBart Van Assche         pr2serr("%s: array length exceeded\n", __func__);
406*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
407*44704f69SBart Van Assche     }
408*44704f69SBart Van Assche     return 0;
409*44704f69SBart Van Assche }
410*44704f69SBart Van Assche 
411*44704f69SBart Van Assche 
412*44704f69SBart Van Assche int
main(int argc,char * argv[])413*44704f69SBart Van Assche main(int argc, char * argv[])
414*44704f69SBart Van Assche {
415*44704f69SBart Van Assche     bool hex = false;
416*44704f69SBart Van Assche     bool raw = false;
417*44704f69SBart Van Assche     bool verbose_given = false;
418*44704f69SBart Van Assche     bool version_given = false;
419*44704f69SBart Van Assche     int k, off, res, c, report_len, tgt_port_count;
420*44704f69SBart Van Assche     int sg_fd = -1;
421*44704f69SBart Van Assche     int port_arr_len = 0;
422*44704f69SBart Van Assche     int verbose = 0;
423*44704f69SBart Van Assche     uint8_t reportTgtGrpBuff[TGT_GRP_BUFF_LEN];
424*44704f69SBart Van Assche     uint8_t setTgtGrpBuff[TGT_GRP_BUFF_LEN];
425*44704f69SBart Van Assche     uint8_t rsp_buff[MX_ALLOC_LEN + 2];
426*44704f69SBart Van Assche     uint8_t * bp;
427*44704f69SBart Van Assche     struct tgtgrp tgtGrpState[256], *tgtStatePtr;
428*44704f69SBart Van Assche     int state = -1;
429*44704f69SBart Van Assche     const char * state_arg = NULL;
430*44704f69SBart Van Assche     const char * tp_arg = NULL;
431*44704f69SBart Van Assche     int port_arr[MAX_PORT_LIST_ARR_LEN];
432*44704f69SBart Van Assche     int state_arr[MAX_PORT_LIST_ARR_LEN];
433*44704f69SBart Van Assche     char b[80];
434*44704f69SBart Van Assche     int state_arr_len = 0;
435*44704f69SBart Van Assche     int portgroup = -1;
436*44704f69SBart Van Assche     int relport = -1;
437*44704f69SBart Van Assche     int numgrp = 0;
438*44704f69SBart Van Assche     const char * device_name = NULL;
439*44704f69SBart Van Assche     int ret = 0;
440*44704f69SBart Van Assche 
441*44704f69SBart Van Assche     while (1) {
442*44704f69SBart Van Assche         int option_index = 0;
443*44704f69SBart Van Assche 
444*44704f69SBart Van Assche         c = getopt_long(argc, argv, "ahHloOrsS:t:uvV", long_options,
445*44704f69SBart Van Assche                         &option_index);
446*44704f69SBart Van Assche         if (c == -1)
447*44704f69SBart Van Assche             break;
448*44704f69SBart Van Assche 
449*44704f69SBart Van Assche         switch (c) {
450*44704f69SBart Van Assche         case 'a':
451*44704f69SBart Van Assche             state = TPGS_STATE_NONOPTIMIZED;
452*44704f69SBart Van Assche             break;
453*44704f69SBart Van Assche         case 'h':
454*44704f69SBart Van Assche         case '?':
455*44704f69SBart Van Assche             usage();
456*44704f69SBart Van Assche             return 0;
457*44704f69SBart Van Assche         case 'H':
458*44704f69SBart Van Assche             hex = true;
459*44704f69SBart Van Assche             break;
460*44704f69SBart Van Assche         case 'l':
461*44704f69SBart Van Assche         case 'O':
462*44704f69SBart Van Assche             state = TPGS_STATE_OFFLINE;
463*44704f69SBart Van Assche             break;
464*44704f69SBart Van Assche         case 'o':
465*44704f69SBart Van Assche             state = TPGS_STATE_OPTIMIZED;
466*44704f69SBart Van Assche             break;
467*44704f69SBart Van Assche         case 'r':
468*44704f69SBart Van Assche             raw = true;
469*44704f69SBart Van Assche             break;
470*44704f69SBart Van Assche         case 's':
471*44704f69SBart Van Assche             state = TPGS_STATE_STANDBY;
472*44704f69SBart Van Assche             break;
473*44704f69SBart Van Assche         case 'S':
474*44704f69SBart Van Assche             state_arg = optarg;
475*44704f69SBart Van Assche             break;
476*44704f69SBart Van Assche         case 't':
477*44704f69SBart Van Assche             tp_arg = optarg;
478*44704f69SBart Van Assche             break;
479*44704f69SBart Van Assche         case 'u':
480*44704f69SBart Van Assche             state = TPGS_STATE_UNAVAILABLE;
481*44704f69SBart Van Assche             break;
482*44704f69SBart Van Assche         case 'v':
483*44704f69SBart Van Assche             verbose_given = true;
484*44704f69SBart Van Assche             ++verbose;
485*44704f69SBart Van Assche             break;
486*44704f69SBart Van Assche         case 'V':
487*44704f69SBart Van Assche             version_given = true;
488*44704f69SBart Van Assche             break;
489*44704f69SBart Van Assche         default:
490*44704f69SBart Van Assche             pr2serr("unrecognised option code 0x%x ??\n", c);
491*44704f69SBart Van Assche             usage();
492*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
493*44704f69SBart Van Assche         }
494*44704f69SBart Van Assche     }
495*44704f69SBart Van Assche     if (optind < argc) {
496*44704f69SBart Van Assche         if (NULL == device_name) {
497*44704f69SBart Van Assche             device_name = argv[optind];
498*44704f69SBart Van Assche             ++optind;
499*44704f69SBart Van Assche         }
500*44704f69SBart Van Assche         if (optind < argc) {
501*44704f69SBart Van Assche             for (; optind < argc; ++optind)
502*44704f69SBart Van Assche                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
503*44704f69SBart Van Assche             usage();
504*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
505*44704f69SBart Van Assche         }
506*44704f69SBart Van Assche     }
507*44704f69SBart Van Assche #ifdef DEBUG
508*44704f69SBart Van Assche     pr2serr("In DEBUG mode, ");
509*44704f69SBart Van Assche     if (verbose_given && version_given) {
510*44704f69SBart Van Assche         pr2serr("but override: '-vV' given, zero verbose and continue\n");
511*44704f69SBart Van Assche         verbose_given = false;
512*44704f69SBart Van Assche         version_given = false;
513*44704f69SBart Van Assche         verbose = 0;
514*44704f69SBart Van Assche     } else if (! verbose_given) {
515*44704f69SBart Van Assche         pr2serr("set '-vv'\n");
516*44704f69SBart Van Assche         verbose = 2;
517*44704f69SBart Van Assche     } else
518*44704f69SBart Van Assche         pr2serr("keep verbose=%d\n", verbose);
519*44704f69SBart Van Assche #else
520*44704f69SBart Van Assche     if (verbose_given && version_given)
521*44704f69SBart Van Assche         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
522*44704f69SBart Van Assche #endif
523*44704f69SBart Van Assche     if (version_given) {
524*44704f69SBart Van Assche         pr2serr("Version: %s\n", version_str);
525*44704f69SBart Van Assche         return 0;
526*44704f69SBart Van Assche     }
527*44704f69SBart Van Assche 
528*44704f69SBart Van Assche     if (state_arg) {
529*44704f69SBart Van Assche         if ((ret = build_state_arr(state_arg, state_arr, &state_arr_len,
530*44704f69SBart Van Assche                                    MAX_PORT_LIST_ARR_LEN))) {
531*44704f69SBart Van Assche             usage();
532*44704f69SBart Van Assche             return ret;
533*44704f69SBart Van Assche         }
534*44704f69SBart Van Assche     }
535*44704f69SBart Van Assche     if (tp_arg) {
536*44704f69SBart Van Assche         if ((ret = build_port_arr(tp_arg, port_arr, &port_arr_len,
537*44704f69SBart Van Assche                                   MAX_PORT_LIST_ARR_LEN))) {
538*44704f69SBart Van Assche             usage();
539*44704f69SBart Van Assche             return ret;
540*44704f69SBart Van Assche         }
541*44704f69SBart Van Assche     }
542*44704f69SBart Van Assche     if ((state >= 0) && (state_arr_len > 0)) {
543*44704f69SBart Van Assche         pr2serr("either use individual state option or '--state=' but not "
544*44704f69SBart Van Assche                 "both\n");
545*44704f69SBart Van Assche         usage();
546*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
547*44704f69SBart Van Assche     }
548*44704f69SBart Van Assche     if ((0 == state_arr_len) && (0 == port_arr_len) && (-1 == state))
549*44704f69SBart Van Assche         state = 0;      /* default to active/optimized */
550*44704f69SBart Van Assche     if ((1 == state_arr_len) && (0 == port_arr_len) && (-1 == state)) {
551*44704f69SBart Van Assche         state = state_arr[0];
552*44704f69SBart Van Assche         state_arr_len = 0;
553*44704f69SBart Van Assche     }
554*44704f69SBart Van Assche     if (state_arr_len > port_arr_len) {
555*44704f69SBart Van Assche         pr2serr("'state=' list longer than expected\n");
556*44704f69SBart Van Assche         usage();
557*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
558*44704f69SBart Van Assche     }
559*44704f69SBart Van Assche     if ((port_arr_len > 0) && (0 == state_arr_len)) {
560*44704f69SBart Van Assche         if (-1 == state) {
561*44704f69SBart Van Assche             pr2serr("target port list given but no state indicated\n");
562*44704f69SBart Van Assche             usage();
563*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
564*44704f69SBart Van Assche         }
565*44704f69SBart Van Assche         state_arr[0] = state;
566*44704f69SBart Van Assche         state_arr_len = 1;
567*44704f69SBart Van Assche         state = -1;
568*44704f69SBart Van Assche     }
569*44704f69SBart Van Assche     if ((port_arr_len > 1) && (1 == state_arr_len)) {
570*44704f69SBart Van Assche         for (k = 1; k < port_arr_len; ++k)
571*44704f69SBart Van Assche             state_arr[k] = state_arr[0];
572*44704f69SBart Van Assche         state_arr_len = port_arr_len;
573*44704f69SBart Van Assche     }
574*44704f69SBart Van Assche     if (port_arr_len != state_arr_len) {
575*44704f69SBart Van Assche         pr2serr("'state=' and '--tp=' lists mismatched\n");
576*44704f69SBart Van Assche         usage();
577*44704f69SBart Van Assche         return SG_LIB_CONTRADICT;
578*44704f69SBart Van Assche     }
579*44704f69SBart Van Assche 
580*44704f69SBart Van Assche     if (NULL == device_name) {
581*44704f69SBart Van Assche         pr2serr("missing device name!\n");
582*44704f69SBart Van Assche         usage();
583*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
584*44704f69SBart Van Assche     }
585*44704f69SBart Van Assche     sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose);
586*44704f69SBart Van Assche     if (sg_fd < 0) {
587*44704f69SBart Van Assche         if (verbose)
588*44704f69SBart Van Assche             pr2serr("open error: %s: %s\n", device_name,
589*44704f69SBart Van Assche                     safe_strerror(-sg_fd));
590*44704f69SBart Van Assche         ret = sg_convert_errno(-sg_fd);
591*44704f69SBart Van Assche         goto err_out;
592*44704f69SBart Van Assche     }
593*44704f69SBart Van Assche 
594*44704f69SBart Van Assche     if (0 == port_arr_len) {
595*44704f69SBart Van Assche         res = sg_ll_inquiry(sg_fd, false, true /* EVPD */, VPD_DEVICE_ID,
596*44704f69SBart Van Assche                             rsp_buff, DEF_VPD_DEVICE_ID_LEN, true, verbose);
597*44704f69SBart Van Assche         if (0 == res) {
598*44704f69SBart Van Assche             report_len = sg_get_unaligned_be16(rsp_buff + 2) + 4;
599*44704f69SBart Van Assche             if (VPD_DEVICE_ID != rsp_buff[1]) {
600*44704f69SBart Van Assche                 pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
601*44704f69SBart Van Assche                         "response\n");
602*44704f69SBart Van Assche                 if (verbose) {
603*44704f69SBart Van Assche                     pr2serr("First 32 bytes of bad response\n");
604*44704f69SBart Van Assche                     hex2stderr(rsp_buff, 32, 0);
605*44704f69SBart Van Assche                 }
606*44704f69SBart Van Assche                 return SG_LIB_CAT_MALFORMED;
607*44704f69SBart Van Assche             }
608*44704f69SBart Van Assche             if (report_len > MX_ALLOC_LEN) {
609*44704f69SBart Van Assche                 pr2serr("response length too long: %d > %d\n", report_len,
610*44704f69SBart Van Assche                         MX_ALLOC_LEN);
611*44704f69SBart Van Assche                 return SG_LIB_CAT_MALFORMED;
612*44704f69SBart Van Assche             } else if (report_len > DEF_VPD_DEVICE_ID_LEN) {
613*44704f69SBart Van Assche                 if (sg_ll_inquiry(sg_fd, false, true, VPD_DEVICE_ID, rsp_buff,
614*44704f69SBart Van Assche                                   report_len, true, verbose))
615*44704f69SBart Van Assche                     return SG_LIB_CAT_OTHER;
616*44704f69SBart Van Assche             }
617*44704f69SBart Van Assche             decode_target_port(rsp_buff + 4, report_len - 4, &relport,
618*44704f69SBart Van Assche                                &portgroup);
619*44704f69SBart Van Assche             printf("Device is at port Group 0x%02x, relative port 0x%02x\n",
620*44704f69SBart Van Assche                    portgroup, relport);
621*44704f69SBart Van Assche         }
622*44704f69SBart Van Assche 
623*44704f69SBart Van Assche         memset(reportTgtGrpBuff, 0x0, sizeof(reportTgtGrpBuff));
624*44704f69SBart Van Assche         /* trunc = 0; */
625*44704f69SBart Van Assche 
626*44704f69SBart Van Assche         res = sg_ll_report_tgt_prt_grp2(sg_fd, reportTgtGrpBuff,
627*44704f69SBart Van Assche                                         sizeof(reportTgtGrpBuff),
628*44704f69SBart Van Assche                                         false /* extended */, true, verbose);
629*44704f69SBart Van Assche         ret = res;
630*44704f69SBart Van Assche         if (0 == res) {
631*44704f69SBart Van Assche             report_len = sg_get_unaligned_be32(reportTgtGrpBuff + 0) + 4;
632*44704f69SBart Van Assche             if (report_len > (int)sizeof(reportTgtGrpBuff)) {
633*44704f69SBart Van Assche                 /* trunc = 1; */
634*44704f69SBart Van Assche                 pr2serr("  <<report too long for internal buffer, output "
635*44704f69SBart Van Assche                         "truncated\n");
636*44704f69SBart Van Assche                 report_len = (int)sizeof(reportTgtGrpBuff);
637*44704f69SBart Van Assche             }
638*44704f69SBart Van Assche             if (raw) {
639*44704f69SBart Van Assche                 dStrRaw(reportTgtGrpBuff, report_len);
640*44704f69SBart Van Assche                 goto err_out;
641*44704f69SBart Van Assche             }
642*44704f69SBart Van Assche             if (verbose)
643*44704f69SBart Van Assche                 printf("Report list length = %d\n", report_len);
644*44704f69SBart Van Assche             if (hex) {
645*44704f69SBart Van Assche                 if (verbose)
646*44704f69SBart Van Assche                     printf("\nOutput response in hex:\n");
647*44704f69SBart Van Assche                 hex2stdout(reportTgtGrpBuff, report_len, 1);
648*44704f69SBart Van Assche                 goto err_out;
649*44704f69SBart Van Assche             }
650*44704f69SBart Van Assche             memset(tgtGrpState, 0, sizeof(struct tgtgrp) * 256);
651*44704f69SBart Van Assche             tgtStatePtr = tgtGrpState;
652*44704f69SBart Van Assche             printf("Current target port groups:\n");
653*44704f69SBart Van Assche             for (k = 4, bp = reportTgtGrpBuff + 4, numgrp = 0; k < report_len;
654*44704f69SBart Van Assche                  k += off, bp += off, numgrp ++) {
655*44704f69SBart Van Assche 
656*44704f69SBart Van Assche                 printf("  target port group id : 0x%x , Pref=%d\n",
657*44704f69SBart Van Assche                        sg_get_unaligned_be16(bp + 2), !!(bp[0] & 0x80));
658*44704f69SBart Van Assche                 printf("    target port group asymmetric access state : ");
659*44704f69SBart Van Assche                 printf("0x%02x", bp[0] & 0x0f);
660*44704f69SBart Van Assche                 printf("\n");
661*44704f69SBart Van Assche                 tgtStatePtr->id = sg_get_unaligned_be16(bp + 2);
662*44704f69SBart Van Assche                 tgtStatePtr->current = bp[0] & 0x0f;
663*44704f69SBart Van Assche                 tgtStatePtr->valid = bp[1];
664*44704f69SBart Van Assche 
665*44704f69SBart Van Assche                 tgt_port_count = bp[7];
666*44704f69SBart Van Assche 
667*44704f69SBart Van Assche                 tgtStatePtr++;
668*44704f69SBart Van Assche                 off = 8 + tgt_port_count * 4;
669*44704f69SBart Van Assche             }
670*44704f69SBart Van Assche         } else {
671*44704f69SBart Van Assche             sg_get_category_sense_str(res, sizeof(b), b, verbose);
672*44704f69SBart Van Assche             pr2serr("Report Target Port Groups: %s\n", b);
673*44704f69SBart Van Assche             if (0 == verbose)
674*44704f69SBart Van Assche                 pr2serr("    try '-v' for more information\n");
675*44704f69SBart Van Assche         }
676*44704f69SBart Van Assche         if (0 != res)
677*44704f69SBart Van Assche              goto err_out;
678*44704f69SBart Van Assche 
679*44704f69SBart Van Assche         printf("Port group 0x%02x: Set asymmetric access state to", portgroup);
680*44704f69SBart Van Assche         decode_tpgs_state(state);
681*44704f69SBart Van Assche         printf("\n");
682*44704f69SBart Van Assche 
683*44704f69SBart Van Assche         transition_tpgs_states(tgtGrpState, numgrp, portgroup, state);
684*44704f69SBart Van Assche 
685*44704f69SBart Van Assche         memset(setTgtGrpBuff, 0x0, sizeof(setTgtGrpBuff));
686*44704f69SBart Van Assche         /* trunc = 0; */
687*44704f69SBart Van Assche 
688*44704f69SBart Van Assche         encode_tpgs_states(setTgtGrpBuff, tgtGrpState, numgrp);
689*44704f69SBart Van Assche         report_len = numgrp * 4 + 4;
690*44704f69SBart Van Assche     } else { /* port_arr_len > 0 */
691*44704f69SBart Van Assche         memset(setTgtGrpBuff, 0x0, sizeof(setTgtGrpBuff));
692*44704f69SBart Van Assche         for (k = 0, bp = setTgtGrpBuff + 4; k < port_arr_len; ++k, bp +=4) {
693*44704f69SBart Van Assche             bp[0] = state_arr[k] & 0xf;
694*44704f69SBart Van Assche             sg_put_unaligned_be16((uint16_t)port_arr[k], bp + 2);
695*44704f69SBart Van Assche         }
696*44704f69SBart Van Assche         report_len = port_arr_len * 4 + 4;
697*44704f69SBart Van Assche     }
698*44704f69SBart Van Assche 
699*44704f69SBart Van Assche     res = sg_ll_set_tgt_prt_grp(sg_fd, setTgtGrpBuff, report_len, true,
700*44704f69SBart Van Assche                                 verbose);
701*44704f69SBart Van Assche 
702*44704f69SBart Van Assche     if (0 == res)
703*44704f69SBart Van Assche         goto err_out;
704*44704f69SBart Van Assche     else {
705*44704f69SBart Van Assche         sg_get_category_sense_str(res, sizeof(b), b, verbose);
706*44704f69SBart Van Assche         pr2serr("Set Target Port Groups: %s\n", b);
707*44704f69SBart Van Assche         if (0 == verbose)
708*44704f69SBart Van Assche             pr2serr("    try '-v' for more information\n");
709*44704f69SBart Van Assche     }
710*44704f69SBart Van Assche 
711*44704f69SBart Van Assche err_out:
712*44704f69SBart Van Assche     if (sg_fd >= 0) {
713*44704f69SBart Van Assche         res = sg_cmds_close_device(sg_fd);
714*44704f69SBart Van Assche         if (res < 0) {
715*44704f69SBart Van Assche             pr2serr("close error: %s\n", safe_strerror(-res));
716*44704f69SBart Van Assche             if (0 == ret)
717*44704f69SBart Van Assche                 ret = sg_convert_errno(-res);
718*44704f69SBart Van Assche         }
719*44704f69SBart Van Assche     }
720*44704f69SBart Van Assche     if (0 == verbose) {
721*44704f69SBart Van Assche         if (! sg_if_can2stderr("sg_stpg failed: ", ret))
722*44704f69SBart Van Assche             pr2serr("Some error occurred, try again with '-v' "
723*44704f69SBart Van Assche                     "or '-vv' for more information\n");
724*44704f69SBart Van Assche     }
725*44704f69SBart Van Assche     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
726*44704f69SBart Van Assche }
727