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