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