• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2004-2022 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 <errno.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <getopt.h>
22 #define __STDC_FORMAT_MACROS 1
23 #include <inttypes.h>
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "sg_lib.h"
30 #include "sg_cmds_basic.h"
31 #include "sg_cmds_extra.h"
32 #include "sg_unaligned.h"
33 #include "sg_pt.h"
34 #include "sg_pr2serr.h"
35 
36 /*
37  * This program issues SCSI SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS
38  * commands tailored for SES (enclosure) devices.
39  */
40 
41 static const char * version_str = "2.58 20220813";    /* ses4r04 */
42 
43 #define MX_ALLOC_LEN ((64 * 1024) - 4)  /* max allowable for big enclosures */
44 #define MX_ELEM_HDR 1024
45 #define REQUEST_SENSE_RESP_SZ 252
46 #define DATA_IN_OFF 4
47 #define MIN_MAXLEN 16
48 #define MIN_DATA_IN_SZ 8192     /* use max(MIN_DATA_IN_SZ, op->maxlen) for
49                                  * the size of data_arr */
50 #define MX_DATA_IN_LINES (16 * 1024)
51 #define MX_JOIN_ROWS 520        /* element index fields in dpages are only 8
52                                  * bit, and index 0xff (255) is sometimes used
53                                  * for 'not applicable'. However this limit
54                                  * can bypassed with sub-enclosure numbers.
55                                  * So try higher figure. */
56 #define MX_DATA_IN_DESCS 32
57 #define NUM_ACTIVE_ET_AESP_ARR 32
58 
59 #define TEMPERAT_OFF 20         /* 8 bits represents -19 C to +235 C */
60                                 /* value of 0 (would imply -20 C) reserved */
61 
62 /* Send Diagnostic and Receive Diagnostic Results page codes */
63 /* Sometimes referred to as "dpage"s in code comments */
64 #define SUPPORTED_DPC 0x0
65 #define CONFIGURATION_DPC 0x1
66 #define ENC_CONTROL_DPC 0x2
67 #define ENC_STATUS_DPC 0x2
68 #define HELP_TEXT_DPC 0x3
69 #define STRING_DPC 0x4
70 #define THRESHOLD_DPC 0x5
71 #define ARRAY_CONTROL_DPC 0x6   /* obsolete, last seen ses-r08b.pdf */
72 #define ARRAY_STATUS_DPC 0x6    /* obsolete */
73 #define ELEM_DESC_DPC 0x7
74 #define SHORT_ENC_STATUS_DPC 0x8
75 #define ENC_BUSY_DPC 0x9
76 #define ADD_ELEM_STATUS_DPC 0xa /* Additional Element Status dpage code */
77 #define SUBENC_HELP_TEXT_DPC 0xb
78 #define SUBENC_STRING_DPC 0xc
79 #define SUPPORTED_SES_DPC 0xd   /* should be 0x1 <= dpc <= 0x2f */
80 #define DOWNLOAD_MICROCODE_DPC 0xe
81 #define SUBENC_NICKNAME_DPC 0xf
82 #define ALL_DPC 0xff
83 
84 /* Element Type codes */
85 #define UNSPECIFIED_ETC 0x0
86 #define DEVICE_ETC 0x1
87 #define POWER_SUPPLY_ETC 0x2
88 #define COOLING_ETC 0x3
89 #define TEMPERATURE_ETC 0x4
90 #define DOOR_ETC 0x5    /* prior to ses3r05 was DOOR_LOCK_ETC */
91 #define AUD_ALARM_ETC 0x6
92 #define ENC_SCELECTR_ETC 0x7 /* Enclosure services controller electronics */
93 #define SCC_CELECTR_ETC 0x8  /* SCC: SCSI Controller Commands (e.g. RAID
94                               * controller). SCC Controller Elecronics */
95 #define NV_CACHE_ETC 0x9
96 #define INV_OP_REASON_ETC 0xa
97 #define UI_POWER_SUPPLY_ETC 0xb
98 #define DISPLAY_ETC 0xc
99 #define KEY_PAD_ETC 0xd
100 #define ENCLOSURE_ETC 0xe
101 #define SCSI_PORT_TRAN_ETC 0xf
102 #define LANGUAGE_ETC 0x10
103 #define COMM_PORT_ETC 0x11
104 #define VOLT_SENSOR_ETC 0x12
105 #define CURR_SENSOR_ETC 0x13
106 #define SCSI_TPORT_ETC 0x14
107 #define SCSI_IPORT_ETC 0x15
108 #define SIMPLE_SUBENC_ETC 0x16
109 #define ARRAY_DEV_ETC 0x17
110 #define SAS_EXPANDER_ETC 0x18
111 #define SAS_CONNECTOR_ETC 0x19
112 #define LAST_ETC SAS_CONNECTOR_ETC      /* adjust as necessary */
113 
114 #define TPROTO_PCIE_PS_NVME 1   /* NVMe regarded as subset of PCIe */
115 #define NUM_ETC (LAST_ETC + 1)
116 
117 #define DEF_CLEAR_VAL 0
118 #define DEF_SET_VAL 1
119 
120 
121 struct element_type_t {
122     int elem_type_code;
123     const char * abbrev;
124     const char * desc;
125 };
126 
127 #define CGS_CL_ARR_MAX_SZ 8
128 #define CGS_STR_MAX_SZ 80
129 
130 enum cgs_select_t {CLEAR_OPT, GET_OPT, SET_OPT};
131 
132 struct cgs_cl_t {
133     enum cgs_select_t cgs_sel;
134     bool last_cs;       /* true only for last --clear= or --set= */
135     char cgs_str[CGS_STR_MAX_SZ];
136 };
137 
138 struct opts_t {
139     bool byte1_given;   /* true if -b B1 or --byte1=B1 given */
140     bool do_control;    /* want to write to DEVICE */
141     bool do_data;       /* flag if --data= option has been used */
142     bool do_list;
143     bool do_status;     /* want to read from DEVICE (or user data) */
144     bool eiioe_auto;    /* Element Index Includes Overall (status) Element */
145     bool eiioe_force;
146     bool ind_given;     /* '--index=...' or '-I ...' */
147     bool inner_hex;
148     bool many_dpages;   /* user supplied data has more than one dpage */
149     bool mask_ign;      /* element read-mask-modify-write actions */
150     bool o_readonly;
151     bool page_code_given;       /* or suitable abbreviation */
152     bool quiet;         /* exit status unaltered by --quiet */
153     bool seid_given;
154     bool verbose_given;
155     bool version_given;
156     bool warn;
157     int byte1;          /* (origin 0 so second byte) in Control dpage */
158     int dev_slot_num;
159     int do_filter;
160     int do_help;
161     int do_hex;
162     int do_join;        /* relational join of Enclosure status, Element
163                            descriptor and Additional element status dpages.
164                            Use twice to add Threshold in dpage to join. */
165     int do_raw;
166     int enumerate;
167     int ind_th;    /* type header index, set by build_type_desc_hdr_arr() */
168     int ind_indiv;      /* individual element index; -1 for overall */
169     int ind_indiv_last; /* if > ind_indiv then [ind_indiv..ind_indiv_last] */
170     int ind_et_inst;    /* ETs can have multiple type header instances */
171     int maxlen;
172     int seid;
173     int page_code;      /* recognised abbreviations converted to dpage num */
174     int verbose;
175     int num_cgs;        /* number of --clear-, --get= and --set= options */
176     int mx_arr_len;     /* allocated size of data_arr */
177     int arr_len;        /* valid bytes in data_arr */
178     uint8_t * data_arr;
179     uint8_t * free_data_arr;
180     const char * desc_name;
181     const char * dev_name;
182     const struct element_type_t * ind_etp;
183     const char * index_str;
184     const char * nickname_str;
185     struct cgs_cl_t cgs_cl_arr[CGS_CL_ARR_MAX_SZ];
186     uint8_t sas_addr[8];  /* Big endian byte sequence */
187 };
188 
189 struct diag_page_code {
190     int page_code;
191     const char * desc;
192 };
193 
194 struct diag_page_abbrev {
195     const char * abbrev;
196     int page_code;
197 };
198 
199 /* The Configuration diagnostic page contains one or more of these. The
200  * elements of the Enclosure Control/Status and Threshold In/ Out page follow
201  * this format. The additional element status page is closely related to
202  * this format (with some element types and all overall elements excluded). */
203 struct type_desc_hdr_t {
204     uint8_t etype;              /* element type code (0: unspecified) */
205     uint8_t num_elements;       /* number of possible elements, excluding
206                                  * overall element */
207     uint8_t se_id;              /* subenclosure id (0 for primary enclosure) */
208     uint8_t txt_len;            /* type descriptor text length; (unused) */
209 };
210 
211 /* A SQL-like join of the Enclosure Status, Threshold In and Additional
212  * Element Status pages based of the format indicated in the Configuration
213  * page. Note that the array of these struct instances is built such that
214  * the array index is equal to the 'ei_ioe' (element index that includes
215  * overall elements). */
216 struct join_row_t {  /* this struct is 72 bytes long on Intel "64" bit arch */
217     int th_i;           /* type header index (origin 0) */
218     int indiv_i;        /* individual (element) index, -1 for overall
219                          * instance, otherwise origin 0 */
220     uint8_t etype;      /* element type */
221     uint8_t se_id;      /* subenclosure id (0 for primary enclosure) */
222     int ei_eoe;         /* element index referring to Enclosure status dpage
223                          * descriptors, origin 0 and excludes overall
224                          * elements, -1 for not applicable. As defined by
225                          * SES-2 standard for the AES descriptor, EIP=1 */
226     int ei_aess;        /* subset of ei_eoe that only includes elements of
227                          * these types:  excludes DEVICE_ETC, ARRAY_DEV_ETC,
228                          * SAS_EXPANDER_ETC, SCSI_IPORT_ETC, SCSI_TPORT_ETC
229                          * and ENC_SCELECTR_ETC. -1 for not applicable */
230     /* following point into Element Descriptor, Enclosure Status, Threshold
231      * In and Additional element status diagnostic pages. enc_statp only
232      * NULL beyond last, other pointers can be NULL . */
233     const uint8_t * elem_descp;
234     uint8_t * enc_statp;  /* NULL indicates past last */
235     uint8_t * thresh_inp;
236     const uint8_t * ae_statp;
237     int dev_slot_num;           /* if not available, set to -1 */
238     uint8_t sas_addr[8];  /* big endian, if not available, set to 0 */
239 };
240 
241 enum fj_select_t {FJ_IOE, FJ_EOE, FJ_AESS, FJ_SAS_CON};
242 
243 /* Instance ('tes' in main() ) holds a type_desc_hdr_t array potentially with
244    the matching join array if present. */
245 struct th_es_t {
246     const struct type_desc_hdr_t * th_base;
247     int num_ths;        /* items in array pointed to by th_base */
248     struct join_row_t * j_base;
249     int num_j_rows;
250     int num_j_eoe;
251 };
252 
253 /* Representation of <acronym>[=<value>] or
254  * <start_byte>:<start_bit>[:<num_bits>][=<value>]. Associated with
255  * --clear=, --get= or --set= option. */
256 struct tuple_acronym_val {
257     const char * acron;
258     const char * val_str;
259     enum cgs_select_t cgs_sel;  /* indicates --clear=, --get= or --set= */
260     int start_byte;     /* -1 indicates no start_byte */
261     int start_bit;
262     int num_bits;
263     int64_t val;
264 };
265 
266 /* Mapping from <acronym> to <start_byte>:<start_bit>:<num_bits> for a
267  * given element type. Table of known acronyms made from these elements. */
268 struct acronym2tuple {
269     const char * acron; /* element name or acronym, NULL for past end */
270     int etype;          /* -1 for all element types */
271     int start_byte;     /* origin 0, normally 0 to 3 */
272     int start_bit;      /* 7 (MSbit or leftmost in SES drafts) to 0 (LSbit) */
273     int num_bits;       /* usually 1, maximum is 64 */
274     const char * info;  /* optional, set to NULL if not used */
275 };
276 
277 /* Structure for holding (sub-)enclosure information found in the
278  * Configuration diagnostic page. */
279 struct enclosure_info {
280     int have_info;
281     int rel_esp_id;     /* relative enclosure services process id (origin 1) */
282     int num_esp;        /* number of enclosure services processes */
283     uint8_t enc_log_id[8];        /* 8 byte NAA */
284     uint8_t enc_vendor_id[8];     /* may differ from INQUIRY response */
285     uint8_t product_id[16];       /* may differ from INQUIRY response */
286     uint8_t product_rev_level[4]; /* may differ from INQUIRY response */
287 };
288 
289 /* When --status is given with --data= the file contents may contain more
290  * than one dpage to be decoded. */
291 struct data_in_desc_t {
292     bool in_use;
293     int page_code;
294     int offset;         /* byte offset from op->data_arr + DATA_IN_OFF */
295     int dp_len;         /* byte length of this diagnostic page */
296 };
297 
298 
299 /* Join array has four "element index"ing strategies:
300  *   [1] based on all descriptors in the Enclosure Status (ES) dpage
301  *   [2] based on the non-overall descriptors in the ES dpage
302  *   [3] based on the non-overall descriptors of these element types
303  *       in the ES dpage: DEVICE_ETC, ARRAY_DEV_ETC, SAS_EXPANDER_ETC,
304  *       SCSI_IPORT_ETC, SCSI_TPORT_ETC and ENC_SCELECTR_ETC.
305  *   [4] based on the non-overall descriptors of the SAS_CONNECTOR_ETC
306  *       element type
307  *
308  * The indexes are all origin 0 with the maximum index being one less then
309  * the number of status descriptors in the ES dpage. Table of supported
310  * permutations follows:
311  *
312  *  ==========|===============================================================
313  *  Algorithm |              Indexes                  |    Notes
314  *            |Element|Connector element|Other element|
315  *  ==========|=======|=================|=============|=======================
316  *   [A]      |  [2]  |       [4]       |    [3]      | SES-2, OR
317  *   [A]      |  [2]  |       [4]       |    [3]      | SES-3,EIIOE=0
318  *  ----------|-------|-----------------|-------------|-----------------------
319  *   [B]      |  [1]  |       [1]       |    [1]      | SES-3, EIIOE=1
320  *  ----------|-------|-----------------|-------------|-----------------------
321  *   [C]      |  [2]  |       [2]       |    [2]      | SES-3, EIIOE=2
322  *  ----------|-------|-----------------|-------------|-----------------------
323  *   [D]      |  [2]  |       [1]       |    [1]      | SES-3, EIIOE=3
324  *  ----------|-------|-----------------|-------------|-----------------------
325  *   [E]      |  [1]  |       [4]       |    [3]      | EIIOE=0 and
326  *            |       |                 |             | --eiioe=force, OR
327  *   [E]      |  [1]  |       [4]       |    [3]      | {HP JBOD} EIIOE=0 and
328  *            |       |                 |             | --eiioe=auto and
329  *            |       |                 |             | AES[desc_0].ei==1 .
330  *  ----------|-------|-----------------|-------------|-----------------------
331  *   [F]      | [2->3]|       [4]       |    [3]      | "broken_ei" when any
332  *            |       |                 |             | of AES[*].ei invalid
333  *            |       |                 |             | using strategy [2]
334  *  ----------|-------|-----------------|-------------|-----------------------
335  *   [Z]      |  -    |       [4]       |    [3]      | EIP=0, implicit
336  *            |       |                 |             | element index of [3]
337  *  ==========================================================================
338  *
339  *
340  */
341 static struct join_row_t join_arr[MX_JOIN_ROWS];
342 static struct join_row_t * join_arr_lastp = join_arr + MX_JOIN_ROWS - 1;
343 static bool join_done = false;
344 
345 static struct type_desc_hdr_t type_desc_hdr_arr[MX_ELEM_HDR];
346 static int type_desc_hdr_count = 0;
347 static uint8_t * config_dp_resp = NULL;
348 static uint8_t * free_config_dp_resp = NULL;
349 static int config_dp_resp_len;
350 
351 static struct data_in_desc_t data_in_desc_arr[MX_DATA_IN_DESCS];
352 
353 /* Large buffers on heap, aligned to page size and zeroed */
354 static uint8_t * enc_stat_rsp;
355 static uint8_t * elem_desc_rsp;
356 static uint8_t * add_elem_rsp;
357 static uint8_t * threshold_rsp;
358 
359 static unsigned enc_stat_rsp_sz;
360 static unsigned elem_desc_rsp_sz;
361 static unsigned add_elem_rsp_sz;
362 static unsigned threshold_rsp_sz;
363 
364 static int enc_stat_rsp_len;
365 static int elem_desc_rsp_len;
366 static int add_elem_rsp_len;
367 static int threshold_rsp_len;
368 
369 
370 /* Diagnostic page names, control and/or status (in and/or out) */
371 static struct diag_page_code dpc_arr[] = {
372     {SUPPORTED_DPC, "Supported Diagnostic Pages"},  /* 0 */
373     {CONFIGURATION_DPC, "Configuration (SES)"},
374     {ENC_STATUS_DPC, "Enclosure Status/Control (SES)"},
375     {HELP_TEXT_DPC, "Help Text (SES)"},
376     {STRING_DPC, "String In/Out (SES)"},
377     {THRESHOLD_DPC, "Threshold In/Out (SES)"},
378     {ARRAY_STATUS_DPC, "Array Status/Control (SES, obsolete)"},
379     {ELEM_DESC_DPC, "Element Descriptor (SES)"},
380     {SHORT_ENC_STATUS_DPC, "Short Enclosure Status (SES)"},  /* 8 */
381     {ENC_BUSY_DPC, "Enclosure Busy (SES-2)"},
382     {ADD_ELEM_STATUS_DPC, "Additional Element Status (SES-2)"},
383     {SUBENC_HELP_TEXT_DPC, "Subenclosure Help Text (SES-2)"},
384     {SUBENC_STRING_DPC, "Subenclosure String In/Out (SES-2)"},
385     {SUPPORTED_SES_DPC, "Supported SES Diagnostic Pages (SES-2)"},
386     {DOWNLOAD_MICROCODE_DPC, "Download Microcode (SES-2)"},
387     {SUBENC_NICKNAME_DPC, "Subenclosure Nickname (SES-2)"},
388     {0x3f, "Protocol Specific (SAS transport)"},
389     {0x40, "Translate Address (SBC)"},
390     {0x41, "Device Status (SBC)"},
391     {0x42, "Rebuild Assist (SBC)"},     /* sbc3r31 */
392     {ALL_DPC, "All SES diagnostic pages output (sg_ses)"},
393     {-1, NULL},
394 };
395 
396 /* Diagnostic page names, for status (or in) pages */
397 static struct diag_page_code in_dpc_arr[] = {
398     {SUPPORTED_DPC, "Supported Diagnostic Pages"},  /* 0 */
399     {CONFIGURATION_DPC, "Configuration (SES)"},
400     {ENC_STATUS_DPC, "Enclosure Status (SES)"},
401     {HELP_TEXT_DPC, "Help Text (SES)"},
402     {STRING_DPC, "String In (SES)"},
403     {THRESHOLD_DPC, "Threshold In (SES)"},
404     {ARRAY_STATUS_DPC, "Array Status (SES, obsolete)"},
405     {ELEM_DESC_DPC, "Element Descriptor (SES)"},
406     {SHORT_ENC_STATUS_DPC, "Short Enclosure Status (SES)"},  /* 8 */
407     {ENC_BUSY_DPC, "Enclosure Busy (SES-2)"},
408     {ADD_ELEM_STATUS_DPC, "Additional Element Status (SES-2)"},
409     {SUBENC_HELP_TEXT_DPC, "Subenclosure Help Text (SES-2)"},
410     {SUBENC_STRING_DPC, "Subenclosure String In (SES-2)"},
411     {SUPPORTED_SES_DPC, "Supported SES Diagnostic Pages (SES-2)"},
412     {DOWNLOAD_MICROCODE_DPC, "Download Microcode (SES-2)"},
413     {SUBENC_NICKNAME_DPC, "Subenclosure Nickname (SES-2)"},
414     {0x3f, "Protocol Specific (SAS transport)"},
415     {0x40, "Translate Address (SBC)"},
416     {0x41, "Device Status (SBC)"},
417     {0x42, "Rebuild Assist Input (SBC)"},
418     {-1, NULL},
419 };
420 
421 /* Diagnostic page names, for control (or out) pages */
422 static struct diag_page_code out_dpc_arr[] = {
423     {SUPPORTED_DPC, "?? [Supported Diagnostic Pages]"},  /* 0 */
424     {CONFIGURATION_DPC, "?? [Configuration (SES)]"},
425     {ENC_CONTROL_DPC, "Enclosure Control (SES)"},
426     {HELP_TEXT_DPC, "Help Text (SES)"},
427     {STRING_DPC, "String Out (SES)"},
428     {THRESHOLD_DPC, "Threshold Out (SES)"},
429     {ARRAY_CONTROL_DPC, "Array Control (SES, obsolete)"},
430     {ELEM_DESC_DPC, "?? [Element Descriptor (SES)]"},
431     {SHORT_ENC_STATUS_DPC, "?? [Short Enclosure Status (SES)]"},  /* 8 */
432     {ENC_BUSY_DPC, "?? [Enclosure Busy (SES-2)]"},
433     {ADD_ELEM_STATUS_DPC, "?? [Additional Element Status (SES-2)]"},
434     {SUBENC_HELP_TEXT_DPC, "?? [Subenclosure Help Text (SES-2)]"},
435     {SUBENC_STRING_DPC, "Subenclosure String Out (SES-2)"},
436     {SUPPORTED_SES_DPC, "?? [Supported SES Diagnostic Pages (SES-2)]"},
437     {DOWNLOAD_MICROCODE_DPC, "Download Microcode (SES-2)"},
438     {SUBENC_NICKNAME_DPC, "Subenclosure Nickname (SES-2)"},
439     {0x3f, "Protocol Specific (SAS transport)"},
440     {0x40, "Translate Address (SBC)"},
441     {0x41, "Device Status (SBC)"},
442     {0x42, "Rebuild Assist Output (SBC)"},
443     {-1, NULL},
444 };
445 
446 static struct diag_page_abbrev dp_abbrev[] = {
447     {"ac", ARRAY_CONTROL_DPC},
448     {"aes", ADD_ELEM_STATUS_DPC},
449     {"all", ALL_DPC},
450     {"as", ARRAY_STATUS_DPC},
451     {"cf", CONFIGURATION_DPC},
452     {"dm", DOWNLOAD_MICROCODE_DPC},
453     {"eb", ENC_BUSY_DPC},
454     {"ec", ENC_CONTROL_DPC},
455     {"ed", ELEM_DESC_DPC},
456     {"es", ENC_STATUS_DPC},
457     {"ht", HELP_TEXT_DPC},
458     {"sdp", SUPPORTED_DPC},
459     {"ses", SHORT_ENC_STATUS_DPC},
460     {"sht", SUBENC_HELP_TEXT_DPC},
461     {"snic", SUBENC_NICKNAME_DPC},
462     {"ssp", SUPPORTED_SES_DPC},
463     {"sstr", SUBENC_STRING_DPC},
464     {"str", STRING_DPC},
465     {"th", THRESHOLD_DPC},
466     {NULL, -999},
467 };
468 
469 /* Names of element types used by the Enclosure Control/Status diagnostic
470  * page. */
471 static struct element_type_t element_type_arr[] = {
472     {UNSPECIFIED_ETC, "un", "Unspecified"},
473     {DEVICE_ETC, "dev", "Device slot"},
474     {POWER_SUPPLY_ETC, "ps", "Power supply"},
475     {COOLING_ETC, "coo", "Cooling"},
476     {TEMPERATURE_ETC, "ts", "Temperature sensor"},
477     {DOOR_ETC, "do", "Door"},   /* prior to ses3r05 was 'dl' (for Door Lock)
478                                    but the "Lock" has been dropped */
479     {AUD_ALARM_ETC, "aa", "Audible alarm"},
480     {ENC_SCELECTR_ETC, "esc", "Enclosure services controller electronics"},
481     {SCC_CELECTR_ETC, "sce", "SCC controller electronics"},
482     {NV_CACHE_ETC, "nc", "Nonvolatile cache"},
483     {INV_OP_REASON_ETC, "ior", "Invalid operation reason"},
484     {UI_POWER_SUPPLY_ETC, "ups", "Uninterruptible power supply"},
485     {DISPLAY_ETC, "dis", "Display"},
486     {KEY_PAD_ETC, "kpe", "Key pad entry"},
487     {ENCLOSURE_ETC, "enc", "Enclosure"},
488     {SCSI_PORT_TRAN_ETC, "sp", "SCSI port/transceiver"},
489     {LANGUAGE_ETC, "lan", "Language"},
490     {COMM_PORT_ETC, "cp", "Communication port"},
491     {VOLT_SENSOR_ETC, "vs", "Voltage sensor"},
492     {CURR_SENSOR_ETC, "cs", "Current sensor"},
493     {SCSI_TPORT_ETC, "stp", "SCSI target port"},
494     {SCSI_IPORT_ETC, "sip", "SCSI initiator port"},
495     {SIMPLE_SUBENC_ETC, "ss", "Simple subenclosure"},
496     {ARRAY_DEV_ETC, "arr", "Array device slot"},
497     {SAS_EXPANDER_ETC, "sse", "SAS expander"},
498     {SAS_CONNECTOR_ETC, "ssc", "SAS connector"},
499     {-1, NULL, NULL},
500 };
501 
502 static struct element_type_t element_type_by_code =
503     {0, NULL, "element type code form"};
504 
505 /* Many control element names below have "RQST" in front in drafts.
506    These are for the Enclosure Control/Status diagnostic page */
507 static struct acronym2tuple ecs_a2t_arr[] = {
508     /* acron   element_type  start_byte  start_bit  num_bits */
509     {"ac_fail", UI_POWER_SUPPLY_ETC, 2, 4, 1, NULL},
510     {"ac_hi", UI_POWER_SUPPLY_ETC, 2, 6, 1, NULL},
511     {"ac_lo", UI_POWER_SUPPLY_ETC, 2, 7, 1, NULL},
512     {"ac_qual", UI_POWER_SUPPLY_ETC, 2, 5, 1, NULL},
513     {"active", DEVICE_ETC, 2, 7, 1, NULL},     /* for control only */
514     {"active", ARRAY_DEV_ETC, 2, 7, 1, NULL},  /* for control only */
515     {"batt_fail", UI_POWER_SUPPLY_ETC, 3, 1, 1, NULL},
516     {"bpf", UI_POWER_SUPPLY_ETC, 3, 0, 1, NULL},
517     {"bypa", DEVICE_ETC, 3, 3, 1, "bypass port A"},
518     {"bypa", ARRAY_DEV_ETC, 3, 3, 1, "bypass port A"},
519     {"bypb", DEVICE_ETC, 3, 2, 1, "bypass port B"},
520     {"bypb", ARRAY_DEV_ETC, 3, 2, 1, "bypass port B"},
521     {"conscheck", ARRAY_DEV_ETC, 1, 4, 1, "consistency check"},
522     {"ctr_link", SAS_CONNECTOR_ETC, 2, 7, 8, "connector physical link"},
523     {"ctr_type", SAS_CONNECTOR_ETC, 1, 6, 7, "connector type"},
524     {"current", CURR_SENSOR_ETC, 2, 7, 16, "current in centiamps"},
525     {"dc_fail", UI_POWER_SUPPLY_ETC, 2, 3, 1, NULL},
526     {"disable", -1, 0, 5, 1, NULL},        /* -1 is for all element types */
527     {"disable_elm", SCSI_PORT_TRAN_ETC, 3, 4, 1, "disable port/transceiver"},
528     {"disable_elm", COMM_PORT_ETC, 3, 0, 1, "disable communication port"},
529     {"devoff", DEVICE_ETC, 3, 4, 1, NULL},     /* device off */
530     {"devoff", ARRAY_DEV_ETC, 3, 4, 1, NULL},
531     {"disp_mode", DISPLAY_ETC, 1, 1, 2, NULL},
532     {"disp_char", DISPLAY_ETC, 2, 7, 16, NULL},
533     {"dnr", ARRAY_DEV_ETC, 2, 6, 1, "do not remove"},
534     {"dnr", COOLING_ETC, 1, 6, 1, "do not remove"},
535     {"dnr", DEVICE_ETC, 2, 6, 1, "do not remove"},
536     {"dnr", ENC_SCELECTR_ETC, 1, 5, 1, "do not remove"},
537     {"dnr", POWER_SUPPLY_ETC, 1, 6, 1, "do not remove"},
538     {"dnr", UI_POWER_SUPPLY_ETC, 3, 3, 1, "do not remove"},
539     {"enable", SCSI_IPORT_ETC, 3, 0, 1, NULL},
540     {"enable", SCSI_TPORT_ETC, 3, 0, 1, NULL},
541     {"fail", AUD_ALARM_ETC, 1, 6, 1, NULL},
542     {"fail", COMM_PORT_ETC, 1, 7, 1, NULL},
543     {"fail", COOLING_ETC, 3, 6, 1, NULL},
544     {"fail", CURR_SENSOR_ETC, 3, 6, 1, NULL},
545     {"fail", DISPLAY_ETC, 1, 6, 1, NULL},
546     {"fail", DOOR_ETC, 1, 6, 1, NULL},
547     {"fail", ENC_SCELECTR_ETC, 1, 6, 1, NULL},
548     {"fail", KEY_PAD_ETC, 1, 6, 1, NULL},
549     {"fail", NV_CACHE_ETC, 3, 6, 1, NULL},
550     {"fail", POWER_SUPPLY_ETC, 3, 6, 1, NULL},
551     {"fail", SAS_CONNECTOR_ETC, 3, 6, 1, NULL},
552     {"fail", SAS_EXPANDER_ETC, 1, 6, 1, NULL},
553     {"fail", SCC_CELECTR_ETC, 3, 6, 1, NULL},
554     {"fail", SCSI_IPORT_ETC, 1, 6, 1, NULL},
555     {"fail", SCSI_PORT_TRAN_ETC, 1, 6, 1, NULL},
556     {"fail", SCSI_TPORT_ETC, 1, 6, 1, NULL},
557     {"fail", SIMPLE_SUBENC_ETC, 1, 6, 1, NULL},
558     {"fail", TEMPERATURE_ETC, 3, 6, 1, NULL},
559     {"fail", UI_POWER_SUPPLY_ETC, 3, 6, 1, NULL},
560     {"fail", VOLT_SENSOR_ETC, 1, 6, 1, NULL},
561     {"failure_ind", ENCLOSURE_ETC, 2, 1, 1, NULL},
562     {"failure", ENCLOSURE_ETC, 3, 1, 1, NULL},
563     {"fault", DEVICE_ETC, 3, 5, 1, NULL},
564     {"fault", ARRAY_DEV_ETC, 3, 5, 1, NULL},
565     {"hotspare", ARRAY_DEV_ETC, 1, 5, 1, NULL},
566     {"hotswap", COOLING_ETC, 3, 7, 1, NULL},
567     {"hotswap", ENC_SCELECTR_ETC, 3, 7, 1, NULL},       /* status only */
568     {"hw_reset", ENC_SCELECTR_ETC, 1, 2, 1, "hardware reset"}, /* 18-047r1 */
569     {"ident", DEVICE_ETC, 2, 1, 1, "flash LED"},
570     {"ident", ARRAY_DEV_ETC, 2, 1, 1, "flash LED"},
571     {"ident", POWER_SUPPLY_ETC, 1, 7, 1, "flash LED"},
572     {"ident", COMM_PORT_ETC, 1, 7, 1, "flash LED"},
573     {"ident", COOLING_ETC, 1, 7, 1, "flash LED"},
574     {"ident", CURR_SENSOR_ETC, 1, 7, 1, "flash LED"},
575     {"ident", DISPLAY_ETC, 1, 7, 1, "flash LED"},
576     {"ident", DOOR_ETC, 1, 7, 1, "flash LED"},
577     {"ident", ENC_SCELECTR_ETC, 1, 7, 1, "flash LED"},
578     {"ident", ENCLOSURE_ETC, 1, 7, 1, "flash LED"},
579     {"ident", KEY_PAD_ETC, 1, 7, 1, "flash LED"},
580     {"ident", LANGUAGE_ETC, 1, 7, 1, "flash LED"},
581     {"ident", AUD_ALARM_ETC, 1, 7, 1, NULL},
582     {"ident", NV_CACHE_ETC, 1, 7, 1, "flash LED"},
583     {"ident", SAS_CONNECTOR_ETC, 1, 7, 1, "flash LED"},
584     {"ident", SAS_EXPANDER_ETC, 1, 7, 1, "flash LED"},
585     {"ident", SCC_CELECTR_ETC, 1, 7, 1, "flash LED"},
586     {"ident", SCSI_IPORT_ETC, 1, 7, 1, "flash LED"},
587     {"ident", SCSI_PORT_TRAN_ETC, 1, 7, 1, "flash LED"},
588     {"ident", SCSI_TPORT_ETC, 1, 7, 1, "flash LED"},
589     {"ident", SIMPLE_SUBENC_ETC, 1, 7, 1, "flash LED"},
590     {"ident", TEMPERATURE_ETC, 1, 7, 1, "flash LED"},
591     {"ident", UI_POWER_SUPPLY_ETC, 3, 7, 1, "flash LED"},
592     {"ident", VOLT_SENSOR_ETC, 1, 7, 1, "flash LED"},
593     {"incritarray", ARRAY_DEV_ETC, 1, 3, 1, NULL},
594     {"infailedarray", ARRAY_DEV_ETC, 1, 2, 1, NULL},
595     {"info", AUD_ALARM_ETC, 3, 3, 1, "emits warning tone when set"},
596     {"insert", DEVICE_ETC, 2, 3, 1, NULL},
597     {"insert", ARRAY_DEV_ETC, 2, 3, 1, NULL},
598     {"intf_fail", UI_POWER_SUPPLY_ETC, 2, 0, 1, NULL},
599     {"language", LANGUAGE_ETC, 2, 7, 16, "language code"},
600     {"locate", DEVICE_ETC, 2, 1, 1, "flash LED"},
601     {"locate", ARRAY_DEV_ETC, 2, 1, 1, "flash LED"},
602     {"locate", POWER_SUPPLY_ETC, 1, 7, 1, "flash LED"},
603     {"locate", COMM_PORT_ETC, 1, 7, 1, "flash LED"},
604     {"locate", COOLING_ETC, 1, 7, 1, "flash LED"},
605     {"locate", CURR_SENSOR_ETC, 1, 7, 1, "flash LED"},
606     {"locate", DISPLAY_ETC, 1, 7, 1, "flash LED"},
607     {"locate", DOOR_ETC, 1, 7, 1, "flash LED"},
608     {"locate", ENC_SCELECTR_ETC, 1, 7, 1, "flash LED"},
609     {"locate", ENCLOSURE_ETC, 1, 7, 1, "flash LED"},
610     {"locate", KEY_PAD_ETC, 1, 7, 1, "flash LED"},
611     {"locate", LANGUAGE_ETC, 1, 7, 1, "flash LED"},
612     {"locate", AUD_ALARM_ETC, 1, 7, 1, NULL},
613     {"locate", NV_CACHE_ETC, 1, 7, 1, "flash LED"},
614     {"locate", SAS_CONNECTOR_ETC, 1, 7, 1, "flash LED"},
615     {"locate", SAS_EXPANDER_ETC, 1, 7, 1, "flash LED"},
616     {"locate", SCC_CELECTR_ETC, 1, 7, 1, "flash LED"},
617     {"locate", SCSI_IPORT_ETC, 1, 7, 1, "flash LED"},
618     {"locate", SCSI_PORT_TRAN_ETC, 1, 7, 1, "flash LED"},
619     {"locate", SCSI_TPORT_ETC, 1, 7, 1, "flash LED"},
620     {"locate", SIMPLE_SUBENC_ETC, 1, 7, 1, "flash LED"},
621     {"locate", TEMPERATURE_ETC, 1, 7, 1, "flash LED"},
622     {"locate", UI_POWER_SUPPLY_ETC, 3, 7, 1, "flash LED"},
623     {"locate", VOLT_SENSOR_ETC, 1, 7, 1, "flash LED"},
624     {"lol", SCSI_PORT_TRAN_ETC, 3, 1, 1, "Loss of Link"},
625     {"mated", SAS_CONNECTOR_ETC, 3, 7, 1, NULL},
626     {"missing", DEVICE_ETC, 2, 4, 1, NULL},
627     {"missing", ARRAY_DEV_ETC, 2, 4, 1, NULL},
628     {"mute", AUD_ALARM_ETC, 3, 6, 1, "control only: mute the alarm"},
629     {"muted", AUD_ALARM_ETC, 3, 6, 1, "status only: alarm is muted"},
630     {"off", POWER_SUPPLY_ETC, 3, 4, 1, "Not providing power"},
631     {"off", COOLING_ETC, 3, 4, 1, "Not providing cooling"},
632     {"offset_temp", TEMPERATURE_ETC, 1, 5, 6, "Offset for reference "
633      "temperature"},
634     {"ok", ARRAY_DEV_ETC, 1, 7, 1, NULL},
635     {"on", COOLING_ETC, 3, 5, 1, NULL},
636     {"on", POWER_SUPPLY_ETC, 3, 5, 1, "0: turn (remain) off; 1: turn on"},
637     {"open", DOOR_ETC, 3, 1, 1, NULL},
638     {"overcurrent", CURR_SENSOR_ETC, 1, 1, 1, "overcurrent"},
639     {"overcurrent", POWER_SUPPLY_ETC, 2, 1, 1, "DC overcurrent"},
640     {"overcurrent", SAS_CONNECTOR_ETC, 3, 5, 1, NULL},  /* added ses3r07 */
641     {"overcurrent_warn", CURR_SENSOR_ETC, 1, 3, 1, "overcurrent warning"},
642     {"overtemp_fail", TEMPERATURE_ETC, 3, 3, 1, "Overtemperature failure"},
643     {"overtemp_warn", TEMPERATURE_ETC, 3, 2, 1, "Overtemperature warning"},
644     {"overvoltage", POWER_SUPPLY_ETC, 2, 3, 1, "DC overvoltage"},
645     {"overvoltage", VOLT_SENSOR_ETC, 1, 1, 1, "overvoltage"},
646     {"overvoltage_warn", POWER_SUPPLY_ETC, 1, 3, 1, "DC overvoltage warning"},
647     {"pow_cycle", ENCLOSURE_ETC, 2, 7, 2,
648      "0: no; 1: start in pow_c_delay minutes; 2: cancel"},
649     {"pow_c_delay", ENCLOSURE_ETC, 2, 5, 6,
650      "delay in minutes before starting power cycle (max: 60)"},
651     {"pow_c_duration", ENCLOSURE_ETC, 3, 7, 6,
652      "0: power off, restore within 1 minute; <=60: restore within that many "
653      "minutes; 63: power off, wait for manual power on"},
654      /* slightly different in Enclosure status element */
655     {"pow_c_time", ENCLOSURE_ETC, 2, 7, 6,
656      "time in minutes remaining until starting power cycle; 0: not "
657      "scheduled; <=60: scheduled in that many minutes; 63: in zero minutes"},
658     {"prdfail", -1, 0, 6, 1, "predict failure"},
659     {"rebuildremap", ARRAY_DEV_ETC, 1, 1, 1, NULL},
660     {"remove", DEVICE_ETC, 2, 2, 1, NULL},
661     {"remove", ARRAY_DEV_ETC, 2, 2, 1, NULL},
662     {"remind", AUD_ALARM_ETC, 3, 4, 1, NULL},
663     {"report", ENC_SCELECTR_ETC, 2, 0, 1, NULL},        /* status only */
664     {"report", SCC_CELECTR_ETC, 2, 0, 1, NULL},
665     {"report", SCSI_IPORT_ETC, 2, 0, 1, NULL},
666     {"report", SCSI_TPORT_ETC, 2, 0, 1, NULL},
667     {"rqst_mute", AUD_ALARM_ETC, 3, 7, 1,
668      "status only: alarm was manually muted"},
669     {"rqst_override", TEMPERATURE_ETC, 3, 7, 1, "Request(ed) override"},
670     {"rrabort", ARRAY_DEV_ETC, 1, 0, 1, "rebuild/remap abort"},
671     {"rsvddevice", ARRAY_DEV_ETC, 1, 6, 1, "reserved device"},
672     {"select_element", ENC_SCELECTR_ETC, 2, 0, 1, NULL},        /* control */
673     {"short_stat", SIMPLE_SUBENC_ETC, 3, 7, 8, "short enclosure status"},
674     {"size", NV_CACHE_ETC, 2, 7, 16, NULL},
675     {"speed_act", COOLING_ETC, 1, 2, 11, "actual speed (rpm / 10)"},
676     {"speed_code", COOLING_ETC, 3, 2, 3,
677      "0: leave; 1: lowest... 7: highest"},
678     {"size_mult", NV_CACHE_ETC, 1, 1, 2, NULL},
679     {"swap", -1, 0, 4, 1, NULL},               /* Reset swap */
680     {"sw_reset", ENC_SCELECTR_ETC, 1, 3, 1, "software reset"},/* 18-047r1 */
681     {"temp", TEMPERATURE_ETC, 2, 7, 8, "(Requested) temperature"},
682     {"unlock", DOOR_ETC, 3, 0, 1, NULL},
683     {"undertemp_fail", TEMPERATURE_ETC, 3, 1, 1, "Undertemperature failure"},
684     {"undertemp_warn", TEMPERATURE_ETC, 3, 0, 1, "Undertemperature warning"},
685     {"undervoltage", POWER_SUPPLY_ETC, 2, 2, 1, "DC undervoltage"},
686     {"undervoltage", VOLT_SENSOR_ETC, 1, 0, 1, "undervoltage"},
687     {"undervoltage_warn", POWER_SUPPLY_ETC, 1, 2, 1,
688      "DC undervoltage warning"},
689     {"ups_fail", UI_POWER_SUPPLY_ETC, 2, 2, 1, NULL},
690     {"urgency", AUD_ALARM_ETC, 3, 3, 4, NULL},  /* Tone urgency control bits */
691     {"voltage", VOLT_SENSOR_ETC, 2, 7, 16, "voltage in centivolts"},
692     {"warning", UI_POWER_SUPPLY_ETC, 2, 1, 1, NULL},
693     {"warning", ENCLOSURE_ETC, 3, 0, 1, NULL},
694     {"warning_ind", ENCLOSURE_ETC, 2, 0, 1, NULL},
695     {"xmit_fail", SCSI_PORT_TRAN_ETC, 3, 0, 1, "Transmitter failure"},
696     {NULL, 0, 0, 0, 0, NULL},
697 };
698 
699 /* These are for the Threshold in/out diagnostic page */
700 static struct acronym2tuple th_a2t_arr[] = {
701     {"high_crit", -1, 0, 7, 8, NULL},
702     {"high_warn", -1, 1, 7, 8, NULL},
703     {"low_crit", -1, 2, 7, 8, NULL},
704     {"low_warn", -1, 3, 7, 8, NULL},
705     {NULL, 0, 0, 0, 0, NULL},
706 };
707 
708 /* These are for the Additional element status diagnostic page for SAS with
709  * the EIP bit set. First phy only. Index from start of AES descriptor */
710 static struct acronym2tuple ae_sas_a2t_arr[] = {
711     {"at_sas_addr", -1, 12, 7, 64, NULL},  /* best viewed with --hex --get= */
712         /* typically this is the expander's SAS address */
713     {"dev_type", -1, 8, 6, 3, "1: SAS/SATA dev, 2: expander"},
714     {"dsn", -1, 7, 7, 8, "device slot number (255: none)"},
715     {"num_phys", -1, 4, 7, 8, "number of phys"},
716     {"phy_id", -1, 28, 7, 8, NULL},
717     {"sas_addr", -1, 20, 7, 64, NULL},  /* should be disk or tape ... */
718     {"exp_sas_addr", -1, 8, 7, 64, NULL},  /* expander address */
719     {"sata_dev", -1, 11, 0, 1, NULL},
720     {"sata_port_sel", -1, 11, 7, 1, NULL},
721     {"smp_init", -1, 10, 1, 1, NULL},
722     {"smp_targ", -1, 11, 1, 1, NULL},
723     {"ssp_init", -1, 10, 3, 1, NULL},
724     {"ssp_targ", -1, 11, 3, 1, NULL},
725     {"stp_init", -1, 10, 2, 1, NULL},
726     {"stp_targ", -1, 11, 2, 1, NULL},
727     {NULL, 0, 0, 0, 0, NULL},
728 };
729 
730 /* Boolean array of element types of interest to the Additional Element
731  * Status page. Indexed by element type (0 <= et < 32). */
732 static bool active_et_aesp_arr[NUM_ACTIVE_ET_AESP_ARR] = {
733     false, true /* dev */, false, false,
734     false, false, false, true /* esce */,
735     false, false, false, false,
736     false, false, false, false,
737     false, false, false, false,
738     true /* starg */, true /* sinit */, false, true /* arr */,
739     true /* sas exp */, false, false, false,
740     false, false, false, false,
741 };
742 
743 /* Command line long option names with corresponding short letter. */
744 static struct option long_options[] = {
745     {"all", no_argument, 0, 'a'},
746     {"ALL", no_argument, 0, 'z'},
747     {"byte1", required_argument, 0, 'b'},
748     {"clear", required_argument, 0, 'C'},
749     {"control", no_argument, 0, 'c'},
750     {"data", required_argument, 0, 'd'},
751     {"descriptor", required_argument, 0, 'D'},
752     {"dev-slot-num", required_argument, 0, 'x'},
753     {"dev_slot_num", required_argument, 0, 'x'},
754     {"dsn", required_argument, 0, 'x'},
755     {"eiioe", required_argument, 0, 'E'},
756     {"enumerate", no_argument, 0, 'e'},
757     {"filter", no_argument, 0, 'f'},
758     {"get", required_argument, 0, 'G'},
759     {"help", no_argument, 0, 'h'},
760     {"hex", no_argument, 0, 'H'},
761     {"index", required_argument, 0, 'I'},
762     {"inhex", required_argument, 0, 'X'},
763     {"inner-hex", no_argument, 0, 'i'},
764     {"inner_hex", no_argument, 0, 'i'},
765     {"join", no_argument, 0, 'j'},
766     {"list", no_argument, 0, 'l'},
767     {"nickid", required_argument, 0, 'N'},
768     {"nickname", required_argument, 0, 'n'},
769     {"mask", required_argument, 0, 'M'},
770     {"maxlen", required_argument, 0, 'm'},
771     {"page", required_argument, 0, 'p'},
772     {"quiet", no_argument, 0, 'q'},
773     {"raw", no_argument, 0, 'r'},
774     {"readonly", no_argument, 0, 'R'},
775     {"sas-addr", required_argument, 0, 'A'},
776     {"sas_addr", required_argument, 0, 'A'},
777     {"set", required_argument, 0, 'S'},
778     {"status", no_argument, 0, 's'},
779     {"verbose", no_argument, 0, 'v'},
780     {"version", no_argument, 0, 'V'},
781     {"warn", no_argument, 0, 'w'},
782     {0, 0, 0, 0},
783 };
784 
785 /* For overzealous SES device servers that don't like some status elements
786  * sent back as control elements. This table is as per ses3r06. */
787 static uint8_t ses3_element_cmask_arr[NUM_ETC][4] = {
788                                 /* Element type code (ETC) names; comment */
789     {0x40, 0xff, 0xff, 0xff},   /* [0] unspecified */
790     {0x40, 0, 0x4e, 0x3c},      /* DEVICE */
791     {0x40, 0x80, 0, 0x60},      /* POWER_SUPPLY */
792     {0x40, 0x80, 0, 0x60},      /* COOLING; requested speed as is unless */
793     {0x40, 0xc0, 0, 0},         /* TEMPERATURE */
794     {0x40, 0xc0, 0, 0x1},       /* DOOR */
795     {0x40, 0xc0, 0, 0x5f},      /* AUD_ALARM */
796     {0x40, 0xc0, 0x1, 0},       /* ENC_SCELECTR_ETC */
797     {0x40, 0xc0, 0, 0},         /* SCC_CELECTR */
798     {0x40, 0xc0, 0, 0},         /* NV_CACHE */
799     {0x40, 0, 0, 0},            /* [10] INV_OP_REASON */
800     {0x40, 0, 0, 0xc0},         /* UI_POWER_SUPPLY */
801     {0x40, 0xc0, 0xff, 0xff},   /* DISPLAY */
802     {0x40, 0xc3, 0, 0},         /* KEY_PAD */
803     {0x40, 0x80, 0, 0xff},      /* ENCLOSURE */
804     {0x40, 0xc0, 0, 0x10},      /* SCSI_PORT_TRAN */
805     {0x40, 0x80, 0xff, 0xff},   /* LANGUAGE */
806     {0x40, 0xc0, 0, 0x1},       /* COMM_PORT */
807     {0x40, 0xc0, 0, 0},         /* VOLT_SENSOR */
808     {0x40, 0xc0, 0, 0},         /* CURR_SENSOR */
809     {0x40, 0xc0, 0, 0x1},       /* [20] SCSI_TPORT */
810     {0x40, 0xc0, 0, 0x1},       /* SCSI_IPORT */
811     {0x40, 0xc0, 0, 0},         /* SIMPLE_SUBENC */
812     {0x40, 0xff, 0x4e, 0x3c},   /* ARRAY */
813     {0x40, 0xc0, 0, 0},         /* SAS_EXPANDER */
814     {0x40, 0x80, 0, 0x40},      /* SAS_CONNECTOR */
815 };
816 
817 
818 static int read_hex(const char * inp, uint8_t * arr, int mx_arr_len,
819                     int * arr_len, bool in_hex, bool may_gave_at, int verb);
820 static int strcase_eq(const char * s1p, const char * s2p);
821 static void enumerate_diag_pages(void);
822 static bool saddr_non_zero(const uint8_t * bp);
823 static const char * find_in_diag_page_desc(int page_num);
824 
825 
826 static void
usage(int help_num)827 usage(int help_num)
828 {
829     if (2 != help_num) {
830         pr2serr(
831             "Usage: sg_ses [--all] [--ALL] [--descriptor=DES] "
832             "[--dev-slot-num=SN]\n"
833             "              [--eiioe=A_F] [--filter] [--get=STR] "
834             "[--hex]\n"
835             "              [--index=IIA | =TIA,II] [--inner-hex] [--join] "
836             "[--maxlen=LEN]\n"
837             "              [--page=PG] [--quiet] [--raw] [--readonly] "
838             "[--sas-addr=SA]\n"
839             "              [--status] [--verbose] [--warn] DEVICE\n\n"
840             "       sg_ses --control [--byte1=B1] [--clear=STR] "
841             "[--data=H,H...]\n"
842             "              [--descriptor=DES] [--dev-slot-num=SN] "
843             "[--index=IIA | =TIA,II]\n"
844             "              [--inhex=FN] [--mask] [--maxlen=LEN] "
845             "[--nickid=SEID]\n"
846             "              [--nickname=SEN] [--page=PG] [--sas-addr=SA] "
847             "[--set=STR]\n"
848             "              [--verbose] DEVICE\n\n"
849             "       sg_ses --data=@FN --status [-rr] [<most options from "
850             "first form>]\n"
851             "       sg_ses --inhex=FN --status [-rr] [<most options from "
852             "first form>]\n\n"
853             "       sg_ses [--enumerate] [--help] [--index=IIA] [--list] "
854             "[--version]\n\n"
855                );
856         if ((help_num < 1) || (help_num > 2)) {
857             pr2serr("Or the corresponding short option usage: \n"
858                     "  sg_ses [-a] [-D DES] [-x SN] [-E A_F] [-f] [-G STR] "
859                     "[-H] [-I IIA|TIA,II]\n"
860                     "         [-i] [-j] [-m LEN] [-p PG] [-q] [-r] [-R] "
861                     "[-A SA] [-s] [-v] [-w]\n"
862                     "         DEVICE\n\n"
863                     "  sg_ses [-b B1] [-C STR] [-c] [-d H,H...] [-D DES] "
864                     "[-x SN] [-I IIA|TIA,II]\n"
865                     "         [-M] [-m LEN] [-N SEID] [-n SEN] [-p PG] "
866                     "[-A SA] [-S STR]\n"
867                     "         [-v] DEVICE\n\n"
868                     "  sg_ses -d @FN -s [-rr] [<most options from first "
869                     "form>]\n"
870                     "  sg_ses -X FN -s [-rr] [<most options from first "
871                     "form>]\n\n"
872                     "  sg_ses [-e] [-h] [-I IIA] [-l] [-V]\n"
873                    );
874             pr2serr("\nFor help use '-h' one or more times.\n");
875             return;
876         }
877         pr2serr(
878             "  where the main options are:\n"
879             "    --all|-a            show (almost) all status pages (same "
880             "as --join)\n"
881             "    --clear=STR|-C STR    clear field by acronym or position\n"
882             "    --control|-c        send control information (def: fetch "
883             "status)\n"
884             "    --descriptor=DES|-D DES    descriptor name (for indexing)\n"
885             "    --dev-slot-num=SN|--dsn=SN|-x SN    device slot number "
886             "(for indexing)\n"
887             "    --filter|-f         filter out enclosure status flags that "
888             "are clear\n"
889             "                        use twice for status=okay entries "
890             "only\n"
891             "    --get=STR|-G STR    get value of field by acronym or "
892             "position\n"
893             "    --help|-h           print out usage message, use twice for "
894             "additional\n"
895             "    --index=IIA|-I IIA    individual index ('-1' for overall) "
896             "or element\n"
897             "                          type abbreviation (e.g. 'arr'). A "
898             "range may be\n"
899             "                          given for the individual index "
900             "(e.g. '2-5')\n"
901             "    --index=TIA,II|-I TIA,II    comma separated pair: TIA is "
902             "type header\n"
903             "                                index or element type "
904             "abbreviation;\n"
905             "                                II is individual index ('-1' "
906             "for overall)\n"
907             );
908         pr2serr(
909             "    --join|-j           group Enclosure Status, Element "
910             "Descriptor\n"
911             "                        and Additional Element Status pages. "
912             "Use twice\n"
913             "                        to add Threshold In page\n"
914             "    --page=PG|-p PG     diagnostic page code (abbreviation "
915             "or number)\n"
916             "                        (def: 'ssp' [0x0] (supported diagnostic "
917             "pages))\n"
918             "    --sas-addr=SA|-A SA    SAS address in hex (for indexing)\n"
919             "    --set=STR|-S STR    set value of field by acronym or "
920             "position\n"
921             "    --status|-s         fetch status information (default "
922             "action)\n\n"
923             "First usage above is for fetching pages or fields from a SCSI "
924             "enclosure.\nThe second usage is for changing a page or field in "
925             "an enclosure. The\n'--clear=', '--get=' and '--set=' options "
926             "can appear multiple times.\nUse '-hh' for more help, including "
927             "the options not explained above.\n");
928     } else {    /* for '-hh' or '--help --help' */
929         pr2serr(
930             "  where the remaining sg_ses options are:\n"
931             "    --ALL|-z            same as --all twice (adds thresholds)\n"
932             "    --byte1=B1|-b B1    byte 1 (2nd byte) of control page set "
933             "to B1\n"
934             "    --data=H,H...|-d H,H...    string of ASCII hex bytes to "
935             "send as a\n"
936             "                               control page or decode as a "
937             "status page\n"
938             "    --data=- | -d -     fetch string of ASCII hex bytes from "
939             "stdin\n"
940             "    --data=@FN | -d @FN    fetch string of ASCII hex bytes from "
941             "file: FN\n"
942             "    --eiioe=A_F|-E A_F    A_F is either 'auto' or 'force'. "
943             "'force' acts\n"
944             "                          as if EIIOE field is 1, 'auto' tries "
945             "to guess\n"
946             "    --enumerate|-e      enumerate page names + element types "
947             "(ignore\n"
948             "                        DEVICE). Use twice for clear,get,set "
949             "acronyms\n"
950             "    --hex|-H            print page response (or field) in hex\n"
951             "    --inhex=FN|-X FN    alternate form of --data=@FN\n"
952             "    --inner-hex|-i      print innermost level of a"
953             " status page in hex\n"
954             "    --list|-l           same as '--enumerate' option\n"
955             "    --mask|-M           ignore status element mask in modify "
956             "actions\n"
957             "                        (e.g.--set= and --clear=) (def: apply "
958             "mask)\n"
959             "    --maxlen=LEN|-m LEN    max response length (allocation "
960             "length in cdb)\n"
961             "    --nickid=SEID|-N SEID   SEID is subenclosure identifier "
962             "(def: 0)\n"
963             "                            used to specify which nickname to "
964             "change\n"
965             "    --nickname=SEN|-n SEN   SEN is new subenclosure nickname\n"
966             "    --quiet|-q          suppress some output messages\n"
967             "    --raw|-r            print status page in ASCII hex suitable "
968             "for '-d';\n"
969             "                        when used twice outputs page in binary "
970             "to stdout\n"
971             "    --readonly|-R       open DEVICE read-only (def: "
972             "read-write)\n"
973             "    --verbose|-v        increase verbosity\n"
974             "    --version|-V        print version string and exit\n"
975             "    --warn|-w           warn about join (and other) issues\n\n"
976             "If no options are given then DEVICE's supported diagnostic "
977             "pages are\nlisted. STR can be '<start_byte>:<start_bit>"
978             "[:<num_bits>][=<val>]'\nor '<acronym>[=val]'. Element type "
979             "abbreviations may be followed by a\nnumber (e.g. 'ps1' is "
980             "the second power supply element type). Use\n'sg_ses -e' and "
981             "'sg_ses -ee' for more information.\n\n"
982             );
983         pr2serr(
984             "Low level indexing can be done with one of the two '--index=' "
985             "options.\nAlternatively, medium level indexing can be done "
986             "with either the\n'--descriptor=', 'dev-slot-num=' or "
987             "'--sas-addr=' options. Support for\nthe medium level options "
988             "in the SES device is itself optional.\n"
989             );
990     }
991 }
992 
993 /* Return 0 for okay, else an error */
994 static int
parse_index(struct opts_t * op)995 parse_index(struct opts_t *op)
996 {
997     int n, n2;
998     const char * cp;
999     char * mallcp;
1000     char * c2p;
1001     const struct element_type_t * etp;
1002     char b[64];
1003     const int blen = sizeof(b);
1004 
1005     op->ind_given = true;
1006     n2 = 0;
1007     if ((cp = strchr(op->index_str, ','))) {
1008         /* decode number following comma */
1009         if (0 == strcmp("-1", cp + 1))
1010             n = -1;
1011         else {
1012             const char * cc3p;
1013 
1014             n = sg_get_num_nomult(cp + 1);
1015             if ((n < 0) || (n > 255)) {
1016                 pr2serr("bad argument to '--index=', after comma expect "
1017                         "number from -1 to 255\n");
1018                 return SG_LIB_SYNTAX_ERROR;
1019             }
1020             if ((cc3p = strchr(cp + 1, '-'))) {
1021                 n2 = sg_get_num_nomult(cc3p + 1);
1022                 if ((n2 < n) || (n2 > 255)) {
1023                     pr2serr("bad argument to '--index', after '-' expect "
1024                             "number from -%d to 255\n", n);
1025                     return SG_LIB_SYNTAX_ERROR;
1026                 }
1027             }
1028         }
1029         op->ind_indiv = n;
1030         if (n2 > 0)
1031             op->ind_indiv_last = n2;
1032         n = cp - op->index_str;
1033         if (n >= (blen - 1)) {
1034             pr2serr("bad argument to '--index', string prior to comma too "
1035                     "long\n");
1036             return SG_LIB_SYNTAX_ERROR;
1037         }
1038     } else {    /* no comma found in index_str */
1039         n = strlen(op->index_str);
1040         if (n >= (blen - 1)) {
1041             pr2serr("bad argument to '--index', string too long\n");
1042             return SG_LIB_SYNTAX_ERROR;
1043         }
1044     }
1045     snprintf(b, blen, "%.*s", n, op->index_str);
1046     if (0 == strcmp("-1", b)) {
1047         if (cp) {
1048             pr2serr("bad argument to '--index', unexpected '-1' type header "
1049                     "index\n");
1050             return SG_LIB_SYNTAX_ERROR;
1051         }
1052         op->ind_th = 0;
1053         op->ind_indiv = -1;
1054     } else if (isdigit((uint8_t)b[0])) {
1055         n = sg_get_num_nomult(b);
1056         if ((n < 0) || (n > 255)) {
1057             pr2serr("bad numeric argument to '--index', expect number from 0 "
1058                     "to 255\n");
1059             return SG_LIB_SYNTAX_ERROR;
1060         }
1061         if (cp)         /* argument to left of comma */
1062             op->ind_th = n;
1063         else {          /* no comma found, so 'n' is ind_indiv */
1064             op->ind_th = 0;
1065             op->ind_indiv = n;
1066             if ((c2p = strchr(b, '-'))) {
1067                 n2 = sg_get_num_nomult(c2p + 1);
1068                 if ((n2 < n) || (n2 > 255)) {
1069                     pr2serr("bad argument to '--index', after '-' expect "
1070                             "number from -%d to 255\n", n);
1071                     return SG_LIB_SYNTAX_ERROR;
1072                 }
1073             }
1074             op->ind_indiv_last = n2;
1075         }
1076     } else if ('_' == b[0]) {   /* leading "_" prefixes element type code */
1077         if ((c2p = strchr(b + 1, '_')))
1078             *c2p = '\0';        /* subsequent "_" prefixes e.t. index */
1079         n = sg_get_num_nomult(b + 1);
1080         if ((n < 0) || (n > 255)) {
1081             pr2serr("bad element type code for '--index', expect value from "
1082                     "0 to 255\n");
1083             return SG_LIB_SYNTAX_ERROR;
1084         }
1085         element_type_by_code.elem_type_code = n;
1086         mallcp = (char *)malloc(8);  /* willfully forget about freeing this */
1087         if (NULL == mallcp)
1088              return sg_convert_errno(ENOMEM);
1089         mallcp[0] = '_';
1090         snprintf(mallcp + 1, 6, "%d", n);
1091         element_type_by_code.abbrev = mallcp;
1092         if (c2p) {
1093             n = sg_get_num_nomult(c2p + 1);
1094             if ((n < 0) || (n > 255)) {
1095                 pr2serr("bad element type code <num> for '--index', expect "
1096                         "<num> from 0 to 255\n");
1097                 return SG_LIB_SYNTAX_ERROR;
1098             }
1099             op->ind_et_inst = n;
1100         }
1101         op->ind_etp = &element_type_by_code;
1102         if (NULL == cp)
1103             op->ind_indiv = -1;
1104     } else { /* element type abbreviation perhaps followed by <num> */
1105         int b_len = strlen(b);
1106 
1107         for (etp = element_type_arr; etp->desc; ++etp) {
1108             n = strlen(etp->abbrev);
1109             if ((n == b_len) && (0 == strncmp(b, etp->abbrev, n)))
1110                 break;
1111         }
1112         if (NULL == etp->desc) {
1113             pr2serr("bad element type abbreviation [%s] for '--index'\n"
1114                     "use '--enumerate' to see possibles\n", b);
1115             return SG_LIB_SYNTAX_ERROR;
1116         }
1117         if (b_len > n) {
1118             n = sg_get_num_nomult(b + n);
1119             if ((n < 0) || (n > 255)) {
1120                 pr2serr("bad element type abbreviation <num> for '--index', "
1121                         "expect <num> from 0 to 255\n");
1122                 return SG_LIB_SYNTAX_ERROR;
1123             }
1124             op->ind_et_inst = n;
1125         }
1126         op->ind_etp = etp;
1127         if (NULL == cp)
1128             op->ind_indiv = -1;
1129     }
1130     if (op->verbose > 1) {
1131         if (op->ind_etp)
1132             pr2serr("   element type abbreviation: %s, etp_num=%d, "
1133                     "individual index=%d\n", op->ind_etp->abbrev,
1134                     op->ind_et_inst, op->ind_indiv);
1135         else
1136             pr2serr("   type header index=%d, individual index=%d\n",
1137                     op->ind_th, op->ind_indiv);
1138     }
1139     return 0;
1140 }
1141 
1142 
1143 /* command line process, options and arguments. Returns 0 if ok. */
1144 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])1145 parse_cmd_line(struct opts_t *op, int argc, char *argv[])
1146 {
1147     int c, j, n, d_len, ret;
1148     const char * data_arg = NULL;
1149     const char * inhex_arg = NULL;
1150     uint64_t saddr;
1151     const char * cp;
1152 
1153     while (1) {
1154         int option_index = 0;
1155 
1156         c = getopt_long(argc, argv, "aA:b:cC:d:D:eE:fG:hHiI:jln:N:m:Mp:qrRs"
1157                         "S:vVwx:z", long_options, &option_index);
1158         if (c == -1)
1159             break;
1160 
1161         switch (c) {
1162         case 'a':       /* --all is synonym for --join */
1163             ++op->do_join;
1164             break;
1165         case 'A':       /* SAS address, assumed to be hex */
1166             cp = optarg;
1167             if ((strlen(optarg) > 2) && ('X' == toupper((uint8_t)optarg[1])))
1168                 cp = optarg + 2;
1169             if (1 != sscanf(cp, "%" SCNx64 "", &saddr)) {
1170                 pr2serr("bad argument to '--sas-addr=SA'\n");
1171                 return SG_LIB_SYNTAX_ERROR;
1172             }
1173             sg_put_unaligned_be64(saddr, op->sas_addr + 0);
1174             if (sg_all_ffs(op->sas_addr, 8)) {
1175                 pr2serr("error decoding '--sas-addr=SA' argument\n");
1176                 return SG_LIB_SYNTAX_ERROR;
1177             }
1178             break;
1179         case 'b':
1180             op->byte1 = sg_get_num_nomult(optarg);
1181             if ((op->byte1 < 0) || (op->byte1 > 255)) {
1182                 pr2serr("bad argument to '--byte1=B1' (0 to 255 "
1183                         "inclusive)\n");
1184                 return SG_LIB_SYNTAX_ERROR;
1185             }
1186             op->byte1_given = true;
1187             break;
1188         case 'c':
1189             op->do_control = true;
1190             break;
1191         case 'C':
1192             if (strlen(optarg) >= CGS_STR_MAX_SZ) {
1193                 pr2serr("--clear= option too long (max %d characters)\n",
1194                         CGS_STR_MAX_SZ);
1195                 return SG_LIB_SYNTAX_ERROR;
1196             }
1197             if (op->num_cgs < CGS_CL_ARR_MAX_SZ) {
1198                 op->cgs_cl_arr[op->num_cgs].cgs_sel = CLEAR_OPT;
1199                 strcpy(op->cgs_cl_arr[op->num_cgs].cgs_str, optarg);
1200                 ++op->num_cgs;
1201             } else {
1202                 pr2serr("Too many --clear=, --get= and --set= options "
1203                         "(max: %d)\n", CGS_CL_ARR_MAX_SZ);
1204                 return SG_LIB_CONTRADICT;
1205             }
1206             break;
1207         case 'd':
1208             data_arg = optarg;
1209             op->do_data = true;
1210             break;
1211         case 'D':
1212             op->desc_name = optarg;
1213             break;
1214         case 'e':
1215             ++op->enumerate;
1216             break;
1217         case 'E':
1218             if (0 == strcmp("auto", optarg))
1219                 op->eiioe_auto = true;
1220             else if (0 == strcmp("force", optarg))
1221                 op->eiioe_force = true;
1222             else {
1223                 pr2serr("--eiioe option expects 'auto' or 'force' as an "
1224                         "argument\n");
1225                 return SG_LIB_CONTRADICT;
1226             }
1227             break;
1228         case 'f':
1229             ++op->do_filter;
1230             break;
1231         case 'G':
1232             if (strlen(optarg) >= CGS_STR_MAX_SZ) {
1233                 pr2serr("--get= option too long (max %d characters)\n",
1234                         CGS_STR_MAX_SZ);
1235                 return SG_LIB_SYNTAX_ERROR;
1236             }
1237             if (op->num_cgs < CGS_CL_ARR_MAX_SZ) {
1238                 op->cgs_cl_arr[op->num_cgs].cgs_sel = GET_OPT;
1239                 strcpy(op->cgs_cl_arr[op->num_cgs].cgs_str, optarg);
1240                 ++op->num_cgs;
1241             } else {
1242                 pr2serr("Too many --clear=, --get= and --set= options "
1243                         "(max: %d)\n", CGS_CL_ARR_MAX_SZ);
1244                 return SG_LIB_CONTRADICT;
1245             }
1246             break;
1247         case 'h':
1248             ++op->do_help;
1249             break;
1250         case '?':
1251             pr2serr("\n");
1252             usage(0);
1253             return SG_LIB_SYNTAX_ERROR;
1254         case 'H':
1255             ++op->do_hex;
1256             break;
1257         case 'i':
1258             op->inner_hex = true;
1259             break;
1260         case 'I':
1261             op->index_str = optarg;
1262             break;
1263         case 'j':
1264             ++op->do_join;
1265             break;
1266         case 'l':
1267             op->do_list = true;
1268             break;
1269         case 'n':
1270             op->nickname_str = optarg;
1271             break;
1272         case 'N':
1273             op->seid = sg_get_num_nomult(optarg);
1274             if ((op->seid < 0) || (op->seid > 255)) {
1275                 pr2serr("bad argument to '--nickid=SEID' (0 to 255 "
1276                         "inclusive)\n");
1277                 return SG_LIB_SYNTAX_ERROR;
1278             }
1279             op->seid_given = true;
1280             break;
1281         case 'm':
1282             n = sg_get_num(optarg);
1283             if ((n < 0) || (n > 65535)) {
1284                 pr2serr("bad argument to '--maxlen=LEN' (0 to 65535 "
1285                         "inclusive expected)\n");
1286                 return SG_LIB_SYNTAX_ERROR;
1287             }
1288             if (0 == n)
1289                 op->maxlen = MX_ALLOC_LEN;
1290             else if (n < MIN_MAXLEN) {
1291                 pr2serr("Warning: --maxlen=LEN less than %d ignored\n",
1292                         MIN_MAXLEN);
1293                 op->maxlen = MX_ALLOC_LEN;
1294             } else
1295                 op->maxlen = n;
1296             break;
1297         case 'M':
1298             op->mask_ign = true;
1299             break;
1300         case 'p':
1301             if (isdigit((uint8_t)optarg[0])) {
1302                 op->page_code = sg_get_num_nomult(optarg);
1303                 if ((op->page_code < 0) || (op->page_code > 255)) {
1304                     pr2serr("bad argument to '--page=PG' (0 to 255 "
1305                             "inclusive)\n");
1306                     return SG_LIB_SYNTAX_ERROR;
1307                 }
1308             } else {
1309                 const struct diag_page_abbrev * ap;
1310 
1311                 for (ap = dp_abbrev; ap->abbrev; ++ap) {
1312                     if (strcase_eq(ap->abbrev, optarg)) {
1313                         op->page_code = ap->page_code;
1314                         break;
1315                     }
1316                 }
1317                 if (NULL == ap->abbrev) {
1318                     pr2serr("'--page=PG' argument abbreviation \"%s\" not "
1319                             "found\nHere are the choices:\n", optarg);
1320                     enumerate_diag_pages();
1321                     return SG_LIB_SYNTAX_ERROR;
1322                 }
1323             }
1324             op->page_code_given = true;
1325             break;
1326         case 'q':
1327             op->quiet = true;
1328             break;
1329         case 'r':
1330             ++op->do_raw;
1331             break;
1332         case 'R':
1333             op->o_readonly = true;
1334             break;
1335         case 's':
1336             op->do_status = true;
1337             break;
1338         case 'S':
1339             if (strlen(optarg) >= CGS_STR_MAX_SZ) {
1340                 pr2serr("--set= option too long (max %d characters)\n",
1341                         CGS_STR_MAX_SZ);
1342                 return SG_LIB_SYNTAX_ERROR;
1343             }
1344             if (op->num_cgs < CGS_CL_ARR_MAX_SZ) {
1345                 op->cgs_cl_arr[op->num_cgs].cgs_sel = SET_OPT;
1346                 strcpy(op->cgs_cl_arr[op->num_cgs].cgs_str, optarg);
1347                 ++op->num_cgs;
1348             } else {
1349                 pr2serr("Too many --clear=, --get= and --set= options "
1350                         "(max: %d)\n", CGS_CL_ARR_MAX_SZ);
1351                 return SG_LIB_CONTRADICT;
1352             }
1353             break;
1354         case 'v':
1355             op->verbose_given = true;
1356             ++op->verbose;
1357             break;
1358         case 'V':
1359             op->version_given = true;
1360             return 0;
1361         case 'w':
1362             op->warn = true;
1363             break;
1364         case 'x':
1365             op->dev_slot_num = sg_get_num_nomult(optarg);
1366             if ((op->dev_slot_num < 0) || (op->dev_slot_num > 255)) {
1367                 pr2serr("bad argument to '--dev-slot-num' (0 to 255 "
1368                         "inclusive)\n");
1369                 return SG_LIB_SYNTAX_ERROR;
1370             }
1371             break;
1372         case 'X':       /* --inhex=FN for compatibility with other utils */
1373             inhex_arg = optarg;
1374             op->do_data = true;
1375             break;
1376         case 'z':       /* --ALL and -z are synonyms for '--join --join' */
1377             /* -A already used for --sas-addr=SA shortened form */
1378             op->do_join += 2;
1379             break;
1380         default:
1381             pr2serr("unrecognised option code 0x%x ??\n", c);
1382             goto err_help;
1383         }
1384     }
1385     if (op->do_help)
1386         return 0;
1387     if (optind < argc) {
1388         if (NULL == op->dev_name) {
1389             op->dev_name = argv[optind];
1390             ++optind;
1391         }
1392         if (optind < argc) {
1393             for (; optind < argc; ++optind)
1394                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
1395             goto err_help;
1396         }
1397     }
1398     op->mx_arr_len = (op->maxlen > MIN_DATA_IN_SZ) ? op->maxlen :
1399                                                      MIN_DATA_IN_SZ;
1400     op->data_arr = sg_memalign(op->mx_arr_len, 0 /* page aligned */,
1401                                &op->free_data_arr, false);
1402     if (NULL == op->data_arr) {
1403         pr2serr("unable to allocate %u bytes on heap\n", op->mx_arr_len);
1404         return sg_convert_errno(ENOMEM);
1405     }
1406     if (data_arg || inhex_arg) {
1407         if (inhex_arg) {
1408             data_arg = inhex_arg;
1409             if (read_hex(data_arg, op->data_arr + DATA_IN_OFF,
1410                          op->mx_arr_len - DATA_IN_OFF, &op->arr_len,
1411                          (op->do_raw < 2), false, op->verbose)) {
1412                 pr2serr("bad argument, expect '--inhex=FN' or '--inhex=-'\n");
1413                 return SG_LIB_SYNTAX_ERROR;
1414             }
1415         } else {
1416             if (read_hex(data_arg, op->data_arr + DATA_IN_OFF,
1417                          op->mx_arr_len - DATA_IN_OFF, &op->arr_len,
1418                          (op->do_raw < 2), true, op->verbose)) {
1419                 pr2serr("bad argument, expect '--data=H,H...', '--data=-' or "
1420                         "'--data=@FN'\n");
1421                 return SG_LIB_SYNTAX_ERROR;
1422             }
1423         }
1424         op->do_raw = 0;
1425         /* struct data_in_desc_t stuff does not apply when --control */
1426         if (op->do_status && (op->arr_len > 3)) {
1427             int off;
1428             int pc = 0;
1429             const uint8_t * bp = op->data_arr + DATA_IN_OFF;
1430             struct data_in_desc_t * didp = data_in_desc_arr;
1431 
1432             d_len = sg_get_unaligned_be16(bp + 2) + 4;
1433             for (n = 0, off = 0; n < MX_DATA_IN_DESCS; ++n, ++didp) {
1434                 didp->in_use = true;
1435                 pc = bp[0];
1436                 didp->page_code = pc;
1437                 didp->offset = off;
1438                 didp->dp_len = d_len;
1439                 off += d_len;
1440                 if ((off + 3) < op->arr_len) {
1441                     bp += d_len;
1442                     d_len = sg_get_unaligned_be16(bp + 2) + 4;
1443                 } else {
1444                     ++n;
1445                     break;
1446                 }
1447             }
1448             if (1 == n) {
1449                 op->page_code_given = true;
1450                 op->page_code = pc;
1451             } else      /* n must be > 1 */
1452                 op->many_dpages = true;
1453 
1454             if (op->verbose > 3) {
1455                 int k;
1456                 char b[128];
1457 
1458                 for (didp = data_in_desc_arr, k = 0; k < n; ++k, ++didp) {
1459                     if ((cp = find_in_diag_page_desc(didp->page_code)))
1460                         snprintf(b, sizeof(b), "%s dpage", cp);
1461                     else
1462                         snprintf(b, sizeof(b), "dpage 0x%x", didp->page_code);
1463                     pr2serr("%s found, offset %d, dp_len=%d\n", b,
1464                             didp->offset, didp->dp_len);
1465                 }
1466             }
1467         }
1468     }
1469     if (op->do_join && op->do_control) {
1470         pr2serr("cannot have '--join' and '--control'\n");
1471         goto err_help;
1472     }
1473     if (op->index_str) {
1474         ret = parse_index(op);
1475         if (ret) {
1476             pr2serr("  For more information use '--help'\n");
1477             return ret;
1478         }
1479     }
1480     if (op->desc_name || (op->dev_slot_num >= 0) ||
1481         saddr_non_zero(op->sas_addr)) {
1482         if (op->ind_given) {
1483             pr2serr("cannot have --index with either --descriptor, "
1484                     "--dev-slot-num or --sas-addr\n");
1485             goto err_help;
1486         }
1487         if (((!! op->desc_name) + (op->dev_slot_num >= 0) +
1488              saddr_non_zero(op->sas_addr)) > 1) {
1489             pr2serr("can only have one of --descriptor, "
1490                     "--dev-slot-num and --sas-addr\n");
1491             goto err_help;
1492         }
1493         if ((0 == op->do_join) && (! op->do_control) &&
1494             (0 == op->num_cgs) && (! op->page_code_given)) {
1495             ++op->do_join;      /* implicit --join */
1496             if (op->verbose)
1497                 pr2serr("process as if --join option is set\n");
1498         }
1499     }
1500     if (op->ind_given) {
1501         if ((0 == op->do_join) && (! op->do_control) &&
1502             (0 == op->num_cgs) && (! op->page_code_given)) {
1503             op->page_code_given = true;
1504             op->page_code = ENC_STATUS_DPC;  /* implicit status page */
1505             if (op->verbose)
1506                 pr2serr("assume --page=2 (es) option is set\n");
1507         }
1508     }
1509     if (op->do_list || op->enumerate)
1510         return 0;
1511 
1512     if (op->do_control && op->do_status) {
1513         pr2serr("cannot have both '--control' and '--status'\n");
1514         goto err_help;
1515     } else if (op->do_control) {
1516         if (op->nickname_str || op->seid_given)
1517             ;
1518         else if (! op->do_data) {
1519             pr2serr("need to give '--data' in control mode\n");
1520             goto err_help;
1521         }
1522     } else if (! op->do_status) {
1523         if (op->do_data) {
1524             pr2serr("when user data given, require '--control' or "
1525                     "'--status' option\n");
1526             goto err_help;
1527         }
1528         op->do_status = true;  /* default to receiving status pages */
1529     } else if (op->do_status && op->do_data && op->dev_name) {
1530         pr2serr(">>> Warning: device name (%s) will be ignored\n",
1531                 op->dev_name);
1532         op->dev_name = NULL;    /* quash device name */
1533     }
1534 
1535     if (op->nickname_str) {
1536         if (! op->do_control) {
1537             pr2serr("since '--nickname=' implies control mode, require "
1538                     "'--control' as well\n");
1539             goto err_help;
1540         }
1541         if (op->page_code_given) {
1542             if (SUBENC_NICKNAME_DPC != op->page_code) {
1543                 pr2serr("since '--nickname=' assume or expect "
1544                         "'--page=snic'\n");
1545                 goto err_help;
1546             }
1547         } else
1548             op->page_code = SUBENC_NICKNAME_DPC;
1549     } else if (op->seid_given) {
1550         pr2serr("'--nickid=' must be used together with '--nickname='\n");
1551         goto err_help;
1552 
1553     }
1554     if ((op->verbose > 4) && saddr_non_zero(op->sas_addr)) {
1555         pr2serr("    SAS address (in hex): ");
1556         for (j = 0; j < 8; ++j)
1557             pr2serr("%02x", op->sas_addr[j]);
1558         pr2serr("\n");
1559     }
1560 
1561     if ((! (op->do_data && op->do_status)) && (NULL == op->dev_name)) {
1562         pr2serr("missing DEVICE name!\n\n");
1563         goto err_help;
1564     }
1565     return 0;
1566 
1567 err_help:
1568     if (op->verbose) {
1569         pr2serr("\n");
1570         usage(0);
1571     }
1572     return SG_LIB_SYNTAX_ERROR;
1573 }
1574 
1575 /* Parse clear/get/set string, writes output to '*tavp'. Uses 'buff' for
1576  * scratch area. Returns 0 on success, else -1. */
1577 static int
parse_cgs_str(char * buff,struct tuple_acronym_val * tavp)1578 parse_cgs_str(char * buff, struct tuple_acronym_val * tavp)
1579 {
1580     char * esp;
1581     char * colp;
1582     unsigned int ui;
1583 
1584     tavp->acron = NULL;
1585     tavp->val_str = NULL;
1586     tavp->start_byte = -1;
1587     tavp->num_bits = 1;
1588     if ((esp = strchr(buff, '='))) {
1589         tavp->val_str = esp + 1;
1590         *esp = '\0';
1591         if (0 == strcmp("-1", esp + 1))
1592             tavp->val = -1;
1593         else {
1594             tavp->val = sg_get_llnum_nomult(esp + 1);
1595             if (-1 == tavp->val) {
1596                 pr2serr("unable to decode: %s value\n", esp + 1);
1597                 pr2serr("    expected: <acronym>[=<val>]\n");
1598                 return -1;
1599             }
1600         }
1601     }
1602     if (isalpha((uint8_t)buff[0]))
1603         tavp->acron = buff;
1604     else {
1605         char * cp;
1606 
1607         colp = strchr(buff, ':');
1608         if ((NULL == colp) || (buff == colp))
1609             return -1;
1610         *colp = '\0';
1611         if (('0' == buff[0]) && ('X' == toupper((uint8_t)buff[1]))) {
1612             if (1 != sscanf(buff + 2, "%x", &ui))
1613                 return -1;
1614             tavp->start_byte = ui;
1615         } else if ('H' == toupper((uint8_t)*(colp - 1))) {
1616             if (1 != sscanf(buff, "%x", &ui))
1617                 return -1;
1618             tavp->start_byte = ui;
1619         } else {
1620             if (1 != sscanf(buff, "%d", &tavp->start_byte))
1621                 return -1;
1622         }
1623         if ((tavp->start_byte < 0) || (tavp->start_byte > 127)) {
1624             pr2serr("<start_byte> needs to be between 0 and 127\n");
1625             return -1;
1626         }
1627         cp = colp + 1;
1628         colp = strchr(cp, ':');
1629         if (cp == colp)
1630             return -1;
1631         if (colp)
1632             *colp = '\0';
1633         if (1 != sscanf(cp, "%d", &tavp->start_bit))
1634             return -1;
1635         if ((tavp->start_bit < 0) || (tavp->start_bit > 7)) {
1636             pr2serr("<start_bit> needs to be between 0 and 7\n");
1637             return -1;
1638         }
1639         if (colp) {
1640             if (1 != sscanf(colp + 1, "%d", &tavp->num_bits))
1641                 return -1;
1642         }
1643         if ((tavp->num_bits < 1) || (tavp->num_bits > 64)) {
1644             pr2serr("<num_bits> needs to be between 1 and 64\n");
1645             return -1;
1646         }
1647     }
1648     return 0;
1649 }
1650 
1651 /* Fetch diagnostic page name (control or out). Returns NULL if not found. */
1652 static const char *
find_out_diag_page_desc(int page_num)1653 find_out_diag_page_desc(int page_num)
1654 {
1655     const struct diag_page_code * pcdp;
1656 
1657     for (pcdp = out_dpc_arr; pcdp->desc; ++pcdp) {
1658         if (page_num == pcdp->page_code)
1659             return pcdp->desc;
1660         else if (page_num < pcdp->page_code)
1661             return NULL;
1662     }
1663     return NULL;
1664 }
1665 
1666 static bool
match_ind_indiv(int index,const struct opts_t * op)1667 match_ind_indiv(int index, const struct opts_t * op)
1668 {
1669     if (index == op->ind_indiv)
1670         return true;
1671     if (op->ind_indiv_last > op->ind_indiv) {
1672         if ((index > op->ind_indiv) && (index <= op->ind_indiv_last))
1673             return true;
1674     }
1675     return false;
1676 }
1677 
1678 #if 0
1679 static bool
1680 match_last_ind_indiv(int index, const struct opts_t * op)
1681 {
1682     if (op->ind_indiv_last >= op->ind_indiv)
1683         return (index == op->ind_indiv_last);
1684     return (index == op->ind_indiv);
1685 }
1686 #endif
1687 
1688 /* Return of 0 -> success, SG_LIB_CAT_* positive values or -1 -> other
1689  * failures */
1690 static int
do_senddiag(struct sg_pt_base * ptvp,void * outgoing_pg,int outgoing_len,bool noisy,int verbose)1691 do_senddiag(struct sg_pt_base * ptvp, void * outgoing_pg, int outgoing_len,
1692             bool noisy, int verbose)
1693 {
1694     int ret;
1695 
1696     if (outgoing_pg && (verbose > 2)) {
1697         int page_num = ((const char *)outgoing_pg)[0];
1698         const char * cp = find_out_diag_page_desc(page_num);
1699 
1700         if (cp)
1701             pr2serr("    Send diagnostic command page name: %s\n", cp);
1702         else
1703             pr2serr("    Send diagnostic command page number: 0x%x\n",
1704                     page_num);
1705     }
1706     ret = sg_ll_send_diag_pt(ptvp, 0 /* sf_code */, true /* pf_bit */,
1707                              false /* sf_bit */, false /* devofl_bit */,
1708                              false /* unitofl_bit */, 0 /* long_duration */,
1709                              outgoing_pg, outgoing_len, noisy, verbose);
1710     clear_scsi_pt_obj(ptvp);
1711     return ret;
1712 }
1713 
1714 /* Fetch diagnostic page name (status and/or control). Returns NULL if not
1715  * found. */
1716 static const char *
find_diag_page_desc(int page_num)1717 find_diag_page_desc(int page_num)
1718 {
1719     const struct diag_page_code * pcdp;
1720 
1721     for (pcdp = dpc_arr; pcdp->desc; ++pcdp) {
1722         if (page_num == pcdp->page_code)
1723             return pcdp->desc;
1724         else if (page_num < pcdp->page_code)
1725             return NULL;
1726     }
1727     return NULL;
1728 }
1729 
1730 /* Fetch diagnostic page name (status or in). Returns NULL if not found. */
1731 static const char *
find_in_diag_page_desc(int page_num)1732 find_in_diag_page_desc(int page_num)
1733 {
1734     const struct diag_page_code * pcdp;
1735 
1736     for (pcdp = in_dpc_arr; pcdp->desc; ++pcdp) {
1737         if (page_num == pcdp->page_code)
1738             return pcdp->desc;
1739         else if (page_num < pcdp->page_code)
1740             return NULL;
1741     }
1742     return NULL;
1743 }
1744 
1745 /* Fetch element type name. Returns NULL if not found. */
1746 static char *
etype_str(int elem_type_code,char * b,int mlen_b)1747 etype_str(int elem_type_code, char * b, int mlen_b)
1748 {
1749     const struct element_type_t * etp;
1750     int len;
1751 
1752     if ((NULL == b) || (mlen_b < 1))
1753         return b;
1754     for (etp = element_type_arr; etp->desc; ++etp) {
1755         if (elem_type_code == etp->elem_type_code) {
1756             len = strlen(etp->desc);
1757             if (len < mlen_b)
1758                 strcpy(b, etp->desc);
1759             else {
1760                 strncpy(b, etp->desc, mlen_b - 1);
1761                 b[mlen_b - 1] = '\0';
1762             }
1763             return b;
1764         } else if (elem_type_code < etp->elem_type_code)
1765             break;
1766     }
1767     if (elem_type_code < 0x80)
1768         snprintf(b, mlen_b - 1, "[0x%x]", elem_type_code);
1769     else
1770         snprintf(b, mlen_b - 1, "vendor specific [0x%x]", elem_type_code);
1771     b[mlen_b - 1] = '\0';
1772     return b;
1773 }
1774 
1775 /* Returns true if el_type (element type) is of interest to the Additional
1776  * Element Status page. Otherwise return false. */
1777 static bool
is_et_used_by_aes(int el_type)1778 is_et_used_by_aes(int el_type)
1779 {
1780     if ((el_type >= 0) && (el_type < NUM_ACTIVE_ET_AESP_ARR))
1781         return active_et_aesp_arr[el_type];
1782     else
1783         return false;
1784 }
1785 
1786 #if 0
1787 static struct join_row_t *
1788 find_join_row(struct th_es_t * tesp, int index, enum fj_select_t sel)
1789 {
1790     int k;
1791     struct join_row_t * jrp = tesp->j_base;
1792 
1793     if (index < 0)
1794         return NULL;
1795     switch (sel) {
1796     case FJ_IOE:     /* index includes overall element */
1797         if (index >= tesp->num_j_rows)
1798             return NULL;
1799         return jrp + index;
1800     case FJ_EOE:     /* index excludes overall element */
1801         if (index >= tesp->num_j_eoe)
1802             return NULL;
1803         for (k = 0; k < tesp->num_j_rows; ++k, ++jrp) {
1804             if (index == jrp->ei_eoe)
1805                 return jrp;
1806         }
1807         return NULL;
1808     case FJ_AESS:    /* index includes only AES listed element types */
1809         if (index >= tesp->num_j_eoe)
1810             return NULL;
1811         for (k = 0; k < tesp->num_j_rows; ++k, ++jrp) {
1812             if (index == jrp->ei_aess)
1813                 return jrp;
1814         }
1815         return NULL;
1816     case FJ_SAS_CON: /* index on non-overall SAS connector etype */
1817         if (index >= tesp->num_j_rows)
1818             return NULL;
1819         for (k = 0; k < tesp->num_j_rows; ++k, ++jrp) {
1820             if (SAS_CONNECTOR_ETC == jrp->etype) {
1821                 if (index == jrp->indiv_i)
1822                     return jrp;
1823             }
1824         }
1825         return NULL;
1826     default:
1827         pr2serr("%s: bad selector: %d\n", __func__, (int)sel);
1828         return NULL;
1829     }
1830 }
1831 #endif
1832 
1833 static const struct join_row_t *
find_join_row_cnst(const struct th_es_t * tesp,int index,enum fj_select_t sel)1834 find_join_row_cnst(const struct th_es_t * tesp, int index,
1835                    enum fj_select_t sel)
1836 {
1837     int k;
1838     const struct join_row_t * jrp = tesp->j_base;
1839 
1840     if (index < 0)
1841         return NULL;
1842     switch (sel) {
1843     case FJ_IOE:     /* index includes overall element */
1844         if (index >= tesp->num_j_rows)
1845             return NULL;
1846         return jrp + index;
1847     case FJ_EOE:     /* index excludes overall element */
1848         if (index >= tesp->num_j_eoe)
1849             return NULL;
1850         for (k = 0; k < tesp->num_j_rows; ++k, ++jrp) {
1851             if (index == jrp->ei_eoe)
1852                 return jrp;
1853         }
1854         return NULL;
1855     case FJ_AESS:   /* index includes only AES listed element types */
1856         if (index >= tesp->num_j_eoe)
1857             return NULL;
1858         for (k = 0; k < tesp->num_j_rows; ++k, ++jrp) {
1859             if (index == jrp->ei_aess)
1860                 return jrp;
1861         }
1862         return NULL;
1863     case FJ_SAS_CON: /* index on non-overall SAS connector etype */
1864         if (index >= tesp->num_j_rows)
1865             return NULL;
1866         for (k = 0; k < tesp->num_j_rows; ++k, ++jrp) {
1867             if (SAS_CONNECTOR_ETC == jrp->etype) {
1868                 if (index == jrp->indiv_i)
1869                     return jrp;
1870             }
1871         }
1872         return NULL;
1873     default:
1874         pr2serr("%s: bad selector: %d\n", __func__, (int)sel);
1875         return NULL;
1876     }
1877 }
1878 
1879 /* Return of 0 -> success, SG_LIB_CAT_* positive values or -2 if response
1880  * had bad format, -1 -> other failures */
1881 static int
do_rec_diag(struct sg_pt_base * ptvp,int page_code,uint8_t * rsp_buff,int rsp_buff_size,struct opts_t * op,int * rsp_lenp)1882 do_rec_diag(struct sg_pt_base * ptvp, int page_code, uint8_t * rsp_buff,
1883             int rsp_buff_size, struct opts_t * op, int * rsp_lenp)
1884 {
1885     int k, d_len, rsp_len, res;
1886     int resid = 0;
1887     int vb = op->verbose;
1888     const char * cp;
1889     char b[80];
1890     char bb[120];
1891     static const char * rdr = "Receive diagnostic results";
1892 
1893     memset(rsp_buff, 0, rsp_buff_size);
1894     if (rsp_lenp)
1895         *rsp_lenp = 0;
1896     if ((cp = find_in_diag_page_desc(page_code)))
1897         snprintf(bb, sizeof(bb), "%s dpage", cp);
1898     else
1899         snprintf(bb, sizeof(bb), "dpage 0x%x", page_code);
1900     cp = bb;
1901 
1902     if (op->data_arr && op->do_data) {  /* user provided data */
1903         /* N.B. First 4 bytes in data_arr are not used, user data was read in
1904          * starting at byte offset 4 */
1905         bool found = false;
1906         int off = 0;
1907         const uint8_t * bp = op->data_arr + DATA_IN_OFF;
1908         const struct data_in_desc_t * didp = data_in_desc_arr;
1909 
1910         for (k = 0, d_len = 0; k < MX_DATA_IN_DESCS; ++k, ++didp) {
1911             if (! didp->in_use)
1912                 break;
1913             if (page_code == didp->page_code) {
1914                 off = didp->offset;
1915                 d_len = didp->dp_len;
1916                 found = true;
1917                 break;
1918             }
1919         }
1920         if (found)
1921             memcpy(rsp_buff, bp + off, d_len);
1922         else {
1923             if (vb)
1924                 pr2serr("%s: %s not found in user data\n", __func__, cp);
1925             return SG_LIB_CAT_OTHER;
1926         }
1927 
1928         cp = find_in_diag_page_desc(page_code);
1929         if (vb > 2) {
1930             pr2serr("    %s: response data from user", rdr);
1931             if (3 == vb) {
1932                 pr2serr("%s:\n", (d_len > 256 ? ", first 256 bytes" : ""));
1933                 hex2stderr(rsp_buff, (d_len > 256 ? 256 : d_len), -1);
1934             } else {
1935                 pr2serr(":\n");
1936                 hex2stderr(rsp_buff, d_len, 0);
1937             }
1938         }
1939         res = 0;
1940         resid = rsp_buff_size - d_len;
1941         goto decode;    /* step over the device access */
1942     }
1943     if (vb > 1)
1944         pr2serr("    %s command for %s\n", rdr, cp);
1945     res = sg_ll_receive_diag_pt(ptvp, true /* pcv */, page_code, rsp_buff,
1946                                 rsp_buff_size, 0 /* default timeout */,
1947                                 &resid, ! op->quiet, vb);
1948     clear_scsi_pt_obj(ptvp);
1949 decode:
1950     if (0 == res) {
1951         rsp_len = sg_get_unaligned_be16(rsp_buff + 2) + 4;
1952         if (rsp_len > rsp_buff_size) {
1953             if (rsp_buff_size > 8) /* tried to get more than header */
1954                 pr2serr("<<< warning response buffer too small [was %d but "
1955                         "need %d]>>>\n", rsp_buff_size, rsp_len);
1956             if (resid > 0)
1957                 rsp_buff_size -= resid;
1958         } else if (resid > 0)
1959             rsp_buff_size -= resid;
1960         rsp_len = (rsp_len < rsp_buff_size) ? rsp_len : rsp_buff_size;
1961         if (rsp_len < 0) {
1962             pr2serr("<<< warning: resid=%d too large, implies negative "
1963                     "reply length: %d\n", resid, rsp_len);
1964             rsp_len = 0;
1965         }
1966         if (rsp_lenp)
1967             *rsp_lenp = rsp_len;
1968         if ((rsp_len > 1) && (page_code != rsp_buff[0])) {
1969             if ((0x9 == rsp_buff[0]) && (1 & rsp_buff[1])) {
1970                 pr2serr("Enclosure busy, try again later\n");
1971                 if (op->do_hex)
1972                     hex2stderr(rsp_buff, rsp_len, 0);
1973             } else if (0x8 == rsp_buff[0]) {
1974                 pr2serr("Enclosure only supports Short Enclosure Status: "
1975                         "0x%x\n", rsp_buff[1]);
1976             } else {
1977                 pr2serr("Invalid response, wanted page code: 0x%x but got "
1978                         "0x%x\n", page_code, rsp_buff[0]);
1979                 hex2stderr(rsp_buff, rsp_len, 0);
1980             }
1981             return -2;
1982         }
1983         return 0;
1984     } else if (vb) {
1985         pr2serr("Attempt to fetch %s failed\n", cp);
1986         sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
1987         pr2serr("    %s\n", b);
1988     }
1989     return res;
1990 }
1991 
1992 #if 1
1993 
1994 static void
dStrRaw(const uint8_t * str,int len)1995 dStrRaw(const uint8_t * str, int len)
1996 {
1997     int k;
1998 
1999     for (k = 0; k < len; ++k)
2000         printf("%c", str[k]);
2001 }
2002 
2003 #else
2004 
2005 static void
dStrRaw(const uint8_t * str,int len)2006 dStrRaw(const uint8_t * str, int len)
2007 {
2008     int res, err;
2009 
2010     if (len > 0) {
2011         res = write(fileno(stdout), str, len);
2012         if (res < 0) {
2013             err = errno;
2014             pr2serr("%s: write to stdout failed: %s [%d]\n", __func__,
2015                     strerror(err), err);
2016         }
2017     }
2018 }
2019 
2020 #endif
2021 
2022 /* CONFIGURATION_DPC [0x1]
2023  * Display Configuration diagnostic page. */
2024 static void
configuration_sdg(const uint8_t * resp,int resp_len)2025 configuration_sdg(const uint8_t * resp, int resp_len)
2026 {
2027     int j, k, el, num_subs, sum_elem_types;
2028     uint32_t gen_code;
2029     const uint8_t * bp;
2030     const uint8_t * last_bp;
2031     const uint8_t * text_bp;
2032     char b[64];
2033 
2034     printf("Configuration diagnostic page:\n");
2035     if (resp_len < 4)
2036         goto truncated;
2037     num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
2038     sum_elem_types = 0;
2039     last_bp = resp + resp_len - 1;
2040     printf("  number of secondary subenclosures: %d\n",
2041             num_subs - 1);
2042     gen_code = sg_get_unaligned_be32(resp + 4);
2043     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
2044     bp = resp + 8;
2045     printf("  enclosure descriptor list\n");
2046     for (k = 0; k < num_subs; ++k, bp += el) {
2047         if ((bp + 3) > last_bp)
2048             goto truncated;
2049         el = bp[3] + 4;
2050         sum_elem_types += bp[2];
2051         printf("    Subenclosure identifier: %d%s\n", bp[1],
2052                (bp[1] ? "" : " [primary]"));
2053         printf("      relative ES process id: %d, number of ES processes"
2054                ": %d\n", ((bp[0] & 0x70) >> 4), (bp[0] & 0x7));
2055         printf("      number of type descriptor headers: %d\n", bp[2]);
2056         if (el < 40) {
2057             pr2serr("      enc descriptor len=%d ??\n", el);
2058             continue;
2059         }
2060         printf("      enclosure logical identifier (hex): ");
2061         for (j = 0; j < 8; ++j)
2062             printf("%02x", bp[4 + j]);
2063         printf("\n      enclosure vendor: %.8s  product: %.16s  rev: %.4s\n",
2064                bp + 12, bp + 20, bp + 36);
2065         if (el > 40) {
2066             char bb[1024];
2067 
2068             printf("      vendor-specific data:\n");
2069             hex2str(bp + 40, el - 40, "        ", 0, sizeof(bb), bb);
2070             printf("%s\n", bb);
2071         }
2072     }
2073     /* printf("\n"); */
2074     printf("  type descriptor header and text list\n");
2075     text_bp = bp + (sum_elem_types * 4);
2076     for (k = 0; k < sum_elem_types; ++k, bp += 4) {
2077         if ((bp + 3) > last_bp)
2078             goto truncated;
2079         printf("    Element type: %s, subenclosure id: %d\n",
2080                etype_str(bp[0], b, sizeof(b)), bp[2]);
2081         printf("      number of possible elements: %d\n", bp[1]);
2082         if (bp[3] > 0) {
2083             if (text_bp > last_bp)
2084                 goto truncated;
2085             printf("      text: %.*s\n", bp[3], text_bp);
2086             text_bp += bp[3];
2087         }
2088     }
2089     return;
2090 truncated:
2091     pr2serr("    <<<ses_configuration_sdg: response too short>>>\n");
2092     return;
2093 }
2094 
2095 /* CONFIGURATION_DPC [0x1] read and used to build array pointed to by
2096  * 'tdhp' with no more than 'max_elems' elements. If 'generationp' is non
2097  * NULL then writes generation code where it points. if 'primary_ip" is
2098  * non NULL the writes rimary enclosure info where it points.
2099  * Returns total number of type descriptor headers written to 'tdhp' or -1
2100  * if there is a problem */
2101 static int
build_type_desc_hdr_arr(struct sg_pt_base * ptvp,struct type_desc_hdr_t * tdhp,int max_elems,uint32_t * generationp,struct enclosure_info * primary_ip,struct opts_t * op)2102 build_type_desc_hdr_arr(struct sg_pt_base * ptvp,
2103                          struct type_desc_hdr_t * tdhp, int max_elems,
2104                         uint32_t * generationp,
2105                         struct enclosure_info * primary_ip,
2106                         struct opts_t * op)
2107 {
2108     int resp_len, k, el, num_subs, sum_type_dheaders, res, n;
2109     int ret = 0;
2110     uint32_t gen_code;
2111     const uint8_t * bp;
2112     const uint8_t * last_bp;
2113 
2114     if (NULL == config_dp_resp) {
2115         config_dp_resp = sg_memalign(op->maxlen, 0, &free_config_dp_resp,
2116                                      false);
2117         if (NULL == config_dp_resp) {
2118             pr2serr("%s: unable to allocate %d bytes on heap\n", __func__,
2119                     op->maxlen);
2120             ret = -1;
2121             goto the_end;
2122         }
2123         res = do_rec_diag(ptvp, CONFIGURATION_DPC, config_dp_resp, op->maxlen,
2124                           op, &resp_len);
2125         if (res) {
2126             pr2serr("%s: couldn't read config page, res=%d\n", __func__, res);
2127             ret = -1;
2128             free(free_config_dp_resp);
2129             free_config_dp_resp = NULL;
2130             goto the_end;
2131         }
2132         if (resp_len < 4) {
2133             ret = -1;
2134             free(free_config_dp_resp);
2135             free_config_dp_resp = NULL;
2136             goto the_end;
2137         }
2138         config_dp_resp_len = resp_len;
2139     } else
2140         resp_len = config_dp_resp_len;
2141 
2142     num_subs = config_dp_resp[1] + 1;
2143     sum_type_dheaders = 0;
2144     last_bp = config_dp_resp + resp_len - 1;
2145     gen_code = sg_get_unaligned_be32(config_dp_resp + 4);
2146     if (generationp)
2147         *generationp = gen_code;
2148     bp = config_dp_resp + 8;
2149     for (k = 0; k < num_subs; ++k, bp += el) {
2150         if ((bp + 3) > last_bp)
2151             goto p_truncated;
2152         el = bp[3] + 4;
2153         sum_type_dheaders += bp[2];
2154         if (el < 40) {
2155             pr2serr("%s: short enc descriptor len=%d ??\n", __func__, el);
2156             continue;
2157         }
2158         if ((0 == k) && primary_ip) {
2159             ++primary_ip->have_info;
2160             primary_ip->rel_esp_id = (bp[0] & 0x70) >> 4;
2161             primary_ip->num_esp = (bp[0] & 0x7);
2162             memcpy(primary_ip->enc_log_id, bp + 4, 8);
2163             memcpy(primary_ip->enc_vendor_id, bp + 12, 8);
2164             memcpy(primary_ip->product_id, bp + 20, 16);
2165             memcpy(primary_ip->product_rev_level, bp + 36, 4);
2166         }
2167     }
2168     for (k = 0; k < sum_type_dheaders; ++k, bp += 4) {
2169         if ((bp + 3) > last_bp)
2170             goto p_truncated;
2171         if (k >= max_elems) {
2172             pr2serr("%s: too many elements\n", __func__);
2173             ret = -1;
2174             goto the_end;
2175         }
2176         tdhp[k].etype = bp[0];
2177         tdhp[k].num_elements = bp[1];
2178         tdhp[k].se_id = bp[2];
2179         tdhp[k].txt_len = bp[3];
2180     }
2181     if (op->ind_given && op->ind_etp) {
2182         n = op->ind_et_inst;
2183         for (k = 0; k < sum_type_dheaders; ++k) {
2184             if (op->ind_etp->elem_type_code == tdhp[k].etype) {
2185                 if (0 == n)
2186                     break;
2187                 else
2188                     --n;
2189             }
2190         }
2191         if (k < sum_type_dheaders)
2192             op->ind_th = k;
2193         else {
2194             if (op->ind_et_inst)
2195                 pr2serr("%s: unable to find element type '%s%d'\n", __func__,
2196                         op->ind_etp->abbrev, op->ind_et_inst);
2197             else
2198                 pr2serr("%s: unable to find element type '%s'\n", __func__,
2199                         op->ind_etp->abbrev);
2200             ret = -1;
2201             goto the_end;
2202         }
2203     }
2204     ret = sum_type_dheaders;
2205     goto the_end;
2206 
2207 p_truncated:
2208     pr2serr("%s: config too short\n", __func__);
2209     ret = -1;
2210 
2211 the_end:
2212     if (0 == ret)
2213         ++type_desc_hdr_count;
2214     return ret;
2215 }
2216 
2217 static char *
find_sas_connector_type(int conn_type,bool abridged,char * buff,int buff_len)2218 find_sas_connector_type(int conn_type, bool abridged, char * buff,
2219                         int buff_len)
2220 {
2221     switch (conn_type) {
2222     case 0x0:
2223         snprintf(buff, buff_len, "No information");
2224         break;
2225     case 0x1:
2226         if (abridged)
2227             snprintf(buff, buff_len, "SAS 4x");
2228         else
2229             snprintf(buff, buff_len, "SAS 4x receptacle (SFF-8470) "
2230                      "[max 4 phys]");
2231         break;
2232     case 0x2:
2233         if (abridged)
2234             snprintf(buff, buff_len, "Mini SAS 4x");
2235         else
2236             snprintf(buff, buff_len, "Mini SAS 4x receptacle (SFF-8088) "
2237                      "[max 4 phys]");
2238         break;
2239     case 0x3:
2240         if (abridged)
2241             snprintf(buff, buff_len, "QSFP+");
2242         else
2243             snprintf(buff, buff_len, "QSFP+ receptacle (SFF-8436) "
2244                      "[max 4 phys]");
2245         break;
2246     case 0x4:
2247         if (abridged)
2248             snprintf(buff, buff_len, "Mini SAS 4x active");
2249         else
2250             snprintf(buff, buff_len, "Mini SAS 4x active receptacle "
2251                      "(SFF-8088) [max 4 phys]");
2252         break;
2253     case 0x5:
2254         if (abridged)
2255             snprintf(buff, buff_len, "Mini SAS HD 4x");
2256         else
2257             snprintf(buff, buff_len, "Mini SAS HD 4x receptacle (SFF-8644) "
2258                      "[max 4 phys]");
2259         break;
2260     case 0x6:
2261         if (abridged)
2262             snprintf(buff, buff_len, "Mini SAS HD 8x");
2263         else
2264             snprintf(buff, buff_len, "Mini SAS HD 8x receptacle (SFF-8644) "
2265                      "[max 8 phys]");
2266         break;
2267     case 0x7:
2268         if (abridged)
2269             snprintf(buff, buff_len, "Mini SAS HD 16x");
2270         else
2271             snprintf(buff, buff_len, "Mini SAS HD 16x receptacle (SFF-8644) "
2272                      "[max 16 phys]");
2273         break;
2274     case 0xf:
2275         snprintf(buff, buff_len, "Vendor specific");
2276         break;
2277     case 0x10:
2278         if (abridged)
2279             snprintf(buff, buff_len, "SAS 4i");
2280         else
2281             snprintf(buff, buff_len, "SAS 4i plug (SFF-8484) [max 4 phys]");
2282         break;
2283     case 0x11:
2284         if (abridged)
2285             snprintf(buff, buff_len, "Mini SAS 4i");
2286         else
2287             snprintf(buff, buff_len, "Mini SAS 4i receptacle (SFF-8087) "
2288                      "[max 4 phys]");
2289         break;
2290     case 0x12:
2291         if (abridged)
2292             snprintf(buff, buff_len, "Mini SAS HD 4i");
2293         else
2294             snprintf(buff, buff_len, "Mini SAS HD 4i receptacle (SFF-8643) "
2295                      "[max 4 phys]");
2296         break;
2297     case 0x13:
2298         if (abridged)
2299             snprintf(buff, buff_len, "Mini SAS HD 8i");
2300         else
2301             snprintf(buff, buff_len, "Mini SAS HD 8i receptacle (SFF-8643) "
2302                      "[max 8 phys]");
2303         break;
2304     case 0x14:
2305         if (abridged)
2306             snprintf(buff, buff_len, "Mini SAS HD 16i");
2307         else
2308             snprintf(buff, buff_len, "Mini SAS HD 16i receptacle (SFF-8643) "
2309                      "[max 16 phys]");
2310         break;
2311     case 0x15:
2312         if (abridged)
2313             snprintf(buff, buff_len, "SlimSAS 4i");  /* was "SAS SlimLine" */
2314         else
2315             snprintf(buff, buff_len, "SlimSAS 4i (SFF-8654) [max 4 phys]");
2316         break;
2317     case 0x16:
2318         if (abridged)
2319             snprintf(buff, buff_len, "SlimSAS 8i");  /* was "SAS SlimLine" */
2320         else
2321             snprintf(buff, buff_len, "SlimSAS 8i (SFF-8654) [max 8 phys]");
2322         break;
2323     case 0x17:
2324         if (abridged)
2325             snprintf(buff, buff_len, "SAS MiniLink 4i");
2326         else
2327             snprintf(buff, buff_len, "SAS MiniLink 4i (SFF-8612) "
2328                      "[max 4 phys]");
2329         break;
2330     case 0x18:
2331         if (abridged)
2332             snprintf(buff, buff_len, "SAS MiniLink 8i");
2333         else
2334             snprintf(buff, buff_len, "SAS MiniLink 8i (SFF-8612) "
2335                      "[max 8 phys]");
2336         break;
2337     case 0x20:
2338         if (abridged)
2339             snprintf(buff, buff_len, "SAS Drive backplane");
2340         else
2341             snprintf(buff, buff_len, "SAS Drive backplane receptacle "
2342                      "(SFF-8482) [max 2 phys]");
2343         break;
2344     case 0x21:
2345         if (abridged)
2346             snprintf(buff, buff_len, "SATA host plug");
2347         else
2348             snprintf(buff, buff_len, "SATA host plug [max 1 phy]");
2349         break;
2350     case 0x22:
2351         if (abridged)
2352             snprintf(buff, buff_len, "SAS Drive plug");
2353         else
2354             snprintf(buff, buff_len, "SAS Drive plug (SFF-8482) "
2355                      "[max 2 phys]");
2356         break;
2357     case 0x23:
2358         if (abridged)
2359             snprintf(buff, buff_len, "SATA device plug");
2360         else
2361             snprintf(buff, buff_len, "SATA device plug [max 1 phy]");
2362         break;
2363     case 0x24:
2364         if (abridged)
2365             snprintf(buff, buff_len, "Micro SAS receptacle");
2366         else
2367             snprintf(buff, buff_len, "Micro SAS receptacle [max 2 phys]");
2368         break;
2369     case 0x25:
2370         if (abridged)
2371             snprintf(buff, buff_len, "Micro SATA device plug");
2372         else
2373             snprintf(buff, buff_len, "Micro SATA device plug [max 1 phy]");
2374         break;
2375     case 0x26:
2376         if (abridged)
2377             snprintf(buff, buff_len, "Micro SAS plug");
2378         else
2379             snprintf(buff, buff_len, "Micro SAS plug (SFF-8486) [max 2 "
2380                      "phys]");
2381         break;
2382     case 0x27:
2383         if (abridged)
2384             snprintf(buff, buff_len, "Micro SAS/SATA plug");
2385         else
2386             snprintf(buff, buff_len, "Micro SAS/SATA plug (SFF-8486) "
2387                      "[max 2 phys]");
2388         break;
2389     case 0x28:
2390         if (abridged)
2391             snprintf(buff, buff_len, "12 Gb/s SAS drive backplane");
2392         else
2393             snprintf(buff, buff_len, "12 Gb/s SAS drive backplane receptacle "
2394                      "(SFF-8680) [max 2 phys]");
2395         break;
2396     case 0x29:
2397         if (abridged)
2398             snprintf(buff, buff_len, "12 Gb/s SAS drive plug");
2399         else
2400             snprintf(buff, buff_len, "12 Gb/s SAS drive plug (SFF-8680) "
2401                      "[max 2 phys]");
2402         break;
2403     case 0x2a:
2404         if (abridged)
2405             snprintf(buff, buff_len, "Multifunction 12 Gb/s 6x receptacle");
2406         else
2407             snprintf(buff, buff_len, "Multifunction 12 Gb/s 6x unshielded "
2408                      "receptacle (SFF-8639)");
2409         break;
2410     case 0x2b:
2411         if (abridged)
2412             snprintf(buff, buff_len, "Multifunction 12 Gb/s 6x plug");
2413         else
2414             snprintf(buff, buff_len, "Multifunction 12 Gb/s 6x unshielded "
2415                      "plug (SFF-8639)");
2416         break;
2417     case 0x2c:
2418         if (abridged)
2419             snprintf(buff, buff_len, "SAS MultiLink Drive backplane "
2420                      "receptacle");
2421         else
2422             snprintf(buff, buff_len, "SAS MultiLink Drive backplane "
2423                      "receptacle (SFF-8630)");
2424         break;
2425     case 0x2d:
2426         if (abridged)
2427             snprintf(buff, buff_len, "SAS MultiLink Drive backplane plug");
2428         else
2429             snprintf(buff, buff_len, "SAS MultiLink Drive backplane plug "
2430                      "(SFF-8630)");
2431         break;
2432     case 0x2e:
2433         if (abridged)
2434             snprintf(buff, buff_len, "Reserved");
2435         else
2436             snprintf(buff, buff_len, "Reserved for internal connectors to "
2437                      "end device");
2438         break;
2439     case 0x2f:
2440         if (abridged)
2441             snprintf(buff, buff_len, "SAS virtual connector");
2442         else
2443             snprintf(buff, buff_len, "SAS virtual connector [max 1 phy]");
2444         break;
2445     case 0x3f:
2446         if (abridged)
2447             snprintf(buff, buff_len, "VS internal connector");
2448         else
2449             snprintf(buff, buff_len, "Vendor specific internal connector");
2450         break;
2451     case 0x40:
2452         if (abridged)
2453             snprintf(buff, buff_len, "SAS high density drive backplane "
2454                      "receptacle");
2455         else
2456             snprintf(buff, buff_len, "SAS high density drive backplane "
2457                      "receptacle (SFF-8631) [max 8 phys]");
2458         break;
2459     case 0x41:
2460         if (abridged)
2461             snprintf(buff, buff_len, "SAS high density drive backplane "
2462                      "plug");
2463         else
2464             snprintf(buff, buff_len, "SAS high density drive backplane "
2465                      "plug (SFF-8631) [max 8 phys]");
2466         break;
2467     default:
2468         if (conn_type < 0x10)
2469             snprintf(buff, buff_len, "unknown external connector type: 0x%x",
2470                      conn_type);
2471         else if (conn_type < 0x20)
2472             snprintf(buff, buff_len, "unknown internal wide connector type: "
2473                      "0x%x", conn_type);
2474         else if (conn_type < 0x3f)
2475             snprintf(buff, buff_len, "reserved for internal connector, "
2476                      "type: 0x%x", conn_type);
2477         else if (conn_type < 0x70)
2478             snprintf(buff, buff_len, "reserved connector type: 0x%x",
2479                      conn_type);
2480         else if (conn_type < 0x80)
2481             snprintf(buff, buff_len, "vendor specific connector type: 0x%x",
2482                      conn_type);
2483         else    /* conn_type is a 7 bit field, so this is impossible */
2484             snprintf(buff, buff_len, "unexpected connector type: 0x%x",
2485                      conn_type);
2486         break;
2487     }
2488     return buff;
2489 }
2490 
2491 /* 'Fan speed factor' new in ses4r04 */
2492 static int
calc_fan_speed(int fan_speed_factor,int actual_fan_speed)2493 calc_fan_speed(int fan_speed_factor, int actual_fan_speed)
2494 {
2495     switch (fan_speed_factor) {
2496     case 0:
2497         return actual_fan_speed * 10;
2498     case 1:
2499         return (actual_fan_speed * 10) + 20480;
2500     case 2:
2501         return actual_fan_speed * 100;
2502     default:
2503         break;
2504     }
2505     return -1;        /* something is wrong */
2506 }
2507 
2508 static const char * elem_status_code_desc[] = {
2509     "Unsupported", "OK", "Critical", "Noncritical",
2510     "Unrecoverable", "Not installed", "Unknown", "Not available",
2511     "No access allowed", "reserved [9]", "reserved [10]", "reserved [11]",
2512     "reserved [12]", "reserved [13]", "reserved [14]", "reserved [15]",
2513 };
2514 
2515 static const char * actual_speed_desc[] = {
2516     "stopped", "at lowest speed", "at second lowest speed",
2517     "at third lowest speed", "at intermediate speed",
2518     "at third highest speed", "at second highest speed", "at highest speed"
2519 };
2520 
2521 static const char * nv_cache_unit[] = {
2522     "Bytes", "KiB", "MiB", "GiB"
2523 };
2524 
2525 static const char * invop_type_desc[] = {
2526     "SEND DIAGNOSTIC page code error", "SEND DIAGNOSTIC page format error",
2527     "Reserved", "Vendor specific error"
2528 };
2529 
2530 static void
enc_status_helper(const char * pad,const uint8_t * statp,int etype,bool abridged,const struct opts_t * op)2531 enc_status_helper(const char * pad, const uint8_t * statp, int etype,
2532                   bool abridged, const struct opts_t * op)
2533 {
2534     int res, a, b, ct, bblen;
2535     bool nofilter = ! op->do_filter;
2536     char bb[128];
2537 
2538 
2539     if (op->inner_hex) {
2540         printf("%s%02x %02x %02x %02x\n", pad, statp[0], statp[1], statp[2],
2541                statp[3]);
2542         return;
2543     }
2544     if (! abridged)
2545         printf("%sPredicted failure=%d, Disabled=%d, Swap=%d, status: %s\n",
2546                pad, !!(statp[0] & 0x40), !!(statp[0] & 0x20),
2547                !!(statp[0] & 0x10), elem_status_code_desc[statp[0] & 0xf]);
2548     switch (etype) { /* element types */
2549     case UNSPECIFIED_ETC:
2550         if (op->verbose)
2551             printf("%sstatus in hex: %02x %02x %02x %02x\n",
2552                    pad, statp[0], statp[1], statp[2], statp[3]);
2553         break;
2554     case DEVICE_ETC:
2555         if (ARRAY_STATUS_DPC == op->page_code) {  /* obsolete after SES-1 */
2556             if (nofilter || (0xf0 & statp[1]))
2557                 printf("%sOK=%d, Reserved device=%d, Hot spare=%d, Cons "
2558                        "check=%d\n", pad, !!(statp[1] & 0x80),
2559                        !!(statp[1] & 0x40), !!(statp[1] & 0x20),
2560                        !!(statp[1] & 0x10));
2561             if (nofilter || (0xf & statp[1]))
2562                 printf("%sIn crit array=%d, In failed array=%d, Rebuild/"
2563                        "remap=%d, R/R abort=%d\n", pad, !!(statp[1] & 0x8),
2564                        !!(statp[1] & 0x4), !!(statp[1] & 0x2),
2565                        !!(statp[1] & 0x1));
2566             if (nofilter || ((0x46 & statp[2]) || (0x8 & statp[3])))
2567                 printf("%sDo not remove=%d, RMV=%d, Ident=%d, Enable bypass "
2568                        "A=%d\n", pad, !!(statp[2] & 0x40), !!(statp[2] & 0x4),
2569                        !!(statp[2] & 0x2), !!(statp[3] & 0x8));
2570             if (nofilter || (0x7 & statp[3]))
2571                 printf("%sEnable bypass B=%d, Bypass A enabled=%d, Bypass B "
2572                         "enabled=%d\n", pad, !!(statp[3] & 0x4),
2573                        !!(statp[3] & 0x2), !!(statp[3] & 0x1));
2574             break;
2575         }
2576         printf("%sSlot address: %d\n", pad, statp[1]);
2577         if (nofilter || (0xe0 & statp[2]))
2578             printf("%sApp client bypassed A=%d, Do not remove=%d, Enc "
2579                    "bypassed A=%d\n", pad, !!(statp[2] & 0x80),
2580                    !!(statp[2] & 0x40), !!(statp[2] & 0x20));
2581         if (nofilter || (0x1c & statp[2]))
2582             printf("%sEnc bypassed B=%d, Ready to insert=%d, RMV=%d, Ident="
2583                    "%d\n", pad, !!(statp[2] & 0x10), !!(statp[2] & 0x8),
2584                    !!(statp[2] & 0x4), !!(statp[2] & 0x2));
2585         if (nofilter || ((1 & statp[2]) || (0xe0 & statp[3])))
2586             printf("%sReport=%d, App client bypassed B=%d, Fault sensed=%d, "
2587                    "Fault requested=%d\n", pad, !!(statp[2] & 0x1),
2588                    !!(statp[3] & 0x80), !!(statp[3] & 0x40),
2589                    !!(statp[3] & 0x20));
2590         if (nofilter || (0x1e & statp[3]))
2591             printf("%sDevice off=%d, Bypassed A=%d, Bypassed B=%d, Device "
2592                    "bypassed A=%d\n", pad, !!(statp[3] & 0x10),
2593                    !!(statp[3] & 0x8), !!(statp[3] & 0x4), !!(statp[3] & 0x2));
2594         if (nofilter || (0x1 & statp[3]))
2595             printf("%sDevice bypassed B=%d\n", pad, !!(statp[3] & 0x1));
2596         break;
2597     case POWER_SUPPLY_ETC:
2598         if (nofilter || ((0xc0 & statp[1]) || (0xc & statp[2]))) {
2599             printf("%sIdent=%d, Do not remove=%d, DC overvoltage=%d, "
2600                    "DC undervoltage=%d\n", pad, !!(statp[1] & 0x80),
2601                    !!(statp[1] & 0x40), !!(statp[2] & 0x8),
2602                    !!(statp[2] & 0x4));
2603         }
2604         if (nofilter || ((0x2 & statp[2]) || (0xf0 & statp[3])))
2605             printf("%sDC overcurrent=%d, Hot swap=%d, Fail=%d, Requested "
2606                    "on=%d, Off=%d\n", pad, !!(statp[2] & 0x2),
2607                    !!(statp[3] & 0x80), !!(statp[3] & 0x40),
2608                    !!(statp[3] & 0x20), !!(statp[3] & 0x10));
2609         if (nofilter || (0xf & statp[3]))
2610             printf("%sOvertmp fail=%d, Temperature warn=%d, AC fail=%d, "
2611                    "DC fail=%d\n", pad, !!(statp[3] & 0x8),
2612                    !!(statp[3] & 0x4), !!(statp[3] & 0x2),
2613                    !!(statp[3] & 0x1));
2614         break;
2615     case COOLING_ETC:
2616         if (nofilter || ((0xc0 & statp[1]) || (0xf0 & statp[3])))
2617             printf("%sIdent=%d, Do not remove=%d, Hot swap=%d, Fail=%d, "
2618                    "Requested on=%d\n", pad, !!(statp[1] & 0x80),
2619                    !!(statp[1] & 0x40), !!(statp[3] & 0x80),
2620                    !!(statp[3] & 0x40), !!(statp[3] & 0x20));
2621         printf("%sOff=%d, Actual speed=%d rpm, Fan %s\n", pad,
2622                !!(statp[3] & 0x10),
2623                calc_fan_speed((statp[1] >> 3) & 0x3,
2624                               ((0x7 & statp[1]) << 8) + statp[2]),
2625                actual_speed_desc[7 & statp[3]]);
2626         if (op->verbose > 1)    /* show real field values */
2627             printf("%s  [Fan_speed_factor=%d, Actual_fan_speed=%d]\n",
2628                    pad, (statp[1] >> 3) & 0x3,
2629                    ((0x7 & statp[1]) << 8) + statp[2]);
2630         break;
2631     case TEMPERATURE_ETC:     /* temperature sensor */
2632         if (nofilter || ((0xc0 & statp[1]) || (0xf & statp[3]))) {
2633             printf("%sIdent=%d, Fail=%d, OT failure=%d, OT warning=%d, "
2634                    "UT failure=%d\n", pad, !!(statp[1] & 0x80),
2635                    !!(statp[1] & 0x40), !!(statp[3] & 0x8),
2636                    !!(statp[3] & 0x4), !!(statp[3] & 0x2));
2637             printf("%sUT warning=%d\n", pad, !!(statp[3] & 0x1));
2638         }
2639         if (statp[2])
2640             printf("%sTemperature=%d C\n", pad,
2641                    (int)statp[2] - TEMPERAT_OFF);
2642         else
2643             printf("%sTemperature: <reserved>\n", pad);
2644         break;
2645     case DOOR_ETC:      /* OPEN field added in ses3r05 */
2646         if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[3])))
2647             printf("%sIdent=%d, Fail=%d, Open=%d, Unlock=%d\n", pad,
2648                    !!(statp[1] & 0x80), !!(statp[1] & 0x40),
2649                    !!(statp[3] & 0x2), !!(statp[3] & 0x1));
2650         break;
2651     case AUD_ALARM_ETC:     /* audible alarm */
2652         if (nofilter || ((0xc0 & statp[1]) || (0xd0 & statp[3])))
2653             printf("%sIdent=%d, Fail=%d, Request mute=%d, Mute=%d, "
2654                    "Remind=%d\n", pad, !!(statp[1] & 0x80),
2655                    !!(statp[1] & 0x40), !!(statp[3] & 0x80),
2656                    !!(statp[3] & 0x40), !!(statp[3] & 0x10));
2657         if (nofilter || (0xf & statp[3]))
2658             printf("%sTone indicator: Info=%d, Non-crit=%d, Crit=%d, "
2659                    "Unrecov=%d\n", pad, !!(statp[3] & 0x8), !!(statp[3] & 0x4),
2660                    !!(statp[3] & 0x2), !!(statp[3] & 0x1));
2661         break;
2662     case ENC_SCELECTR_ETC: /* enclosure services controller electronics */
2663         if (nofilter || (0xe0 & statp[1]) || (0x1 & statp[2]) ||
2664             (0x80 & statp[3]))
2665             printf("%sIdent=%d, Fail=%d, Do not remove=%d, Report=%d, "
2666                    "Hot swap=%d\n", pad, !!(statp[1] & 0x80),
2667                    !!(statp[1] & 0x40), !!(statp[1] & 0x20),
2668                    !!(statp[2] & 0x1), !!(statp[3] & 0x80));
2669         break;
2670     case SCC_CELECTR_ETC:     /* SCC controller electronics */
2671         if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[2])))
2672             printf("%sIdent=%d, Fail=%d, Report=%d\n", pad,
2673                    !!(statp[1] & 0x80), !!(statp[1] & 0x40),
2674                    !!(statp[2] & 0x1));
2675         break;
2676     case NV_CACHE_ETC:     /* Non volatile cache */
2677         res = sg_get_unaligned_be16(statp + 2);
2678         printf("%sIdent=%d, Fail=%d, Size multiplier=%d, Non volatile cache "
2679                "size=0x%x\n", pad, !!(statp[1] & 0x80), !!(statp[1] & 0x40),
2680                (statp[1] & 0x3), res);
2681         printf("%sHence non volatile cache size: %d %s\n", pad, res,
2682                nv_cache_unit[statp[1] & 0x3]);
2683         break;
2684     case INV_OP_REASON_ETC:   /* Invalid operation reason */
2685         res = ((statp[1] >> 6) & 3);
2686         printf("%sInvop type=%d   %s\n", pad, res, invop_type_desc[res]);
2687         switch (res) {
2688         case 0:
2689             printf("%sPage not supported=%d\n", pad, (statp[1] & 1));
2690             break;
2691         case 1:
2692             printf("%sByte offset=%d, bit number=%d\n", pad,
2693                    sg_get_unaligned_be16(statp + 2), (statp[1] & 7));
2694             break;
2695         case 2:
2696         case 3:
2697             printf("%slast 3 bytes (hex): %02x %02x %02x\n", pad, statp[1],
2698                    statp[2], statp[3]);
2699             break;
2700         }
2701         break;
2702     case UI_POWER_SUPPLY_ETC:   /* Uninterruptible power supply */
2703         if (0 == statp[1])
2704             printf("%sBattery status: discharged or unknown\n", pad);
2705         else if (255 == statp[1])
2706             printf("%sBattery status: 255 or more minutes remaining\n", pad);
2707         else
2708             printf("%sBattery status: %d minutes remaining\n", pad, statp[1]);
2709         if (nofilter || (0xf8 & statp[2]))
2710             printf("%sAC low=%d, AC high=%d, AC qual=%d, AC fail=%d, DC fail="
2711                    "%d\n", pad, !!(statp[2] & 0x80), !!(statp[2] & 0x40),
2712                    !!(statp[2] & 0x20), !!(statp[2] & 0x10),
2713                    !!(statp[2] & 0x8));
2714         if (nofilter || ((0x7 & statp[2]) || (0xe3 & statp[3]))) {
2715             printf("%sUPS fail=%d, Warn=%d, Intf fail=%d, Ident=%d, Fail=%d, "
2716                    "Do not remove=%d\n", pad, !!(statp[2] & 0x4),
2717                    !!(statp[2] & 0x2), !!(statp[2] & 0x1),
2718                    !!(statp[3] & 0x80), !!(statp[3] & 0x40),
2719                    !!(statp[3] & 0x20));
2720             printf("%sBatt fail=%d, BPF=%d\n", pad, !!(statp[3] & 0x2),
2721                    !!(statp[3] & 0x1));
2722         }
2723         break;
2724     case DISPLAY_ETC:   /* Display (ses2r15) */
2725         if (nofilter || (0xc0 & statp[1])) {
2726             int dms = statp[1] & 0x3;
2727 
2728             printf("%sIdent=%d, Fail=%d, Display mode status=%d", pad,
2729                    !!(statp[1] & 0x80), !!(statp[1] & 0x40), dms);
2730             if ((1 == dms) || (2 == dms)) {
2731                 uint16_t dcs = sg_get_unaligned_be16(statp + 2);
2732 
2733                 printf(", Display character status=0x%x", dcs);
2734                 if (statp[2] && (0 == statp[3]))
2735                     printf(" ['%c']", statp[2]);
2736             }
2737             printf("\n");
2738         }
2739         break;
2740     case KEY_PAD_ETC:   /* Key pad entry */
2741         if (nofilter || (0xc0 & statp[1]))
2742             printf("%sIdent=%d, Fail=%d\n", pad, !!(statp[1] & 0x80),
2743                    !!(statp[1] & 0x40));
2744         break;
2745     case ENCLOSURE_ETC:
2746         a = ((statp[2] >> 2) & 0x3f);
2747         if (nofilter || ((0x80 & statp[1]) || a || (0x2 & statp[2])))
2748             printf("%sIdent=%d, Time until power cycle=%d, "
2749                    "Failure indication=%d\n", pad, !!(statp[1] & 0x80),
2750                    a, !!(statp[2] & 0x2));
2751         b = ((statp[3] >> 2) & 0x3f);
2752         if (nofilter || (0x1 & statp[2]) || a || b)
2753             printf("%sWarning indication=%d, Requested power off "
2754                    "duration=%d\n", pad, !!(statp[2] & 0x1), b);
2755         if (nofilter || (0x3 & statp[3]))
2756             printf("%sFailure requested=%d, Warning requested=%d\n",
2757                    pad, !!(statp[3] & 0x2), !!(statp[3] & 0x1));
2758         break;
2759     case SCSI_PORT_TRAN_ETC:   /* SCSI port/transceiver */
2760         if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[2]) ||
2761                            (0x13 & statp[3])))
2762             printf("%sIdent=%d, Fail=%d, Report=%d, Disabled=%d, Loss of "
2763                    "link=%d, Xmit fail=%d\n", pad, !!(statp[1] & 0x80),
2764                    !!(statp[1] & 0x40), !!(statp[2] & 0x1),
2765                    !!(statp[3] & 0x10), !!(statp[3] & 0x2),
2766                    !!(statp[3] & 0x1));
2767         break;
2768     case LANGUAGE_ETC:
2769         printf("%sIdent=%d, Language code: %.2s\n", pad, !!(statp[1] & 0x80),
2770                statp + 2);
2771         break;
2772     case COMM_PORT_ETC:   /* Communication port */
2773         if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[3])))
2774             printf("%sIdent=%d, Fail=%d, Disabled=%d\n", pad,
2775                    !!(statp[1] & 0x80), !!(statp[1] & 0x40),
2776                    !!(statp[3] & 0x1));
2777         break;
2778     case VOLT_SENSOR_ETC:   /* Voltage sensor */
2779         if (nofilter || (0xcf & statp[1])) {
2780             printf("%sIdent=%d, Fail=%d,  Warn Over=%d, Warn Under=%d, "
2781                    "Crit Over=%d\n", pad, !!(statp[1] & 0x80),
2782                    !!(statp[1] & 0x40), !!(statp[1] & 0x8),
2783                    !!(statp[1] & 0x4), !!(statp[1] & 0x2));
2784             printf("%sCrit Under=%d\n", pad, !!(statp[1] & 0x1));
2785         }
2786 #ifdef SG_LIB_MINGW
2787         printf("%sVoltage: %g volts\n", pad,
2788                ((int)(short)sg_get_unaligned_be16(statp + 2) / 100.0));
2789 #else
2790         printf("%sVoltage: %.2f volts\n", pad,
2791                ((int)(short)sg_get_unaligned_be16(statp + 2) / 100.0));
2792 #endif
2793         break;
2794     case CURR_SENSOR_ETC:   /* Current sensor */
2795         if (nofilter || (0xca & statp[1]))
2796             printf("%sIdent=%d, Fail=%d, Warn Over=%d, Crit Over=%d\n",
2797                     pad, !!(statp[1] & 0x80), !!(statp[1] & 0x40),
2798                     !!(statp[1] & 0x8), !!(statp[1] & 0x2));
2799 #ifdef SG_LIB_MINGW
2800         printf("%sCurrent: %g amps\n", pad,
2801                ((int)(short)sg_get_unaligned_be16(statp + 2) / 100.0));
2802 #else
2803         printf("%sCurrent: %.2f amps\n", pad,
2804                ((int)(short)sg_get_unaligned_be16(statp + 2) / 100.0));
2805 #endif
2806         break;
2807     case SCSI_TPORT_ETC:   /* SCSI target port */
2808         if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[2]) ||
2809                            (0x1 & statp[3])))
2810             printf("%sIdent=%d, Fail=%d, Report=%d, Enabled=%d\n", pad,
2811                    !!(statp[1] & 0x80), !!(statp[1] & 0x40),
2812                    !!(statp[2] & 0x1), !!(statp[3] & 0x1));
2813         break;
2814     case SCSI_IPORT_ETC:   /* SCSI initiator port */
2815         if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[2]) ||
2816                            (0x1 & statp[3])))
2817             printf("%sIdent=%d, Fail=%d, Report=%d, Enabled=%d\n", pad,
2818                    !!(statp[1] & 0x80), !!(statp[1] & 0x40),
2819                    !!(statp[2] & 0x1), !!(statp[3] & 0x1));
2820         break;
2821     case SIMPLE_SUBENC_ETC:   /* Simple subenclosure */
2822         printf("%sIdent=%d, Fail=%d, Short enclosure status: 0x%x\n", pad,
2823                !!(statp[1] & 0x80), !!(statp[1] & 0x40), statp[3]);
2824         break;
2825     case ARRAY_DEV_ETC:   /* Array device */
2826         if (nofilter || (0xf0 & statp[1]))
2827             printf("%sOK=%d, Reserved device=%d, Hot spare=%d, Cons check="
2828                    "%d\n", pad, !!(statp[1] & 0x80), !!(statp[1] & 0x40),
2829                    !!(statp[1] & 0x20), !!(statp[1] & 0x10));
2830         if (nofilter || (0xf & statp[1]))
2831             printf("%sIn crit array=%d, In failed array=%d, Rebuild/remap=%d"
2832                    ", R/R abort=%d\n", pad, !!(statp[1] & 0x8),
2833                    !!(statp[1] & 0x4), !!(statp[1] & 0x2),
2834                    !!(statp[1] & 0x1));
2835         if (nofilter || (0xf0 & statp[2]))
2836             printf("%sApp client bypass A=%d, Do not remove=%d, Enc bypass "
2837                    "A=%d, Enc bypass B=%d\n", pad, !!(statp[2] & 0x80),
2838                    !!(statp[2] & 0x40), !!(statp[2] & 0x20),
2839                    !!(statp[2] & 0x10));
2840         if (nofilter || (0xf & statp[2]))
2841             printf("%sReady to insert=%d, RMV=%d, Ident=%d, Report=%d\n",
2842                    pad, !!(statp[2] & 0x8), !!(statp[2] & 0x4),
2843                    !!(statp[2] & 0x2), !!(statp[2] & 0x1));
2844         if (nofilter || (0xf0 & statp[3]))
2845             printf("%sApp client bypass B=%d, Fault sensed=%d, Fault reqstd="
2846                    "%d, Device off=%d\n", pad, !!(statp[3] & 0x80),
2847                    !!(statp[3] & 0x40), !!(statp[3] & 0x20),
2848                    !!(statp[3] & 0x10));
2849         if (nofilter || (0xf & statp[3]))
2850             printf("%sBypassed A=%d, Bypassed B=%d, Dev bypassed A=%d, "
2851                    "Dev bypassed B=%d\n",
2852                    pad, !!(statp[3] & 0x8), !!(statp[3] & 0x4),
2853                    !!(statp[3] & 0x2), !!(statp[3] & 0x1));
2854         break;
2855     case SAS_EXPANDER_ETC:
2856         printf("%sIdent=%d, Fail=%d\n", pad, !!(statp[1] & 0x80),
2857                !!(statp[1] & 0x40));
2858         break;
2859     case SAS_CONNECTOR_ETC:     /* OC (overcurrent) added in ses3r07 */
2860         ct = (statp[1] & 0x7f);
2861         bblen = sizeof(bb);
2862         if (abridged)
2863             printf("%s%s, pl=%d", pad,
2864                    find_sas_connector_type(ct, true, bb, bblen), statp[2]);
2865         else {
2866             printf("%sIdent=%d, %s\n", pad, !!(statp[1] & 0x80),
2867                    find_sas_connector_type(ct, false, bb, bblen));
2868             /* Mated added in ses3r10 */
2869             printf("%sConnector physical link=0x%x, Mated=%d, Fail=%d, "
2870                    "OC=%d\n", pad, statp[2], !!(statp[3] & 0x80),
2871                    !!(statp[3] & 0x40), !!(statp[3] & 0x20));
2872         }
2873         break;
2874     default:
2875         if (etype < 0x80)
2876             printf("%sUnknown element type, status in hex: %02x %02x %02x "
2877                    "%02x\n", pad, statp[0], statp[1], statp[2], statp[3]);
2878         else
2879             printf("%sVendor specific element type, status in hex: %02x "
2880                    "%02x %02x %02x\n", pad, statp[0], statp[1], statp[2],
2881                    statp[3]);
2882         break;
2883     }
2884 }
2885 
2886 /* ENC_STATUS_DPC [0x2]
2887  * Display enclosure status diagnostic page. */
2888 static void
enc_status_dp(const struct th_es_t * tesp,uint32_t ref_gen_code,const uint8_t * resp,int resp_len,const struct opts_t * op)2889 enc_status_dp(const struct th_es_t * tesp, uint32_t ref_gen_code,
2890               const uint8_t * resp, int resp_len,
2891               const struct opts_t * op)
2892 {
2893     int j, k;
2894     uint32_t gen_code;
2895     bool got1, match_ind_th;
2896     const uint8_t * bp;
2897     const uint8_t * last_bp;
2898     const struct type_desc_hdr_t * tdhp = tesp->th_base;
2899     char b[64];
2900 
2901     printf("Enclosure Status diagnostic page:\n");
2902     if (resp_len < 4)
2903         goto truncated;
2904     printf("  INVOP=%d, INFO=%d, NON-CRIT=%d, CRIT=%d, UNRECOV=%d\n",
2905            !!(resp[1] & 0x10), !!(resp[1] & 0x8), !!(resp[1] & 0x4),
2906            !!(resp[1] & 0x2), !!(resp[1] & 0x1));
2907     last_bp = resp + resp_len - 1;
2908     if (resp_len < 8)
2909         goto truncated;
2910     gen_code = sg_get_unaligned_be32(resp + 4);
2911     printf("  generation code: 0x%x\n", gen_code);
2912     if (ref_gen_code != gen_code) {
2913         pr2serr("  <<state of enclosure changed, please try again>>\n");
2914         return;
2915     }
2916     printf("  status descriptor list\n");
2917     bp = resp + 8;
2918     for (k = 0, got1 = false; k < tesp->num_ths; ++k, ++tdhp) {
2919         if ((bp + 3) > last_bp)
2920             goto truncated;
2921         match_ind_th = (op->ind_given && (k == op->ind_th));
2922         if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
2923             printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
2924                    etype_str(tdhp->etype, b, sizeof(b)), tdhp->se_id, k);
2925             printf("      Overall descriptor:\n");
2926             enc_status_helper("        ", bp, tdhp->etype, false, op);
2927             got1 = true;
2928         }
2929         for (bp += 4, j = 0; j < tdhp->num_elements; ++j, bp += 4) {
2930             if (op->ind_given) {
2931                 if ((! match_ind_th) || (-1 == op->ind_indiv) ||
2932                     (! match_ind_indiv(j, op)))
2933                     continue;
2934             }
2935             printf("      Element %d descriptor:\n", j);
2936             enc_status_helper("        ", bp, tdhp->etype, false, op);
2937             got1 = true;
2938         }
2939     }
2940     if (op->ind_given && (! got1)) {
2941         printf("      >>> no match on --index=%d,%d", op->ind_th,
2942                op->ind_indiv);
2943         if (op->ind_indiv_last > op->ind_indiv)
2944             printf("-%d\n", op->ind_indiv_last);
2945         else
2946             printf("\n");
2947     }
2948     return;
2949 truncated:
2950     pr2serr("    <<<enc: response too short>>>\n");
2951     return;
2952 }
2953 
2954 /* ARRAY_STATUS_DPC [0x6]
2955  * Display array status diagnostic page. */
2956 static void
array_status_dp(const struct th_es_t * tesp,uint32_t ref_gen_code,const uint8_t * resp,int resp_len,const struct opts_t * op)2957 array_status_dp(const struct th_es_t * tesp, uint32_t ref_gen_code,
2958                 const uint8_t * resp, int resp_len,
2959                 const struct opts_t * op)
2960 {
2961     int j, k;
2962     uint32_t gen_code;
2963     bool got1, match_ind_th;
2964     const uint8_t * bp;
2965     const uint8_t * last_bp;
2966     const struct type_desc_hdr_t * tdhp = tesp->th_base;
2967     char b[64];
2968 
2969     printf("Array Status diagnostic page:\n");
2970     if (resp_len < 4)
2971         goto truncated;
2972     printf("  INVOP=%d, INFO=%d, NON-CRIT=%d, CRIT=%d, UNRECOV=%d\n",
2973            !!(resp[1] & 0x10), !!(resp[1] & 0x8), !!(resp[1] & 0x4),
2974            !!(resp[1] & 0x2), !!(resp[1] & 0x1));
2975     last_bp = resp + resp_len - 1;
2976     if (resp_len < 8)
2977         goto truncated;
2978     gen_code = sg_get_unaligned_be32(resp + 4);
2979     printf("  generation code: 0x%x\n", gen_code);
2980     if (ref_gen_code != gen_code) {
2981         pr2serr("  <<state of enclosure changed, please try again>>\n");
2982         return;
2983     }
2984     printf("  status descriptor list\n");
2985     bp = resp + 8;
2986     for (k = 0, got1 = false; k < tesp->num_ths; ++k, ++tdhp) {
2987         if ((bp + 3) > last_bp)
2988             goto truncated;
2989         match_ind_th = (op->ind_given && (k == op->ind_th));
2990         if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
2991             printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
2992                    etype_str(tdhp->etype, b, sizeof(b)), tdhp->se_id, k);
2993             printf("      Overall descriptor:\n");
2994             enc_status_helper("        ", bp, tdhp->etype, false, op);
2995             got1 = true;
2996         }
2997         for (bp += 4, j = 0; j < tdhp->num_elements; ++j, bp += 4) {
2998             if (op->ind_given) {
2999                 if ((! match_ind_th) || (-1 == op->ind_indiv) ||
3000                     (! match_ind_indiv(j, op)))
3001                     continue;
3002             }
3003             printf("      Element %d descriptor:\n", j);
3004             enc_status_helper("        ", bp, tdhp->etype, false, op);
3005             got1 = true;
3006         }
3007     }
3008     if (op->ind_given && (! got1)) {
3009         printf("      >>> no match on --index=%d,%d", op->ind_th,
3010                op->ind_indiv);
3011         if (op->ind_indiv_last > op->ind_indiv)
3012             printf("-%d\n", op->ind_indiv_last);
3013         else
3014             printf("\n");
3015     }
3016     return;
3017 truncated:
3018     pr2serr("    <<<arr: response too short>>>\n");
3019     return;
3020 }
3021 
3022 static char *
reserved_or_num(char * buff,int buff_len,int num,int reserve_num)3023 reserved_or_num(char * buff, int buff_len, int num, int reserve_num)
3024 {
3025     if (num == reserve_num)
3026         strncpy(buff, "<res>", buff_len);
3027     else
3028         snprintf(buff, buff_len, "%d", num);
3029     if (buff_len > 0)
3030         buff[buff_len - 1] = '\0';
3031     return buff;
3032 }
3033 
3034 static void
threshold_helper(const char * header,const char * pad,const uint8_t * tp,int etype,const struct opts_t * op)3035 threshold_helper(const char * header, const char * pad,
3036                  const uint8_t *tp, int etype,
3037                  const struct opts_t * op)
3038 {
3039     char b[128];
3040     char b2[128];
3041 
3042     if (op->inner_hex) {
3043         if (header)
3044             printf("%s", header);
3045         printf("%s%02x %02x %02x %02x\n", pad, tp[0], tp[1], tp[2], tp[3]);
3046         return;
3047     }
3048     switch (etype) {
3049     case 0x4:  /*temperature */
3050         if (header)
3051             printf("%s", header);
3052         printf("%shigh critical=%s, high warning=%s", pad,
3053                reserved_or_num(b, 128, tp[0] - TEMPERAT_OFF, -TEMPERAT_OFF),
3054                reserved_or_num(b2, 128, tp[1] - TEMPERAT_OFF, -TEMPERAT_OFF));
3055         if (op->do_filter && (0 == tp[2]) && (0 == tp[3])) {
3056             printf(" (in Celsius)\n");
3057             break;
3058         }
3059         printf("\n%slow warning=%s, low critical=%s (in Celsius)\n", pad,
3060                reserved_or_num(b, 128, tp[2] - TEMPERAT_OFF, -TEMPERAT_OFF),
3061                reserved_or_num(b2, 128, tp[3] - TEMPERAT_OFF, -TEMPERAT_OFF));
3062         break;
3063     case 0xb:  /* UPS */
3064         if (header)
3065             printf("%s", header);
3066         if (0 == tp[2])
3067             strcpy(b, "<vendor>");
3068         else
3069             snprintf(b, sizeof(b), "%d", tp[2]);
3070         printf("%slow warning=%s, ", pad, b);
3071         if (0 == tp[3])
3072             strcpy(b, "<vendor>");
3073         else
3074             snprintf(b, sizeof(b), "%d", tp[3]);
3075         printf("low critical=%s (in minutes)\n", b);
3076         break;
3077     case 0x12: /* voltage */
3078         if (header)
3079             printf("%s", header);
3080 #ifdef SG_LIB_MINGW
3081         printf("%shigh critical=%g %%, high warning=%g %% (above nominal "
3082                "voltage)\n", pad, 0.5 * tp[0], 0.5 * tp[1]);
3083         printf("%slow warning=%g %%, low critical=%g %% (below nominal "
3084                "voltage)\n", pad, 0.5 * tp[2], 0.5 * tp[3]);
3085 #else
3086         printf("%shigh critical=%.1f %%, high warning=%.1f %% (above nominal "
3087                "voltage)\n", pad, 0.5 * tp[0], 0.5 * tp[1]);
3088         printf("%slow warning=%.1f %%, low critical=%.1f %% (below nominal "
3089                "voltage)\n", pad, 0.5 * tp[2], 0.5 * tp[3]);
3090 #endif
3091         break;
3092     case 0x13: /* current */
3093         if (header)
3094             printf("%s", header);
3095 #ifdef SG_LIB_MINGW
3096         printf("%shigh critical=%g %%, high warning=%g %%", pad,
3097                0.5 * tp[0], 0.5 * tp[1]);
3098 #else
3099         printf("%shigh critical=%.1f %%, high warning=%.1f %%", pad,
3100                0.5 * tp[0], 0.5 * tp[1]);
3101 #endif
3102         printf(" (above nominal current)\n");
3103         break;
3104     default:
3105         if (op->verbose) {
3106             if (header)
3107                 printf("%s", header);
3108             printf("%s<< no thresholds for this element type >>\n", pad);
3109         }
3110         break;
3111     }
3112 }
3113 
3114 /* THRESHOLD_DPC [0x5] */
3115 static void
threshold_sdg(const struct th_es_t * tesp,uint32_t ref_gen_code,const uint8_t * resp,int resp_len,const struct opts_t * op)3116 threshold_sdg(const struct th_es_t * tesp, uint32_t ref_gen_code,
3117               const uint8_t * resp, int resp_len,
3118               const struct opts_t * op)
3119 {
3120     int j, k;
3121     uint32_t gen_code;
3122     bool got1, match_ind_th;
3123     const uint8_t * bp;
3124     const uint8_t * last_bp;
3125     const struct type_desc_hdr_t * tdhp = tesp->th_base;
3126     char b[64];
3127 
3128     printf("Threshold In diagnostic page:\n");
3129     if (resp_len < 4)
3130         goto truncated;
3131     printf("  INVOP=%d\n", !!(resp[1] & 0x10));
3132     last_bp = resp + resp_len - 1;
3133     if (resp_len < 8)
3134         goto truncated;
3135     gen_code = sg_get_unaligned_be32(resp + 4);
3136     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
3137     if (ref_gen_code != gen_code) {
3138         pr2serr("  <<state of enclosure changed, please try again>>\n");
3139         return;
3140     }
3141     printf("  Threshold status descriptor list\n");
3142     bp = resp + 8;
3143     for (k = 0, got1 = false; k < tesp->num_ths; ++k, ++tdhp) {
3144         if ((bp + 3) > last_bp)
3145             goto truncated;
3146         match_ind_th = (op->ind_given && (k == op->ind_th));
3147         if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
3148             printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
3149                    etype_str(tdhp->etype, b, sizeof(b)), tdhp->se_id, k);
3150             threshold_helper("      Overall descriptor:\n", "        ", bp,
3151                              tdhp->etype, op);
3152             got1 = true;
3153         }
3154         for (bp += 4, j = 0; j < tdhp->num_elements; ++j, bp += 4) {
3155             if (op->ind_given) {
3156                 if ((! match_ind_th) || (-1 == op->ind_indiv) ||
3157                     (! match_ind_indiv(j, op)))
3158                     continue;
3159             }
3160             snprintf(b, sizeof(b), "      Element %d descriptor:\n", j);
3161             threshold_helper(b, "        ", bp, tdhp->etype, op);
3162             got1 = true;
3163         }
3164     }
3165     if (op->ind_given && (! got1)) {
3166         printf("      >>> no match on --index=%d,%d", op->ind_th,
3167                op->ind_indiv);
3168         if (op->ind_indiv_last > op->ind_indiv)
3169             printf("-%d\n", op->ind_indiv_last);
3170         else
3171             printf("\n");
3172     }
3173     return;
3174 truncated:
3175     pr2serr("    <<<thresh: response too short>>>\n");
3176     return;
3177 }
3178 
3179 /* ELEM_DESC_DPC [0x7]
3180  * This page essentially contains names of overall and individual
3181  * elements. */
3182 static void
element_desc_sdg(const struct th_es_t * tesp,uint32_t ref_gen_code,const uint8_t * resp,int resp_len,const struct opts_t * op)3183 element_desc_sdg(const struct th_es_t * tesp, uint32_t ref_gen_code,
3184                  const uint8_t * resp, int resp_len,
3185                  const struct opts_t * op)
3186 {
3187     int j, k, desc_len;
3188     uint32_t gen_code;
3189     bool got1, match_ind_th;
3190     const uint8_t * bp;
3191     const uint8_t * last_bp;
3192     const struct type_desc_hdr_t * tp;
3193     char b[64];
3194 
3195     printf("Element Descriptor In diagnostic page:\n");
3196     if (resp_len < 4)
3197         goto truncated;
3198     last_bp = resp + resp_len - 1;
3199     if (resp_len < 8)
3200         goto truncated;
3201     gen_code = sg_get_unaligned_be32(resp + 4);
3202     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
3203     if (ref_gen_code != gen_code) {
3204         pr2serr("  <<state of enclosure changed, please try again>>\n");
3205         return;
3206     }
3207     printf("  element descriptor list (grouped by type):\n");
3208     bp = resp + 8;
3209     got1 = false;
3210     for (k = 0, tp = tesp->th_base; k < tesp->num_ths; ++k, ++tp) {
3211         if ((bp + 3) > last_bp)
3212             goto truncated;
3213         desc_len = sg_get_unaligned_be16(bp + 2) + 4;
3214         match_ind_th = (op->ind_given && (k == op->ind_th));
3215         if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
3216             printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
3217                    etype_str(tp->etype, b, sizeof(b)), tp->se_id, k);
3218             if (desc_len > 4)
3219                 printf("      Overall descriptor: %.*s\n", desc_len - 4,
3220                        bp + 4);
3221             else
3222                 printf("      Overall descriptor: <empty>\n");
3223             got1 = true;
3224         }
3225         for (bp += desc_len, j = 0; j < tp->num_elements;
3226              ++j, bp += desc_len) {
3227             desc_len = sg_get_unaligned_be16(bp + 2) + 4;
3228             if (op->ind_given) {
3229                 if ((! match_ind_th) || (-1 == op->ind_indiv) ||
3230                     (! match_ind_indiv(j, op)))
3231                     continue;
3232             }
3233             if (desc_len > 4)
3234                 printf("      Element %d descriptor: %.*s\n", j,
3235                        desc_len - 4, bp + 4);
3236             else
3237                 printf("      Element %d descriptor: <empty>\n", j);
3238             got1 = true;
3239         }
3240     }
3241     if (op->ind_given && (! got1)) {
3242         printf("      >>> no match on --index=%d,%d", op->ind_th,
3243                op->ind_indiv);
3244         if (op->ind_indiv_last > op->ind_indiv)
3245             printf("-%d\n", op->ind_indiv_last);
3246         else
3247             printf("\n");
3248     }
3249     return;
3250 truncated:
3251     pr2serr("    <<<element: response too short>>>\n");
3252     return;
3253 }
3254 
3255 static bool
saddr_non_zero(const uint8_t * bp)3256 saddr_non_zero(const uint8_t * bp)
3257 {
3258     return ! sg_all_zeros(bp, 8);
3259 }
3260 
3261 static const char * sas_device_type[] = {
3262     "no SAS device attached",   /* but might be SATA device */
3263     "end device",
3264     "expander device",  /* in SAS-1.1 this was a "edge expander device */
3265     "expander device (fanout, SAS-1.1)",  /* marked obsolete in SAS-2 */
3266     "reserved [4]", "reserved [5]", "reserved [6]", "reserved [7]"
3267 };
3268 
3269 static void
additional_elem_sas(const char * pad,const uint8_t * ae_bp,int etype,const struct th_es_t * tesp,const struct opts_t * op)3270 additional_elem_sas(const char * pad, const uint8_t * ae_bp, int etype,
3271                     const struct th_es_t * tesp, const struct opts_t * op)
3272 {
3273     int phys, j, m, n, desc_type, eiioe, eip_offset;
3274     bool nofilter = ! op->do_filter;
3275     bool eip;
3276     const struct join_row_t * jrp;
3277     const uint8_t * aep;
3278     const uint8_t * ed_bp;
3279     const char * cp;
3280     char b[64];
3281 
3282     eip = !!(0x10 & ae_bp[0]);
3283     eiioe = eip ? (0x3 & ae_bp[2]) : 0;
3284     eip_offset = eip ? 2 : 0;
3285     desc_type = (ae_bp[3 + eip_offset] >> 6) & 0x3;
3286     if (op->verbose > 1)
3287         printf("%sdescriptor_type: %d\n", pad, desc_type);
3288     if (0 == desc_type) {
3289         phys = ae_bp[2 + eip_offset];
3290         printf("%snumber of phys: %d, not all phys: %d", pad, phys,
3291                ae_bp[3 + eip_offset] & 1);
3292         if (eip_offset)
3293             printf(", device slot number: %d", ae_bp[5 + eip_offset]);
3294         printf("\n");
3295         aep = ae_bp + 4 + eip_offset + eip_offset;
3296         for (j = 0; j < phys; ++j, aep += 28) {
3297             bool print_sas_addr = false;
3298             bool saddr_nz;
3299 
3300             printf("%sphy index: %d\n", pad, j);
3301             printf("%s  SAS device type: %s\n", pad,
3302                    sas_device_type[(0x70 & aep[0]) >> 4]);
3303             if (nofilter || (0xe & aep[2]))
3304                 printf("%s  initiator port for:%s%s%s\n", pad,
3305                        ((aep[2] & 8) ? " SSP" : ""),
3306                        ((aep[2] & 4) ? " STP" : ""),
3307                        ((aep[2] & 2) ? " SMP" : ""));
3308             if (nofilter || (0x8f & aep[3]))
3309                 printf("%s  target port for:%s%s%s%s%s\n", pad,
3310                        ((aep[3] & 0x80) ? " SATA_port_selector" : ""),
3311                        ((aep[3] & 8) ? " SSP" : ""),
3312                        ((aep[3] & 4) ? " STP" : ""),
3313                        ((aep[3] & 2) ? " SMP" : ""),
3314                        ((aep[3] & 1) ? " SATA_device" : ""));
3315             saddr_nz = saddr_non_zero(aep + 4);
3316             if (nofilter || saddr_nz) {
3317                 print_sas_addr = true;
3318                 printf("%s  attached SAS address: 0x", pad);
3319                 if (saddr_nz) {
3320                     for (m = 0; m < 8; ++m)
3321                         printf("%02x", aep[4 + m]);
3322                 } else
3323                     printf("0");
3324             }
3325             saddr_nz = saddr_non_zero(aep + 12);
3326             if (nofilter || saddr_nz) {
3327                 print_sas_addr = true;
3328                 printf("\n%s  SAS address: 0x", pad);
3329                 if (saddr_nz) {
3330                     for (m = 0; m < 8; ++m)
3331                         printf("%02x", aep[12 + m]);
3332                 } else
3333                     printf("0");
3334             }
3335             if (print_sas_addr)
3336                 printf("\n%s  phy identifier: 0x%x\n", pad, aep[20]);
3337         }
3338     } else if (1 == desc_type) {
3339         phys = ae_bp[2 + eip_offset];
3340         if (SAS_EXPANDER_ETC == etype) {
3341             printf("%snumber of phys: %d\n", pad, phys);
3342             printf("%sSAS address: 0x", pad);
3343             for (m = 0; m < 8; ++m)
3344                 printf("%02x", ae_bp[6 + eip_offset + m]);
3345             printf("\n%sAttached connector; other_element pairs:\n", pad);
3346             aep = ae_bp + 14 + eip_offset;
3347             for (j = 0; j < phys; ++j, aep += 2) {
3348                 printf("%s  [%d] ", pad, j);
3349                 m = aep[0];     /* connector element index */
3350                 if (0xff == m)
3351                     printf("no connector");
3352                 else {
3353                     if (tesp->j_base) {
3354                         if (0 == eiioe)
3355                             jrp = find_join_row_cnst(tesp, m, FJ_SAS_CON);
3356                         else if ((1 == eiioe) || (3 == eiioe))
3357                             jrp = find_join_row_cnst(tesp, m, FJ_IOE);
3358                         else
3359                             jrp = find_join_row_cnst(tesp, m, FJ_EOE);
3360                         if ((NULL == jrp) || (NULL == jrp->enc_statp) ||
3361                             (SAS_CONNECTOR_ETC != jrp->etype))
3362                             printf("broken [conn_idx=%d]", m);
3363                         else {
3364                             enc_status_helper("", jrp->enc_statp, jrp->etype,
3365                                               true, op);
3366                             printf(" [%d]", jrp->indiv_i);
3367                         }
3368                     } else
3369                         printf("connector ei: %d", m);
3370                 }
3371                 m = aep[1];     /* other element index */
3372                 if (0xff != m) {
3373                     printf("; ");
3374                     if (tesp->j_base) {
3375 
3376                         if (0 == eiioe)
3377                             jrp = find_join_row_cnst(tesp, m, FJ_AESS);
3378                         else if ((1 == eiioe) || (3 == eiioe))
3379                             jrp = find_join_row_cnst(tesp, m, FJ_IOE);
3380                         else
3381                             jrp = find_join_row_cnst(tesp, m, FJ_EOE);
3382                         if (NULL == jrp)
3383                             printf("broken [oth_elem_idx=%d]", m);
3384                         else if (jrp->elem_descp) {
3385                             cp = etype_str(jrp->etype, b, sizeof(b));
3386                             ed_bp = jrp->elem_descp;
3387                             n = sg_get_unaligned_be16(ed_bp + 2);
3388                             if (n > 0)
3389                                 printf("%.*s [%d,%d] etype: %s", n,
3390                                        (const char *)(ed_bp + 4),
3391                                        jrp->th_i, jrp->indiv_i, cp);
3392                             else
3393                                 printf("[%d,%d] etype: %s", jrp->th_i,
3394                                        jrp->indiv_i, cp);
3395                         } else {
3396                             cp = etype_str(jrp->etype, b, sizeof(b));
3397                             printf("[%d,%d] etype: %s", jrp->th_i,
3398                                    jrp->indiv_i, cp);
3399                         }
3400                     } else
3401                         printf("other ei: %d", m);
3402                 }
3403                 printf("\n");
3404             }
3405         } else if ((SCSI_TPORT_ETC == etype) ||
3406                    (SCSI_IPORT_ETC == etype) ||
3407                    (ENC_SCELECTR_ETC == etype)) {
3408             printf("%snumber of phys: %d\n", pad, phys);
3409             aep = ae_bp + 6 + eip_offset;
3410             for (j = 0; j < phys; ++j, aep += 12) {
3411                 printf("%sphy index: %d\n", pad, j);
3412                 printf("%s  phy_id: 0x%x\n", pad, aep[0]);
3413                 printf("%s  ", pad);
3414                 m = aep[2];     /* connector element index */
3415                 if (0xff == m)
3416                     printf("no connector");
3417                 else {
3418                     if (tesp->j_base) {
3419                         if (0 == eiioe)
3420                             jrp = find_join_row_cnst(tesp, m, FJ_SAS_CON);
3421                         else if ((1 == eiioe) || (3 == eiioe))
3422                             jrp = find_join_row_cnst(tesp, m, FJ_IOE);
3423                         else
3424                             jrp = find_join_row_cnst(tesp, m, FJ_EOE);
3425                         if ((NULL == jrp) || (NULL == jrp->enc_statp) ||
3426                             (SAS_CONNECTOR_ETC != jrp->etype))
3427                             printf("broken [conn_idx=%d]", m);
3428                         else {
3429                             enc_status_helper("", jrp->enc_statp, jrp->etype,
3430                                               true, op);
3431                             printf(" [%d]", jrp->indiv_i);
3432                         }
3433                     } else
3434                         printf("connector ei: %d", m);
3435                 }
3436                 m = aep[3];     /* other element index */
3437                 if (0xff != m) {
3438                     printf("; ");
3439                     if (tesp->j_base) {
3440                         if (0 == eiioe)
3441                             jrp = find_join_row_cnst(tesp, m, FJ_AESS);
3442                         else if ((1 == eiioe) || (3 == eiioe))
3443                             jrp = find_join_row_cnst(tesp, m, FJ_IOE);
3444                         else
3445                             jrp = find_join_row_cnst(tesp, m, FJ_EOE);
3446                         if (NULL == jrp)
3447                             printf("broken [oth_elem_idx=%d]", m);
3448                         else if (jrp->elem_descp) {
3449                             cp = etype_str(jrp->etype, b, sizeof(b));
3450                             ed_bp = jrp->elem_descp;
3451                             n = sg_get_unaligned_be16(ed_bp + 2);
3452                             if (n > 0)
3453                                 printf("%.*s [%d,%d] etype: %s", n,
3454                                        (const char *)(ed_bp + 4),
3455                                        jrp->th_i, jrp->indiv_i, cp);
3456                             else
3457                                 printf("[%d,%d] etype: %s", jrp->th_i,
3458                                        jrp->indiv_i, cp);
3459                         } else {
3460                             cp = etype_str(jrp->etype, b, sizeof(b));
3461                             printf("[%d,%d] etype: %s", jrp->th_i,
3462                                    jrp->indiv_i, cp);
3463                         }
3464                     } else
3465                         printf("other ei: %d", m);
3466                 }
3467                 printf("\n");
3468                 printf("%s  SAS address: 0x", pad);
3469                 for (m = 0; m < 8; ++m)
3470                     printf("%02x", aep[4 + m]);
3471                 printf("\n");
3472             }   /* end_for: loop over phys in SCSI initiator, target */
3473         } else
3474             printf("%sunrecognised element type [%d] for desc_type "
3475                    "1\n", pad, etype);
3476     } else
3477         printf("%sunrecognised descriptor type [%d]\n", pad, desc_type);
3478 }
3479 
3480 static void
additional_elem_helper(const char * pad,const uint8_t * ae_bp,int len,int etype,const struct th_es_t * tesp,const struct opts_t * op)3481 additional_elem_helper(const char * pad, const uint8_t * ae_bp,
3482                        int len, int etype, const struct th_es_t * tesp,
3483                        const struct opts_t * op)
3484 {
3485     int ports, phys, j, m, eip_offset, pcie_pt;
3486     bool eip;
3487     uint16_t pcie_vid;
3488     const uint8_t * aep;
3489     char b[64];
3490 
3491     if (op->inner_hex) {
3492         for (j = 0; j < len; ++j) {
3493             if (0 == (j % 16))
3494                 printf("%s%s", ((0 == j) ? "" : "\n"), pad);
3495             printf("%02x ", ae_bp[j]);
3496         }
3497         printf("\n");
3498         return;
3499     }
3500     eip = !!(0x10 & ae_bp[0]);
3501     eip_offset = eip ? 2 : 0;
3502     switch (0xf & ae_bp[0]) {     /* switch on protocol identifier */
3503     case TPROTO_FCP:
3504         printf("%sTransport protocol: FCP\n", pad);
3505         if (len < (12 + eip_offset))
3506             break;
3507         ports = ae_bp[2 + eip_offset];
3508         printf("%snumber of ports: %d\n", pad, ports);
3509         printf("%snode_name: ", pad);
3510         for (m = 0; m < 8; ++m)
3511             printf("%02x", ae_bp[6 + eip_offset + m]);
3512         if (eip_offset)
3513             printf(", device slot number: %d", ae_bp[5 + eip_offset]);
3514         printf("\n");
3515         aep = ae_bp + 14 + eip_offset;
3516         for (j = 0; j < ports; ++j, aep += 16) {
3517             printf("%s  port index: %d, port loop position: %d, port "
3518                    "bypass reason: 0x%x\n", pad, j, aep[0], aep[1]);
3519             printf("%srequested hard address: %d, n_port identifier: "
3520                    "%02x%02x%02x\n", pad, aep[4], aep[5],
3521                    aep[6], aep[7]);
3522             printf("%s  n_port name: ", pad);
3523             for (m = 0; m < 8; ++m)
3524                 printf("%02x", aep[8 + m]);
3525             printf("\n");
3526         }
3527         break;
3528     case TPROTO_SAS:
3529         printf("%sTransport protocol: SAS\n", pad);
3530         if (len < (4 + eip_offset))
3531             break;
3532         additional_elem_sas(pad, ae_bp, etype, tesp, op);
3533         break;
3534     case TPROTO_PCIE: /* added in ses3r08; contains little endian fields */
3535         printf("%sTransport protocol: PCIe\n", pad);
3536         if (0 == eip_offset) {
3537             printf("%sfor this protocol EIP must be set (it isn't)\n", pad);
3538             break;
3539         }
3540         if (len < 6)
3541             break;
3542         pcie_pt = (ae_bp[5] >> 5) & 0x7;
3543         if (TPROTO_PCIE_PS_NVME == pcie_pt)
3544             printf("%sPCIe protocol type: NVMe\n", pad);
3545         else {  /* no others currently defined */
3546             printf("%sTransport protocol: PCIe subprotocol=0x%x not "
3547                    "decoded\n", pad, pcie_pt);
3548             if (op->verbose)
3549                 hex2stdout(ae_bp, len, 0);
3550             break;
3551         }
3552         phys = ae_bp[4];
3553         printf("%snumber of ports: %d, not all ports: %d", pad, phys,
3554                ae_bp[5] & 1);
3555         printf(", device slot number: %d\n", ae_bp[7]);
3556 
3557         pcie_vid = sg_get_unaligned_le16(ae_bp + 10);   /* N.B. LE */
3558         printf("%sPCIe vendor id: 0x%" PRIx16 "%s\n", pad, pcie_vid,
3559                (0xffff == pcie_vid) ? " (not reported)" : "");
3560         printf("%sserial number: %.20s\n", pad, ae_bp + 12);
3561         printf("%smodel number: %.40s\n", pad, ae_bp + 32);
3562         aep = ae_bp + 72;
3563         for (j = 0; j < phys; ++j, aep += 8) {
3564             bool psn_valid = !!(0x4 & aep[0]);
3565             bool bdf_valid = !!(0x2 & aep[0]);
3566             bool cid_valid = !!(0x1 & aep[0]);
3567 
3568             printf("%sport index: %d\n", pad, j);
3569             printf("%s  PSN_VALID=%d, BDF_VALID=%d, CID_VALID=%d\n", pad,
3570                    (int)psn_valid, (int)bdf_valid, (int)cid_valid);
3571             if (cid_valid)      /* N.B. little endian */
3572                 printf("%s  controller id: 0x%" PRIx16 "\n", pad,
3573                        sg_get_unaligned_le16(aep + 1)); /* N.B. LEndian */
3574             if (bdf_valid)
3575                 printf("%s  bus number: 0x%x, device number: 0x%x, "
3576                        "function number: 0x%x\n", pad, aep[4],
3577                        (aep[5] >> 3) & 0x1f, 0x7 & aep[5]);
3578             if (psn_valid)      /* little endian, top 3 bits assumed zero */
3579                 printf("%s  physical slot number: 0x%" PRIx16 "\n", pad,
3580                        0x1fff & sg_get_unaligned_le16(aep + 6)); /* N.B. LE */
3581         }
3582         break;
3583     default:
3584         printf("%sTransport protocol: %s not decoded\n", pad,
3585                sg_get_trans_proto_str((0xf & ae_bp[0]), sizeof(b), b));
3586         if (op->verbose)
3587             hex2stdout(ae_bp, len, 0);
3588         break;
3589     }
3590 }
3591 
3592 /* ADD_ELEM_STATUS_DPC [0xa] Additional Element Status dpage
3593  * Previously called "Device element status descriptor". Changed "device"
3594  * to "additional" to allow for SAS expander and SATA devices */
3595 static void
additional_elem_sdg(const struct th_es_t * tesp,uint32_t ref_gen_code,const uint8_t * resp,int resp_len,const struct opts_t * op)3596 additional_elem_sdg(const struct th_es_t * tesp, uint32_t ref_gen_code,
3597                     const uint8_t * resp, int resp_len,
3598                     const struct opts_t * op)
3599 {
3600     int j, k, desc_len, etype, el_num, ind, elem_count, ei, eiioe, num_elems;
3601     int fake_ei;
3602     uint32_t gen_code;
3603     bool eip, invalid, match_ind_th, my_eiioe_force, skip;
3604     const uint8_t * bp;
3605     const uint8_t * last_bp;
3606     const struct type_desc_hdr_t * tp = tesp->th_base;
3607     char b[64];
3608 
3609     printf("Additional element status diagnostic page:\n");
3610     if (resp_len < 4)
3611         goto truncated;
3612     last_bp = resp + resp_len - 1;
3613     gen_code = sg_get_unaligned_be32(resp + 4);
3614     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
3615     if (ref_gen_code != gen_code) {
3616         pr2serr("  <<state of enclosure changed, please try again>>\n");
3617         return;
3618     }
3619     printf("  additional element status descriptor list\n");
3620     bp = resp + 8;
3621     my_eiioe_force = op->eiioe_force;
3622     for (k = 0, elem_count = 0; k < tesp->num_ths; ++k, ++tp) {
3623         fake_ei = -1;
3624         etype = tp->etype;
3625         num_elems = tp->num_elements;
3626         if (! is_et_used_by_aes(etype)) {
3627             elem_count += num_elems;
3628             continue;   /* skip if not element type of interest */
3629         }
3630         if ((bp + 1) > last_bp)
3631             goto truncated;
3632 
3633         eip = !! (bp[0] & 0x10);
3634         if (eip) { /* do bounds check on the element index */
3635             ei = bp[3];
3636             skip = false;
3637             if ((0 == k) && op->eiioe_auto && (1 == ei)) {
3638                 /* heuristic: if first AES descriptor has EIP set and its
3639                  * element index equal to 1, then act as if the EIIOE field
3640                  * is one. */
3641                 my_eiioe_force = true;
3642             }
3643             eiioe = (0x3 & bp[2]);
3644             if (my_eiioe_force && (0 == eiioe))
3645                 eiioe = 1;
3646             if (1 == eiioe) {
3647                 if ((ei < (elem_count + k)) ||
3648                     (ei > (elem_count + k + num_elems))) {
3649                     elem_count += num_elems;
3650                     skip = true;
3651                 }
3652             } else {
3653                 if ((ei < elem_count) || (ei > elem_count + num_elems)) {
3654                     if ((0 == ei) && (TPROTO_SAS == (0xf & bp[0])) &&
3655                         (1 == (bp[5] >> 6))) {
3656                         /* heuristic (hack) for Areca 8028 */
3657                         fake_ei = elem_count;
3658                         if (op->verbose > 2)
3659                             pr2serr("%s: hack, bad ei=%d, fake_ei=%d\n",
3660                                     __func__, ei, fake_ei);
3661                         ei = fake_ei;
3662                     } else {
3663                         elem_count += num_elems;
3664                         skip = true;
3665                     }
3666                 }
3667             }
3668             if (skip) {
3669                 if (op->verbose > 2)
3670                     pr2serr("skipping etype=0x%x, k=%d due to "
3671                             "element_index=%d bounds\n  effective eiioe=%d, "
3672                             "elem_count=%d, num_elems=%d\n", etype, k,
3673                             ei, eiioe, elem_count, num_elems);
3674                 continue;
3675             }
3676         }
3677         match_ind_th = (op->ind_given && (k == op->ind_th));
3678         if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
3679             printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
3680                    etype_str(etype, b, sizeof(b)), tp->se_id, k);
3681         }
3682         el_num = 0;
3683         for (j = 0; j < num_elems; ++j, bp += desc_len, ++el_num) {
3684             invalid = !!(bp[0] & 0x80);
3685             desc_len = bp[1] + 2;
3686             eip = !!(bp[0] & 0x10);
3687             eiioe = eip ? (0x3 & bp[2]) : 0;
3688             if (fake_ei >= 0)
3689                 ind = fake_ei;
3690             else
3691                 ind = eip ? bp[3] : el_num;
3692             if (op->ind_given) {
3693                 if ((! match_ind_th) || (-1 == op->ind_indiv) ||
3694                     (! match_ind_indiv(el_num, op)))
3695                     continue;
3696             }
3697             if (eip)
3698                 printf("      Element index: %d  eiioe=%d%s\n", ind, eiioe,
3699                        (((0 != eiioe) && my_eiioe_force) ?
3700                         " but overridden" : ""));
3701             else
3702                 printf("      Element %d descriptor\n", ind);
3703             if (invalid && (! op->inner_hex))
3704                 printf("        flagged as invalid (no further "
3705                        "information)\n");
3706             else
3707                 additional_elem_helper("        ", bp, desc_len, etype,
3708                                        tesp, op);
3709         }
3710         elem_count += tp->num_elements;
3711     }           /* end_for: loop over type descriptor headers */
3712     return;
3713 truncated:
3714     pr2serr("    <<<additional: response too short>>>\n");
3715     return;
3716 }
3717 
3718 /* SUBENC_HELP_TEXT_DPC [0xb] */
3719 static void
subenc_help_sdg(const uint8_t * resp,int resp_len)3720 subenc_help_sdg(const uint8_t * resp, int resp_len)
3721 {
3722     int k, el, num_subs;
3723     uint32_t gen_code;
3724     const uint8_t * bp;
3725     const uint8_t * last_bp;
3726 
3727     printf("Subenclosure help text diagnostic page:\n");
3728     if (resp_len < 4)
3729         goto truncated;
3730     num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
3731     last_bp = resp + resp_len - 1;
3732     printf("  number of secondary subenclosures: %d\n", num_subs - 1);
3733     gen_code = sg_get_unaligned_be32(resp + 4);
3734     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
3735     bp = resp + 8;
3736     for (k = 0; k < num_subs; ++k, bp += el) {
3737         if ((bp + 3) > last_bp)
3738             goto truncated;
3739         el = sg_get_unaligned_be16(bp + 2) + 4;
3740         printf("   subenclosure identifier: %d\n", bp[1]);
3741         if (el > 4)
3742             printf("    %.*s\n", el - 4, bp + 4);
3743         else
3744             printf("    <empty>\n");
3745     }
3746     return;
3747 truncated:
3748     pr2serr("    <<<subenc: response too short>>>\n");
3749     return;
3750 }
3751 
3752 /* SUBENC_STRING_DPC [0xc] */
3753 static void
subenc_string_sdg(const uint8_t * resp,int resp_len)3754 subenc_string_sdg(const uint8_t * resp, int resp_len)
3755 {
3756     int k, el, num_subs;
3757     uint32_t gen_code;
3758     const uint8_t * bp;
3759     const uint8_t * last_bp;
3760 
3761     printf("Subenclosure string in diagnostic page:\n");
3762     if (resp_len < 4)
3763         goto truncated;
3764     num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
3765     last_bp = resp + resp_len - 1;
3766     printf("  number of secondary subenclosures: %d\n", num_subs - 1);
3767     gen_code = sg_get_unaligned_be32(resp + 4);
3768     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
3769     bp = resp + 8;
3770     for (k = 0; k < num_subs; ++k, bp += el) {
3771         if ((bp + 3) > last_bp)
3772             goto truncated;
3773         el = sg_get_unaligned_be16(bp + 2) + 4;
3774         printf("   subenclosure identifier: %d\n", bp[1]);
3775         if (el > 4) {
3776             char bb[1024];
3777 
3778             hex2str(bp + 40, el - 40, "    ", 0, sizeof(bb), bb);
3779             printf("%s\n", bb);
3780         } else
3781             printf("    <empty>\n");
3782     }
3783     return;
3784 truncated:
3785     pr2serr("    <<<subence str: response too short>>>\n");
3786     return;
3787 }
3788 
3789 /* SUBENC_NICKNAME_DPC [0xf] */
3790 static void
subenc_nickname_sdg(const uint8_t * resp,int resp_len)3791 subenc_nickname_sdg(const uint8_t * resp, int resp_len)
3792 {
3793     int k, el, num_subs;
3794     uint32_t gen_code;
3795     const uint8_t * bp;
3796     const uint8_t * last_bp;
3797 
3798     printf("Subenclosure nickname status diagnostic page:\n");
3799     if (resp_len < 4)
3800         goto truncated;
3801     num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
3802     last_bp = resp + resp_len - 1;
3803     printf("  number of secondary subenclosures: %d\n", num_subs - 1);
3804     gen_code = sg_get_unaligned_be32(resp + 4);
3805     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
3806     bp = resp + 8;
3807     el = 40;
3808     for (k = 0; k < num_subs; ++k, bp += el) {
3809         if ((bp + el - 1) > last_bp)
3810             goto truncated;
3811         printf("   subenclosure identifier: %d\n", bp[1]);
3812         printf("   nickname status: 0x%x\n", bp[2]);
3813         printf("   nickname additional status: 0x%x\n", bp[3]);
3814         printf("   nickname language code: %.2s\n", bp + 6);
3815         printf("   nickname: %.*s\n", 32, bp + 8);
3816     }
3817     return;
3818 truncated:
3819     pr2serr("    <<<subence str: response too short>>>\n");
3820     return;
3821 }
3822 
3823 /* SUPPORTED_SES_DPC [0xd] */
3824 static void
supported_pages_sdg(const char * leadin,const uint8_t * resp,int resp_len)3825 supported_pages_sdg(const char * leadin, const uint8_t * resp,
3826                     int resp_len)
3827 {
3828     int k, code, prev;
3829     bool got1;
3830     const struct diag_page_abbrev * ap;
3831 
3832     printf("%s:\n", leadin);
3833     for (k = 0, prev = 0; k < (resp_len - 4); ++k, prev = code) {
3834         const char * cp;
3835 
3836         code = resp[k + 4];
3837         if (code < prev)
3838             break;      /* assume to be padding at end */
3839         cp = find_diag_page_desc(code);
3840         if (cp) {
3841             printf("  %s [", cp);
3842             for (ap = dp_abbrev, got1 = false; ap->abbrev; ++ap) {
3843                 if (ap->page_code == code) {
3844                     printf("%s%s", (got1 ? "," : ""), ap->abbrev);
3845                     got1 = true;
3846                 }
3847             }
3848             printf("] [0x%x]\n", code);
3849         } else
3850             printf("  <unknown> [0x%x]\n", code);
3851     }
3852 }
3853 
3854 /* An array of Download microcode status field values and descriptions */
3855 static struct diag_page_code mc_status_arr[] = {
3856     {0x0, "No download microcode operation in progress"},
3857     {0x1, "Download in progress, awaiting more"},
3858     {0x2, "Download complete, updating non-volatile storage"},
3859     {0x3, "Updating non-volatile storage with deferred microcode"},
3860     {0x10, "Complete, no error, starting now"},
3861     {0x11, "Complete, no error, start after hard reset or power cycle"},
3862     {0x12, "Complete, no error, start after power cycle"},
3863     {0x13, "Complete, no error, start after activate_mc, hard reset or "
3864            "power cycle"},
3865     {0x80, "Error, discarded, see additional status"},
3866     {0x81, "Error, discarded, image error"},
3867     {0x82, "Timeout, discarded"},
3868     {0x83, "Internal error, need new microcode before reset"},
3869     {0x84, "Internal error, need new microcode, reset safe"},
3870     {0x85, "Unexpected activate_mc received"},
3871     {0x1000, NULL},
3872 };
3873 
3874 static const char *
get_mc_status(uint8_t status_val)3875 get_mc_status(uint8_t status_val)
3876 {
3877     const struct diag_page_code * mcsp;
3878 
3879     for (mcsp = mc_status_arr; mcsp->desc; ++mcsp) {
3880         if (status_val == mcsp->page_code)
3881             return mcsp->desc;
3882     }
3883     return "";
3884 }
3885 
3886 /* DOWNLOAD_MICROCODE_DPC [0xe] */
3887 static void
download_code_sdg(const uint8_t * resp,int resp_len)3888 download_code_sdg(const uint8_t * resp, int resp_len)
3889 {
3890     int k, num_subs;
3891     uint32_t gen_code;
3892     const uint8_t * bp;
3893     const uint8_t * last_bp;
3894     const char * cp;
3895 
3896     printf("Download microcode status diagnostic page:\n");
3897     if (resp_len < 4)
3898         goto truncated;
3899     num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
3900     last_bp = resp + resp_len - 1;
3901     printf("  number of secondary subenclosures: %d\n", num_subs - 1);
3902     gen_code = sg_get_unaligned_be32(resp + 4);
3903     printf("  generation code: 0x%" PRIx32 "\n", gen_code);
3904     bp = resp + 8;
3905     for (k = 0; k < num_subs; ++k, bp += 16) {
3906         if ((bp + 3) > last_bp)
3907             goto truncated;
3908         cp = (0 == bp[1]) ? " [primary]" : "";
3909         printf("   subenclosure identifier: %d%s\n", bp[1], cp);
3910         cp = get_mc_status(bp[2]);
3911         if (strlen(cp) > 0) {
3912             printf("     download microcode status: %s [0x%x]\n", cp, bp[2]);
3913             printf("     download microcode additional status: 0x%x\n",
3914                    bp[3]);
3915         } else
3916             printf("     download microcode status: 0x%x [additional "
3917                    "status: 0x%x]\n", bp[2], bp[3]);
3918         printf("     download microcode maximum size: %d bytes\n",
3919                sg_get_unaligned_be32(bp + 4));
3920         printf("     download microcode expected buffer id: 0x%x\n", bp[11]);
3921         printf("     download microcode expected buffer id offset: %d\n",
3922                sg_get_unaligned_be32(bp + 12));
3923     }
3924     return;
3925 truncated:
3926     pr2serr("    <<<download: response too short>>>\n");
3927     return;
3928 }
3929 
3930 /* Reads hex data from command line, stdin or a file when in_hex is true.
3931  * Reads binary from stdin or file when in_hex is false. Returns 0 on
3932  * success, 1 otherwise. If inp is a file and may_have_at, then the
3933  * first character is skipped to get filename (since it should be '@'). */
3934 static int
read_hex(const char * inp,uint8_t * arr,int mx_arr_len,int * arr_len,bool in_hex,bool may_have_at,int vb)3935 read_hex(const char * inp, uint8_t * arr, int mx_arr_len, int * arr_len,
3936          bool in_hex, bool may_have_at, int vb)
3937 {
3938     bool has_stdin, split_line;
3939     int in_len, k, j, m, off, off_fn;
3940     unsigned int h;
3941     const char * lcp;
3942     char * cp;
3943     char * c2p;
3944     char line[512];
3945     char carry_over[4];
3946     FILE * fp = NULL;
3947 
3948     if ((NULL == inp) || (NULL == arr) || (NULL == arr_len))
3949         return 1;
3950     off_fn = may_have_at ? 1 : 0;
3951     lcp = inp;
3952     in_len = strlen(inp);
3953     if (0 == in_len) {
3954         *arr_len = 0;
3955         return 0;
3956     }
3957     has_stdin = ((1 == in_len) && ('-' == inp[0]));
3958 
3959     if (! in_hex) {     /* binary, assume its not on the command line, */
3960         int fd;         /* that leaves stdin or a file (pipe) */
3961         struct stat a_stat;
3962 
3963         if (has_stdin)
3964             fd = STDIN_FILENO;
3965         else {
3966             fd = open(inp + off_fn, O_RDONLY);
3967             if (fd < 0) {
3968                 pr2serr("unable to open binary file %s: %s\n", inp + off_fn,
3969                          safe_strerror(errno));
3970                 return 1;
3971             }
3972         }
3973         k = read(fd, arr, mx_arr_len);
3974         if (k <= 0) {
3975             if (0 == k)
3976                 pr2serr("read 0 bytes from binary file %s\n", inp + off_fn);
3977             else
3978                 pr2serr("read from binary file %s: %s\n", inp + off_fn,
3979                         safe_strerror(errno));
3980             if (! has_stdin)
3981                 close(fd);
3982             return 1;
3983         }
3984         if ((0 == fstat(fd, &a_stat)) && S_ISFIFO(a_stat.st_mode)) {
3985             /* pipe; keep reading till error or 0 read */
3986             while (k < mx_arr_len) {
3987                 m = read(fd, arr + k, mx_arr_len - k);
3988                 if (0 == m)
3989                    break;
3990                 if (m < 0) {
3991                     pr2serr("read from binary pipe %s: %s\n", inp + off_fn,
3992                             safe_strerror(errno));
3993                     if (! has_stdin)
3994                         close(fd);
3995                     return 1;
3996                 }
3997                 k += m;
3998             }
3999         }
4000         *arr_len = k;
4001         if (! has_stdin)
4002             close(fd);
4003         return 0;
4004     }
4005     if (has_stdin || (! may_have_at) || ('@' == inp[0])) {
4006         /* read hex from stdin or file */
4007         if (has_stdin)
4008             fp = stdin;
4009         else {
4010             fp = fopen(inp + off_fn, "r");
4011             if (NULL == fp) {
4012                 pr2serr("%s: unable to open file: %s\n", __func__,
4013                         inp + off_fn);
4014                 return 1;
4015             }
4016         }
4017         carry_over[0] = 0;
4018         for (j = 0, off = 0; j < MX_DATA_IN_LINES; ++j) {
4019             if (NULL == fgets(line, sizeof(line), fp))
4020                 break;
4021             in_len = strlen(line);
4022             if (in_len > 0) {
4023                 if ('\n' == line[in_len - 1]) {
4024                     --in_len;
4025                     line[in_len] = '\0';
4026                     split_line = false;
4027                 } else
4028                     split_line = true;
4029             }
4030             if (in_len < 1) {
4031                 carry_over[0] = 0;
4032                 continue;
4033             }
4034             if (carry_over[0]) {
4035                 if (isxdigit((uint8_t)line[0])) {
4036                     carry_over[1] = line[0];
4037                     carry_over[2] = '\0';
4038                     if (1 == sscanf(carry_over, "%x", &h))
4039                         arr[off - 1] = h;       /* back up and overwrite */
4040                     else {
4041                         pr2serr("%s: carry_over error ['%s'] around line "
4042                                 "%d\n", __func__, carry_over, j + 1);
4043                         goto err_with_fp;
4044                     }
4045                     lcp = line + 1;
4046                     --in_len;
4047                 } else
4048                     lcp = line;
4049                 carry_over[0] = 0;
4050             } else
4051                 lcp = line;
4052             m = strspn(lcp, " \t");
4053             if (m == in_len)
4054                 continue;
4055             lcp += m;
4056             in_len -= m;
4057             if ('#' == *lcp)
4058                 continue;
4059             k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
4060             if (in_len != k) {
4061                 pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
4062                         j + 1, m + k + 1);
4063                 if (vb > 2)
4064                     pr2serr("first 40 characters of line: %.40s\n", line);
4065                 goto err_with_fp;
4066             }
4067             for (k = 0; k < (mx_arr_len - off); ++k) {
4068                 if (1 == sscanf(lcp, "%x", &h)) {
4069                     if (h > 0xff) {
4070                         pr2serr("%s: hex number larger than 0xff in line %d, "
4071                                 "pos %d\n", __func__, j + 1,
4072                                 (int)(lcp - line + 1));
4073                         if (vb > 2)
4074                             pr2serr("first 40 characters of line: %.40s\n",
4075                                     line);
4076                         goto err_with_fp;
4077                     }
4078                     if (split_line && (1 == strlen(lcp))) {
4079                         /* single trailing hex digit might be a split pair */
4080                         carry_over[0] = *lcp;
4081                     }
4082                     arr[off + k] = h;
4083                     lcp = strpbrk(lcp, " ,\t");
4084                     if (NULL == lcp)
4085                         break;
4086                     lcp += strspn(lcp, " ,\t");
4087                     if ('\0' == *lcp)
4088                         break;
4089                 } else {
4090                     pr2serr("%s: error in line %d, at pos %d\n", __func__,
4091                             j + 1, (int)(lcp - line + 1));
4092                     if (vb > 2)
4093                         pr2serr("first 40 characters of line: %.40s\n", line);
4094                     goto err_with_fp;
4095                 }
4096             }
4097             off += k + 1;
4098             if (off >= mx_arr_len)
4099                 break;
4100         }
4101         *arr_len = off;
4102     } else {        /* hex string on command line */
4103         k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
4104         if (in_len != k) {
4105             pr2serr("%s: error at pos %d\n", __func__, k + 1);
4106             goto err_with_fp;
4107         }
4108         for (k = 0; k < mx_arr_len; ++k) {
4109             if (1 == sscanf(lcp, "%x", &h)) {
4110                 if (h > 0xff) {
4111                     pr2serr("%s: hex number larger than 0xff at pos %d\n",
4112                             __func__, (int)(lcp - inp + 1));
4113                     goto err_with_fp;
4114                 }
4115                 arr[k] = h;
4116                 cp = (char *)strchr(lcp, ',');
4117                 c2p = (char *)strchr(lcp, ' ');
4118                 if (NULL == cp)
4119                     cp = c2p;
4120                 if (NULL == cp)
4121                     break;
4122                 if (c2p && (c2p < cp))
4123                     cp = c2p;
4124                 lcp = cp + 1;
4125             } else {
4126                 pr2serr("%s: error at pos %d\n", __func__,
4127                         (int)(lcp - inp + 1));
4128                 goto err_with_fp;
4129             }
4130         }
4131         *arr_len = k + 1;
4132     }
4133     if (vb > 3) {
4134         pr2serr("%s: user provided data:\n", __func__);
4135         hex2stderr(arr, *arr_len, 0);
4136     }
4137     if (fp && (fp != stdin))
4138         fclose(fp);
4139     return 0;
4140 
4141 err_with_fp:
4142     if (fp && (fp != stdin))
4143         fclose(fp);
4144     return 1;
4145 }
4146 
4147 static int
process_status_dpage(struct sg_pt_base * ptvp,int page_code,uint8_t * resp,int resp_len,struct opts_t * op)4148 process_status_dpage(struct sg_pt_base * ptvp, int page_code, uint8_t * resp,
4149                      int resp_len, struct opts_t * op)
4150 {
4151     int j, num_ths;
4152     int ret = 0;
4153     uint32_t ref_gen_code;
4154     const char * cp;
4155     struct enclosure_info primary_info;
4156     struct th_es_t tes;
4157     struct th_es_t * tesp;
4158     char bb[120];
4159 
4160     tesp = &tes;
4161     memset(tesp, 0, sizeof(tes));
4162     if ((cp = find_in_diag_page_desc(page_code)))
4163         snprintf(bb, sizeof(bb), "%s dpage", cp);
4164     else
4165         snprintf(bb, sizeof(bb), "dpage 0x%x", page_code);
4166     cp = bb;
4167     if (op->do_raw) {
4168         if (1 == op->do_raw)
4169             hex2stdout(resp + 4, resp_len - 4, -1);
4170         else {
4171             if (sg_set_binary_mode(STDOUT_FILENO) < 0)
4172                 perror("sg_set_binary_mode");
4173             dStrRaw(resp, resp_len);
4174         }
4175         goto fini;
4176     } else if (op->do_hex) {
4177         if (op->do_hex > 2) {
4178             if (op->do_hex > 3) {
4179                 if (4 == op->do_hex)
4180                     printf("\n# %s:\n", cp);
4181                 else
4182                     printf("\n# %s [0x%x]:\n", cp, page_code);
4183             }
4184             hex2stdout(resp, resp_len, -1);
4185          } else {
4186             printf("# Response in hex for %s:\n", cp);
4187             hex2stdout(resp, resp_len, (2 == op->do_hex));
4188         }
4189         goto fini;
4190     }
4191 
4192     memset(&primary_info, 0, sizeof(primary_info));
4193     switch (page_code) {
4194     case SUPPORTED_DPC:
4195         supported_pages_sdg("Supported diagnostic pages", resp, resp_len);
4196         break;
4197     case CONFIGURATION_DPC:
4198         configuration_sdg(resp, resp_len);
4199         break;
4200     case ENC_STATUS_DPC:
4201         num_ths = build_type_desc_hdr_arr(ptvp, type_desc_hdr_arr,
4202                                           MX_ELEM_HDR, &ref_gen_code,
4203                                           &primary_info, op);
4204         if (num_ths < 0) {
4205             ret = num_ths;
4206             goto fini;
4207         }
4208         if ((1 == type_desc_hdr_count) && primary_info.have_info) {
4209             printf("  Primary enclosure logical identifier (hex): ");
4210             for (j = 0; j < 8; ++j)
4211                 printf("%02x", primary_info.enc_log_id[j]);
4212             printf("\n");
4213         }
4214         tesp->th_base = type_desc_hdr_arr;
4215         tesp->num_ths = num_ths;
4216         enc_status_dp(tesp, ref_gen_code, resp, resp_len, op);
4217         break;
4218     case ARRAY_STATUS_DPC:
4219         num_ths = build_type_desc_hdr_arr(ptvp, type_desc_hdr_arr,
4220                                           MX_ELEM_HDR, &ref_gen_code,
4221                                           &primary_info, op);
4222         if (num_ths < 0) {
4223             ret = num_ths;
4224             goto fini;
4225         }
4226         if ((1 == type_desc_hdr_count) && primary_info.have_info) {
4227             printf("  Primary enclosure logical identifier (hex): ");
4228             for (j = 0; j < 8; ++j)
4229                 printf("%02x", primary_info.enc_log_id[j]);
4230             printf("\n");
4231         }
4232         tesp->th_base = type_desc_hdr_arr;
4233         tesp->num_ths = num_ths;
4234         array_status_dp(tesp, ref_gen_code, resp, resp_len, op);
4235         break;
4236     case HELP_TEXT_DPC:
4237         printf("Help text diagnostic page (for primary "
4238                "subenclosure):\n");
4239         if (resp_len > 4)
4240             printf("  %.*s\n", resp_len - 4, resp + 4);
4241         else
4242             printf("  <empty>\n");
4243         break;
4244     case STRING_DPC:
4245         printf("String In diagnostic page (for primary "
4246                "subenclosure):\n");
4247         if (resp_len > 4)
4248             hex2stdout(resp + 4, resp_len - 4, 0);
4249         else
4250             printf("  <empty>\n");
4251         break;
4252     case THRESHOLD_DPC:
4253         num_ths = build_type_desc_hdr_arr(ptvp, type_desc_hdr_arr,
4254                                           MX_ELEM_HDR, &ref_gen_code,
4255                                           &primary_info, op);
4256         if (num_ths < 0) {
4257             ret = num_ths;
4258             goto fini;
4259         }
4260         if ((1 == type_desc_hdr_count) && primary_info.have_info) {
4261             printf("  Primary enclosure logical identifier (hex): ");
4262             for (j = 0; j < 8; ++j)
4263                 printf("%02x", primary_info.enc_log_id[j]);
4264             printf("\n");
4265         }
4266         tesp->th_base = type_desc_hdr_arr;
4267         tesp->num_ths = num_ths;
4268         threshold_sdg(tesp, ref_gen_code, resp, resp_len, op);
4269         break;
4270     case ELEM_DESC_DPC:
4271         num_ths = build_type_desc_hdr_arr(ptvp, type_desc_hdr_arr,
4272                                               MX_ELEM_HDR, &ref_gen_code,
4273                                               &primary_info, op);
4274             if (num_ths < 0) {
4275             ret = num_ths;
4276             goto fini;
4277         }
4278         if ((1 == type_desc_hdr_count) && primary_info.have_info) {
4279             printf("  Primary enclosure logical identifier (hex): ");
4280             for (j = 0; j < 8; ++j)
4281                 printf("%02x", primary_info.enc_log_id[j]);
4282             printf("\n");
4283         }
4284         tesp->th_base = type_desc_hdr_arr;
4285         tesp->num_ths = num_ths;
4286         element_desc_sdg(tesp, ref_gen_code, resp, resp_len, op);
4287         break;
4288     case SHORT_ENC_STATUS_DPC:
4289         printf("Short enclosure status diagnostic page, "
4290                "status=0x%x\n", resp[1]);
4291         break;
4292     case ENC_BUSY_DPC:
4293         printf("Enclosure Busy diagnostic page, "
4294                "busy=%d [vendor specific=0x%x]\n",
4295                resp[1] & 1, (resp[1] >> 1) & 0xff);
4296         break;
4297     case ADD_ELEM_STATUS_DPC:
4298         num_ths = build_type_desc_hdr_arr(ptvp, type_desc_hdr_arr,
4299                                           MX_ELEM_HDR, &ref_gen_code,
4300                                           &primary_info, op);
4301         if (num_ths < 0) {
4302             ret = num_ths;
4303             goto fini;
4304         }
4305         if (primary_info.have_info) {
4306             printf("  Primary enclosure logical identifier (hex): ");
4307             for (j = 0; j < 8; ++j)
4308                 printf("%02x", primary_info.enc_log_id[j]);
4309             printf("\n");
4310         }
4311         tesp->th_base = type_desc_hdr_arr;
4312         tesp->num_ths = num_ths;
4313         additional_elem_sdg(tesp, ref_gen_code, resp, resp_len, op);
4314         break;
4315     case SUBENC_HELP_TEXT_DPC:
4316         subenc_help_sdg(resp, resp_len);
4317         break;
4318     case SUBENC_STRING_DPC:
4319         subenc_string_sdg(resp, resp_len);
4320         break;
4321     case SUPPORTED_SES_DPC:
4322         supported_pages_sdg("Supported SES diagnostic pages", resp,
4323                             resp_len);
4324         break;
4325     case DOWNLOAD_MICROCODE_DPC:
4326         download_code_sdg(resp, resp_len);
4327         break;
4328     case SUBENC_NICKNAME_DPC:
4329         subenc_nickname_sdg(resp, resp_len);
4330         break;
4331     default:
4332         printf("Cannot decode response from diagnostic page: %s\n", cp);
4333         hex2stdout(resp, resp_len, 0);
4334     }
4335 
4336 fini:
4337     return ret;
4338 }
4339 
4340 /* Display "status" page or pages (if op->page_code==0xff) . data-in from
4341  * SES device or user provided (with --data= option). Return 0 for success */
4342 static int
process_status_page_s(struct sg_pt_base * ptvp,struct opts_t * op)4343 process_status_page_s(struct sg_pt_base * ptvp, struct opts_t * op)
4344 {
4345     int page_code, ret, resp_len;
4346     uint8_t * resp = NULL;
4347     uint8_t * free_resp = NULL;
4348 
4349     resp = sg_memalign(op->maxlen, 0, &free_resp, false);
4350     if (NULL == resp) {
4351         pr2serr("%s: unable to allocate %d bytes on heap\n", __func__,
4352                 op->maxlen);
4353         ret = -1;
4354         goto fini;
4355     }
4356     page_code = op->page_code;
4357     if (ALL_DPC == page_code) {
4358         int k, n;
4359         uint8_t pc, prev;
4360         uint8_t supp_dpg_arr[256];
4361         const int s_arr_sz = sizeof(supp_dpg_arr);
4362 
4363         memset(supp_dpg_arr, 0, s_arr_sz);
4364         ret = do_rec_diag(ptvp, SUPPORTED_DPC, resp, op->maxlen, op,
4365                           &resp_len);
4366         if (ret)        /* SUPPORTED_DPC failed so try SUPPORTED_SES_DPC */
4367             ret = do_rec_diag(ptvp, SUPPORTED_SES_DPC, resp, op->maxlen, op,
4368                               &resp_len);
4369         if (ret)
4370             goto fini;
4371         for (n = 0, pc = 0; (n < s_arr_sz) && (n < (resp_len - 4)); ++n) {
4372             prev = pc;
4373             pc = resp[4 + n];
4374             if (prev > pc) {
4375                 if (pc) {       /* could be zero pad at end which is ok */
4376                     pr2serr("%s: Supported (SES) dpage seems corrupt, "
4377                             "should ascend\n", __func__);
4378                     ret = SG_LIB_CAT_OTHER;
4379                     goto fini;
4380                 }
4381                 break;
4382             }
4383             if (pc > 0x2f)
4384                 break;
4385             supp_dpg_arr[n] = pc;
4386         }
4387         for (k = 0; k < n; ++k) {
4388             page_code = supp_dpg_arr[k];
4389             ret = do_rec_diag(ptvp, page_code, resp, op->maxlen, op,
4390                               &resp_len);
4391             if (ret)
4392                 goto fini;
4393             ret = process_status_dpage(ptvp, page_code, resp, resp_len, op);
4394         }
4395     } else {    /* asking for a specific page code */
4396         ret = do_rec_diag(ptvp, page_code, resp, op->maxlen, op, &resp_len);
4397         if (ret)
4398             goto fini;
4399         ret = process_status_dpage(ptvp, page_code, resp, resp_len, op);
4400     }
4401 
4402 fini:
4403     if (free_resp)
4404         free(free_resp);
4405     return ret;
4406 }
4407 
4408 static void
devslotnum_and_sasaddr(struct join_row_t * jrp,const uint8_t * ae_bp)4409 devslotnum_and_sasaddr(struct join_row_t * jrp, const uint8_t * ae_bp)
4410 {
4411     if ((NULL == jrp) || (NULL == ae_bp) || (0 == (0x10 & ae_bp[0])))
4412         return; /* sanity and expect EIP=1 */
4413     switch (0xf & ae_bp[0]) {
4414     case TPROTO_FCP:
4415         jrp->dev_slot_num = ae_bp[7];
4416         break;
4417     case TPROTO_SAS:
4418         if (0 == (0xc0 & ae_bp[5])) {
4419             /* only for device slot and array device slot elements */
4420             jrp->dev_slot_num = ae_bp[7];
4421             if (ae_bp[4] > 0) {        /* number of phys */
4422                 int m;
4423 
4424                 /* Use the first phy's "SAS ADDRESS" field */
4425                 for (m = 0; m < 8; ++m)
4426                     jrp->sas_addr[m] = ae_bp[(4 + 4 + 12) + m];
4427             }
4428         }
4429         break;
4430     case TPROTO_PCIE:
4431         jrp->dev_slot_num = ae_bp[7];
4432         break;
4433     default:
4434         ;
4435     }
4436 }
4437 
4438 static const char *
offset_str(long offset,bool in_hex,char * b,int blen)4439 offset_str(long offset, bool in_hex, char * b, int blen)
4440 {
4441     if (in_hex && (offset >= 0))
4442         snprintf(b, blen, "0x%lx", offset);
4443     else
4444         snprintf(b, blen, "%ld", offset);
4445     return b;
4446 }
4447 
4448 /* Returns broken_ei which is only true when EIP=1 and EIIOE=0 is overridden
4449  * as outlined in join array description near the top of this file. */
4450 static bool
join_aes_helper(const uint8_t * ae_bp,const uint8_t * ae_last_bp,const struct th_es_t * tesp,const struct opts_t * op)4451 join_aes_helper(const uint8_t * ae_bp, const uint8_t * ae_last_bp,
4452                 const struct th_es_t * tesp, const struct opts_t * op)
4453 {
4454     int k, j, ei, eiioe, aes_i, hex, blen;
4455     bool eip, broken_ei;
4456     struct join_row_t * jrp;
4457     struct join_row_t * jr2p;
4458     const struct type_desc_hdr_t * tdhp = tesp->th_base;
4459     char b[20];
4460 
4461     jrp = tesp->j_base;
4462     blen = sizeof(b);
4463     hex = op->do_hex;
4464     broken_ei = false;
4465     /* loop over all type descriptor headers in the Configuration dpge */
4466     for (k = 0, aes_i = 0; k < tesp->num_ths; ++k, ++tdhp) {
4467         if (is_et_used_by_aes(tdhp->etype)) {
4468             /* only consider element types that AES element are permiited
4469              * to refer to, then loop over those number of elements */
4470             for (j = 0; j < tdhp->num_elements;
4471                  ++j, ++aes_i, ae_bp += ae_bp[1] + 2) {
4472                 if ((ae_bp + 1) > ae_last_bp) {
4473                     if (op->verbose || op->warn)
4474                         pr2serr("warning: %s: off end of ae page\n",
4475                                 __func__);
4476                     return broken_ei;
4477                 }
4478                 eip = !!(ae_bp[0] & 0x10); /* EIP == Element Index Present */
4479                 if (eip) {
4480                     eiioe = 0x3 & ae_bp[2];
4481                     if ((0 == eiioe) && op->eiioe_force)
4482                         eiioe = 1;
4483                 } else
4484                     eiioe = 0;
4485                 if (eip && (1 == eiioe)) {         /* EIP and EIIOE=1 */
4486                     ei = ae_bp[3];
4487                     jr2p = tesp->j_base + ei;
4488                     if ((ei >= tesp->num_j_eoe) ||
4489                         (NULL == jr2p->enc_statp)) {
4490                         pr2serr("%s: oi=%d, ei=%d [num_eoe=%d], eiioe=1 "
4491                                 "not in join_arr\n", __func__, k, ei,
4492                                 tesp->num_j_eoe);
4493                         return broken_ei;
4494                     }
4495                     devslotnum_and_sasaddr(jr2p, ae_bp);
4496                     if (jr2p->ae_statp) {
4497                         if (op->warn || op->verbose) {
4498                             pr2serr("warning: aes slot already in use, "
4499                                     "keep existing AES+%s\n\t",
4500                                     offset_str(jr2p->ae_statp - add_elem_rsp,
4501                                                hex, b, blen));
4502                             pr2serr("dropping AES+%s [length=%d, oi=%d, "
4503                                     "ei=%d, aes_i=%d]\n",
4504                                     offset_str(ae_bp - add_elem_rsp, hex, b,
4505                                                blen),
4506                                     ae_bp[1] + 2, k, ei, aes_i);
4507                         }
4508                     } else
4509                         jr2p->ae_statp = ae_bp;
4510                 } else if (eip && (0 == eiioe)) {     /* SES-2 so be careful */
4511                     ei = ae_bp[3];
4512 try_again:
4513                     /* Check AES dpage descriptor ei is valid */
4514                     for (jr2p = tesp->j_base; jr2p->enc_statp; ++jr2p) {
4515                         if (broken_ei) {
4516                             if (ei == jr2p->ei_aess)
4517                                 break;
4518                         } else {
4519                             if (ei == jr2p->ei_eoe)
4520                                 break;
4521                         }
4522                     }
4523                     if (NULL == jr2p->enc_statp) {
4524                         pr2serr("warning: %s: oi=%d, ei=%d (broken_ei=%d) "
4525                                 "not in join_arr\n", __func__, k, ei,
4526                                 (int)broken_ei);
4527                         return broken_ei;
4528                     }
4529                     if (! is_et_used_by_aes(jr2p->etype)) {
4530                         /* unexpected element type so  ... */
4531                         broken_ei = true;
4532                         goto try_again;
4533                     }
4534                     devslotnum_and_sasaddr(jr2p, ae_bp);
4535                     if (jr2p->ae_statp) {
4536                         /* 1 to 1 AES to ES mapping assumption violated */
4537                         if ((0 == ei) && (TPROTO_SAS == (0xf & ae_bp[0])) &&
4538                             (1 == (ae_bp[5] >> 6))) {
4539                             /* heuristic for (hack) Areca 8028 */
4540                             for (jr2p = tesp->j_base; jr2p->enc_statp;
4541                                  ++jr2p) {
4542                                 if ((-1 == jr2p->indiv_i) ||
4543                                     (! is_et_used_by_aes(jr2p->etype)) ||
4544                                     jr2p->ae_statp)
4545                                     continue;
4546                                 jr2p->ae_statp = ae_bp;
4547                                 break;
4548                             }
4549                             if ((NULL == jr2p->enc_statp) &&
4550                                 (op->warn || op->verbose))
4551                                 pr2serr("warning2: dropping AES+%s [length="
4552                                         "%d, oi=%d, ei=%d, aes_i=%d]\n",
4553                                         offset_str(ae_bp - add_elem_rsp, hex,
4554                                                    b, blen),
4555                                         ae_bp[1] + 2, k, ei, aes_i);
4556                         } else if (op->warn || op->verbose) {
4557                             pr2serr("warning3: aes slot already in use, "
4558                                     "keep existing AES+%s\n\t",
4559                                     offset_str(jr2p->ae_statp - add_elem_rsp,
4560                                                hex, b, blen));
4561                             pr2serr("dropping AES+%s [length=%d, oi=%d, ei="
4562                                     "%d, aes_i=%d]\n",
4563                                     offset_str(ae_bp - add_elem_rsp, hex, b,
4564                                                blen),
4565                                     ae_bp[1] + 2, k, ei, aes_i);
4566                         }
4567                     } else
4568                         jr2p->ae_statp = ae_bp;
4569                 } else if (eip) {              /* EIP and EIIOE=2,3 */
4570                     ei = ae_bp[3];
4571                     for (jr2p = tesp->j_base; jr2p->enc_statp; ++jr2p) {
4572                         if (ei == jr2p->ei_eoe)
4573                             break;  /* good, found match on ei_eoe */
4574                     }
4575                     if (NULL == jr2p->enc_statp) {
4576                         pr2serr("warning: %s: oi=%d, ei=%d, not in "
4577                                 "join_arr\n", __func__, k, ei);
4578                         return broken_ei;
4579                     }
4580                     if (! is_et_used_by_aes(jr2p->etype)) {
4581                         pr2serr("warning: %s: oi=%d, ei=%d, unexpected "
4582                                 "element_type=0x%x\n", __func__, k, ei,
4583                                 jr2p->etype);
4584                         return broken_ei;
4585                     }
4586                     devslotnum_and_sasaddr(jr2p, ae_bp);
4587                     if (jr2p->ae_statp) {
4588                         if (op->warn || op->verbose) {
4589                             pr2serr("warning3: aes slot already in use, "
4590                                     "keep existing AES+%s\n\t",
4591                                     offset_str(jr2p->ae_statp - add_elem_rsp,
4592                                                hex, b, blen));
4593                             pr2serr("dropping AES+%s [length=%d, oi=%d, ei="
4594                                     "%d, aes_i=%d]\n",
4595                                     offset_str(ae_bp - add_elem_rsp, hex, b,
4596                                                blen),
4597                                     ae_bp[1] + 2, k, ei, aes_i);
4598                         }
4599                     } else
4600                         jr2p->ae_statp = ae_bp;
4601                 } else {    /* EIP=0 */
4602                     /* step jrp over overall elements or those with
4603                      * jrp->ae_statp already used */
4604                     while (jrp->enc_statp && ((-1 == jrp->indiv_i) ||
4605                                               jrp->ae_statp))
4606                         ++jrp;
4607                     if (NULL == jrp->enc_statp) {
4608                         pr2serr("warning: %s: join_arr has no space for "
4609                                 "ae\n", __func__);
4610                         return broken_ei;
4611                     }
4612                     jrp->ae_statp = ae_bp;
4613                     ++jrp;
4614                 }
4615             }       /* end_for: loop over non-overall elements of the
4616                      * current type descriptor header */
4617         } else {    /* element type _not_ relevant to ae status */
4618             /* step jrp over overall and individual elements */
4619             for (j = 0; j <= tdhp->num_elements; ++j, ++jrp) {
4620                 if (NULL == jrp->enc_statp) {
4621                     pr2serr("warning: %s: join_arr has no space\n",
4622                             __func__);
4623                     return broken_ei;
4624                 }
4625             }
4626         }
4627     }       /* end_for: loop over type descriptor headers */
4628     return broken_ei;
4629 }
4630 
4631 
4632 /* User output of join array */
4633 static void
join_array_display(struct th_es_t * tesp,struct opts_t * op)4634 join_array_display(struct th_es_t * tesp, struct opts_t * op)
4635 {
4636     bool got1, need_aes;
4637     int k, j, blen, desc_len, dn_len;
4638     const uint8_t * ae_bp;
4639     const char * cp;
4640     const uint8_t * ed_bp;
4641     struct join_row_t * jrp;
4642     uint8_t * t_bp;
4643     char b[64];
4644 
4645     blen = sizeof(b);
4646     need_aes = (op->page_code_given &&
4647                 (ADD_ELEM_STATUS_DPC == op->page_code));
4648     dn_len = op->desc_name ? (int)strlen(op->desc_name) : 0;
4649     for (k = 0, jrp = tesp->j_base, got1 = false;
4650          ((k < MX_JOIN_ROWS) && jrp->enc_statp); ++k, ++jrp) {
4651         if (op->ind_given) {
4652             if (op->ind_th != jrp->th_i)
4653                 continue;
4654             if (! match_ind_indiv(jrp->indiv_i, op))
4655                 continue;
4656         }
4657         if (need_aes && (NULL == jrp->ae_statp))
4658             continue;
4659         ed_bp = jrp->elem_descp;
4660         if (op->desc_name) {
4661             if (NULL == ed_bp)
4662                 continue;
4663             desc_len = sg_get_unaligned_be16(ed_bp + 2);
4664             /* some element descriptor strings have trailing NULLs and
4665              * count them in their length; adjust */
4666             while (desc_len && ('\0' == ed_bp[4 + desc_len - 1]))
4667                 --desc_len;
4668             if (desc_len != dn_len)
4669                 continue;
4670             if (0 != strncmp(op->desc_name, (const char *)(ed_bp + 4),
4671                              desc_len))
4672                 continue;
4673         } else if (op->dev_slot_num >= 0) {
4674             if (op->dev_slot_num != jrp->dev_slot_num)
4675                 continue;
4676         } else if (saddr_non_zero(op->sas_addr)) {
4677             for (j = 0; j < 8; ++j) {
4678                 if (op->sas_addr[j] != jrp->sas_addr[j])
4679                     break;
4680             }
4681             if (j < 8)
4682                 continue;
4683         }
4684         got1 = true;
4685         if ((op->do_filter > 1) && (1 != (0xf & jrp->enc_statp[0])))
4686             continue;   /* when '-ff' and status!=OK, skip */
4687         cp = etype_str(jrp->etype, b, blen);
4688         if (ed_bp) {
4689             desc_len = sg_get_unaligned_be16(ed_bp + 2) + 4;
4690             if (desc_len > 4)
4691                 printf("%.*s [%d,%d]  Element type: %s\n", desc_len - 4,
4692                        (const char *)(ed_bp + 4), jrp->th_i,
4693                        jrp->indiv_i, cp);
4694             else
4695                 printf("[%d,%d]  Element type: %s\n", jrp->th_i,
4696                        jrp->indiv_i, cp);
4697         } else
4698             printf("[%d,%d]  Element type: %s\n", jrp->th_i,
4699                    jrp->indiv_i, cp);
4700         printf("  Enclosure Status:\n");
4701         enc_status_helper("    ", jrp->enc_statp, jrp->etype, false, op);
4702         if (jrp->ae_statp) {
4703             printf("  Additional Element Status:\n");
4704             ae_bp = jrp->ae_statp;
4705             desc_len = ae_bp[1] + 2;
4706             additional_elem_helper("    ",  ae_bp, desc_len, jrp->etype,
4707                                    tesp, op);
4708         }
4709         if (jrp->thresh_inp) {
4710             t_bp = jrp->thresh_inp;
4711             threshold_helper("  Threshold In:\n", "    ", t_bp, jrp->etype,
4712                              op);
4713         }
4714     }
4715     if (! got1) {
4716         if (op->ind_given) {
4717             printf("      >>> no match on --index=%d,%d", op->ind_th,
4718                    op->ind_indiv);
4719             if (op->ind_indiv_last > op->ind_indiv)
4720                 printf("-%d\n", op->ind_indiv_last);
4721             else
4722                 printf("\n");
4723         } else if (op->desc_name)
4724             printf("      >>> no match on --descriptor=%s\n", op->desc_name);
4725         else if (op->dev_slot_num >= 0)
4726             printf("      >>> no match on --dev-slot-name=%d\n",
4727                    op->dev_slot_num);
4728         else if (saddr_non_zero(op->sas_addr)) {
4729             printf("      >>> no match on --sas-addr=0x");
4730             for (j = 0; j < 8; ++j)
4731                 printf("%02x", op->sas_addr[j]);
4732             printf("\n");
4733         }
4734     }
4735 }
4736 
4737 /* This is for debugging, output to stderr */
4738 static void
join_array_dump(struct th_es_t * tesp,int broken_ei,struct opts_t * op)4739 join_array_dump(struct th_es_t * tesp, int broken_ei, struct opts_t * op)
4740 {
4741     int k, j, blen, hex;
4742     int eiioe_count = 0;
4743     int eip_count = 0;
4744     struct join_row_t * jrp;
4745     char b[64];
4746 
4747     blen = sizeof(b);
4748     hex = op->do_hex;
4749     pr2serr("Dump of join array, each line is a row. Lines start with\n");
4750     pr2serr("[<element_type>: <type_hdr_index>,<elem_ind_within>]\n");
4751     pr2serr("'-1' indicates overall element or not applicable.\n");
4752     jrp = tesp->j_base;
4753     for (k = 0; ((k < MX_JOIN_ROWS) && jrp->enc_statp); ++k, ++jrp) {
4754         pr2serr("[0x%x: %d,%d] ", jrp->etype, jrp->th_i, jrp->indiv_i);
4755         if (jrp->se_id > 0)
4756             pr2serr("se_id=%d ", jrp->se_id);
4757         pr2serr("ei_ioe,_eoe,_aess=%s", offset_str(k, hex, b, blen));
4758         pr2serr(",%s", offset_str(jrp->ei_eoe, hex, b, blen));
4759         pr2serr(",%s", offset_str(jrp->ei_aess, hex, b, blen));
4760         pr2serr(" dsn=%s", offset_str(jrp->dev_slot_num, hex, b, blen));
4761         if (op->do_join > 2) {
4762             pr2serr(" sa=0x");
4763             if (saddr_non_zero(jrp->sas_addr)) {
4764                 for (j = 0; j < 8; ++j)
4765                     pr2serr("%02x", jrp->sas_addr[j]);
4766             } else
4767                 pr2serr("0");
4768         }
4769         if (jrp->enc_statp)
4770             pr2serr(" ES+%s", offset_str(jrp->enc_statp - enc_stat_rsp,
4771                                          hex, b, blen));
4772         if (jrp->elem_descp)
4773             pr2serr(" ED+%s", offset_str(jrp->elem_descp - elem_desc_rsp,
4774                                          hex, b, blen));
4775         if (jrp->ae_statp) {
4776             pr2serr(" AES+%s", offset_str(jrp->ae_statp - add_elem_rsp,
4777                                           hex, b, blen));
4778             if (jrp->ae_statp[0] & 0x10) {
4779                 ++eip_count;
4780                 if (jrp->ae_statp[2] & 0x3)
4781                     ++eiioe_count;
4782             }
4783         }
4784         if (jrp->thresh_inp)
4785             pr2serr(" TI+%s", offset_str(jrp->thresh_inp - threshold_rsp,
4786                                          hex, b, blen));
4787         pr2serr("\n");
4788     }
4789     pr2serr(">> ES len=%s, ", offset_str(enc_stat_rsp_len, hex, b, blen));
4790     pr2serr("ED len=%s, ", offset_str(elem_desc_rsp_len, hex, b, blen));
4791     pr2serr("AES len=%s, ", offset_str(add_elem_rsp_len, hex, b, blen));
4792     pr2serr("TI len=%s\n", offset_str(threshold_rsp_len, hex, b, blen));
4793     pr2serr(">> join_arr elements=%s, ", offset_str(k, hex, b, blen));
4794     pr2serr("eip_count=%s, ", offset_str(eip_count, hex, b, blen));
4795     pr2serr("eiioe_count=%s ", offset_str(eiioe_count, hex, b, blen));
4796     pr2serr("broken_ei=%d\n", (int)broken_ei);
4797 }
4798 
4799 /* EIIOE juggling (standards + heuristics) for join with AES page */
4800 static void
join_juggle_aes(struct th_es_t * tesp,uint8_t * es_bp,const uint8_t * ed_bp,uint8_t * t_bp)4801 join_juggle_aes(struct th_es_t * tesp, uint8_t * es_bp, const uint8_t * ed_bp,
4802                 uint8_t * t_bp)
4803 {
4804     int k, j, eoe, ei4aess;
4805     struct join_row_t * jrp;
4806     const struct type_desc_hdr_t * tdhp;
4807 
4808     jrp = tesp->j_base;
4809     tdhp = tesp->th_base;
4810     for (k = 0, eoe = 0, ei4aess = 0; k < tesp->num_ths; ++k, ++tdhp) {
4811         bool et_used_by_aes;
4812 
4813         jrp->th_i = k;
4814         jrp->indiv_i = -1;
4815         jrp->etype = tdhp->etype;
4816         jrp->ei_eoe = -1;
4817         et_used_by_aes = is_et_used_by_aes(tdhp->etype);
4818         jrp->ei_aess = -1;
4819         jrp->se_id = tdhp->se_id;
4820         /* check es_bp < es_last_bp still in range */
4821         jrp->enc_statp = es_bp;
4822         es_bp += 4;
4823         jrp->elem_descp = ed_bp;
4824         if (ed_bp)
4825             ed_bp += sg_get_unaligned_be16(ed_bp + 2) + 4;
4826         jrp->ae_statp = NULL;
4827         jrp->thresh_inp = t_bp;
4828         jrp->dev_slot_num = -1;
4829         /* assume sas_addr[8] zeroed since it's static file scope */
4830         if (t_bp)
4831             t_bp += 4;
4832         ++jrp;
4833         for (j = 0; j < tdhp->num_elements; ++j, ++jrp) {
4834             if (jrp >= join_arr_lastp)
4835                 break;
4836             jrp->th_i = k;
4837             jrp->indiv_i = j;
4838             jrp->ei_eoe = eoe++;
4839             if (et_used_by_aes)
4840                 jrp->ei_aess = ei4aess++;
4841             else
4842                 jrp->ei_aess = -1;
4843             jrp->etype = tdhp->etype;
4844             jrp->se_id = tdhp->se_id;
4845             jrp->enc_statp = es_bp;
4846             es_bp += 4;
4847             jrp->elem_descp = ed_bp;
4848             if (ed_bp)
4849                 ed_bp += sg_get_unaligned_be16(ed_bp + 2) + 4;
4850             jrp->thresh_inp = t_bp;
4851             jrp->dev_slot_num = -1;
4852             /* assume sas_addr[8] zeroed since it's static file scope */
4853             if (t_bp)
4854                 t_bp += 4;
4855             jrp->ae_statp = NULL;
4856             ++tesp->num_j_eoe;
4857         }
4858         if (jrp >= join_arr_lastp) {
4859             /* ++k; */
4860             break;      /* leave last row all zeros */
4861         }
4862     }
4863     tesp->num_j_rows = jrp - tesp->j_base;
4864 }
4865 
4866 /* Fetch Configuration, Enclosure Status, Element Descriptor, Additional
4867  * Element Status and optionally Threshold In pages, place in static arrays.
4868  * Collate (join) overall and individual elements into the static join_arr[].
4869  * When 'display' is true then the join_arr[]  is output to stdout in a form
4870  * suitable for end users. For debug purposes the join_arr[] is output to
4871  * stderr when op->verbose > 3. Returns 0 for success, any other return value
4872  * is an error. */
4873 static int
join_work(struct sg_pt_base * ptvp,struct opts_t * op,bool display)4874 join_work(struct sg_pt_base * ptvp, struct opts_t * op, bool display)
4875 {
4876     bool broken_ei;
4877     int res, num_ths, mlen;
4878     uint32_t ref_gen_code, gen_code;
4879     const uint8_t * ae_bp;
4880     const uint8_t * ae_last_bp;
4881     const char * enc_state_changed = "  <<state of enclosure changed, "
4882                                      "please try again>>\n";
4883     uint8_t * es_bp;
4884     const uint8_t * ed_bp;
4885     uint8_t * t_bp;
4886     struct th_es_t * tesp;
4887     struct enclosure_info primary_info;
4888     struct th_es_t tes;
4889 
4890     memset(&primary_info, 0, sizeof(primary_info));
4891     num_ths = build_type_desc_hdr_arr(ptvp, type_desc_hdr_arr, MX_ELEM_HDR,
4892                                       &ref_gen_code, &primary_info, op);
4893     if (num_ths < 0)
4894         return num_ths;
4895     tesp = &tes;
4896     memset(tesp, 0, sizeof(tes));
4897     tesp->th_base = type_desc_hdr_arr;
4898     tesp->num_ths = num_ths;
4899     if (display && primary_info.have_info) {
4900         int j;
4901 
4902         printf("  Primary enclosure logical identifier (hex): ");
4903         for (j = 0; j < 8; ++j)
4904             printf("%02x", primary_info.enc_log_id[j]);
4905         printf("\n");
4906     }
4907     mlen = enc_stat_rsp_sz;
4908     if (mlen > op->maxlen)
4909         mlen = op->maxlen;
4910     res = do_rec_diag(ptvp, ENC_STATUS_DPC, enc_stat_rsp, mlen, op,
4911                       &enc_stat_rsp_len);
4912     if (res)
4913         return res;
4914     if (enc_stat_rsp_len < 8) {
4915         pr2serr("Enclosure Status response too short\n");
4916         return -1;
4917     }
4918     gen_code = sg_get_unaligned_be32(enc_stat_rsp + 4);
4919     if (ref_gen_code != gen_code) {
4920         pr2serr("%s", enc_state_changed);
4921         return -1;
4922     }
4923     es_bp = enc_stat_rsp + 8;
4924     /* es_last_bp = enc_stat_rsp + enc_stat_rsp_len - 1; */
4925 
4926     mlen = elem_desc_rsp_sz;
4927     if (mlen > op->maxlen)
4928         mlen = op->maxlen;
4929     res = do_rec_diag(ptvp, ELEM_DESC_DPC, elem_desc_rsp, mlen, op,
4930                       &elem_desc_rsp_len);
4931     if (0 == res) {
4932         if (elem_desc_rsp_len < 8) {
4933             pr2serr("Element Descriptor response too short\n");
4934             return -1;
4935         }
4936         gen_code = sg_get_unaligned_be32(elem_desc_rsp + 4);
4937         if (ref_gen_code != gen_code) {
4938             pr2serr("%s", enc_state_changed);
4939             return -1;
4940         }
4941         ed_bp = elem_desc_rsp + 8;
4942         /* ed_last_bp = elem_desc_rsp + elem_desc_rsp_len - 1; */
4943     } else {
4944         elem_desc_rsp_len = 0;
4945         ed_bp = NULL;
4946         res = 0;
4947         if (op->verbose)
4948             pr2serr("  Element Descriptor page not available\n");
4949     }
4950 
4951     /* check if we want to add the AES page to the join */
4952     if (display || (ADD_ELEM_STATUS_DPC == op->page_code) ||
4953         (op->dev_slot_num >= 0) || saddr_non_zero(op->sas_addr)) {
4954         mlen = add_elem_rsp_sz;
4955         if (mlen > op->maxlen)
4956             mlen = op->maxlen;
4957         res = do_rec_diag(ptvp, ADD_ELEM_STATUS_DPC, add_elem_rsp, mlen, op,
4958                           &add_elem_rsp_len);
4959         if (0 == res) {
4960             if (add_elem_rsp_len < 8) {
4961                 pr2serr("Additional Element Status response too short\n");
4962                 return -1;
4963             }
4964             gen_code = sg_get_unaligned_be32(add_elem_rsp + 4);
4965             if (ref_gen_code != gen_code) {
4966                 pr2serr("%s", enc_state_changed);
4967                 return -1;
4968             }
4969             ae_bp = add_elem_rsp + 8;
4970             ae_last_bp = add_elem_rsp + add_elem_rsp_len - 1;
4971             if (op->eiioe_auto && (add_elem_rsp_len > 11)) {
4972                 /* heuristic: if first AES descriptor has EIP set and its
4973                  * EI equal to 1, then act as if the EIIOE field is 1. */
4974                 if ((ae_bp[0] & 0x10) && (1 == ae_bp[3]))
4975                     op->eiioe_force = true;
4976             }
4977         } else {        /* unable to read AES dpage */
4978             add_elem_rsp_len = 0;
4979             ae_bp = NULL;
4980             ae_last_bp = NULL;
4981             res = 0;
4982             if (op->verbose)
4983                 pr2serr("  Additional Element Status page not available\n");
4984         }
4985     } else {
4986         ae_bp = NULL;
4987         ae_last_bp = NULL;
4988     }
4989 
4990     if ((op->do_join > 1) ||
4991         ((! display) && (THRESHOLD_DPC == op->page_code))) {
4992         mlen = threshold_rsp_sz;
4993         if (mlen > op->maxlen)
4994             mlen = op->maxlen;
4995         res = do_rec_diag(ptvp, THRESHOLD_DPC, threshold_rsp, mlen, op,
4996                           &threshold_rsp_len);
4997         if (0 == res) {
4998             if (threshold_rsp_len < 8) {
4999                 pr2serr("Threshold In response too short\n");
5000                 return -1;
5001             }
5002             gen_code = sg_get_unaligned_be32(threshold_rsp + 4);
5003             if (ref_gen_code != gen_code) {
5004                 pr2serr("%s", enc_state_changed);
5005                 return -1;
5006             }
5007             t_bp = threshold_rsp + 8;
5008             /* t_last_bp = threshold_rsp + threshold_rsp_len - 1; */
5009         } else {
5010             threshold_rsp_len = 0;
5011             t_bp = NULL;
5012             res = 0;
5013             if (op->verbose)
5014                 pr2serr("  Threshold In page not available\n");
5015         }
5016     } else {
5017         threshold_rsp_len = 0;
5018         t_bp = NULL;
5019     }
5020 
5021 
5022     tesp->j_base = join_arr;
5023     join_juggle_aes(tesp, es_bp, ed_bp, t_bp);
5024 
5025     broken_ei = false;
5026     if (ae_bp)
5027         broken_ei = join_aes_helper(ae_bp, ae_last_bp, tesp, op);
5028 
5029     if (op->verbose > 3)
5030         join_array_dump(tesp, broken_ei, op);
5031 
5032     join_done = true;
5033     if (display)      /* probably wanted join_arr[] built only */
5034         join_array_display(tesp, op);
5035 
5036     return res;
5037 
5038 }
5039 
5040 /* Returns 1 if strings equal (same length, characters same or only differ
5041  * by case), else returns 0. Assumes 7 bit ASCII (English alphabet). */
5042 static int
strcase_eq(const char * s1p,const char * s2p)5043 strcase_eq(const char * s1p, const char * s2p)
5044 {
5045     int c1;
5046 
5047     do {
5048         int c2;
5049 
5050         c1 = *s1p++;
5051         c2 = *s2p++;
5052         if (c1 != c2) {
5053             if (c2 >= 'a')
5054                 c2 = toupper(c2);
5055             else if (c1 >= 'a')
5056                 c1 = toupper(c1);
5057             else
5058                 return 0;
5059             if (c1 != c2)
5060                 return 0;
5061         }
5062     } while (c1);
5063     return 1;
5064 }
5065 
5066 static bool
is_acronym_in_status_ctl(const struct tuple_acronym_val * tavp)5067 is_acronym_in_status_ctl(const struct tuple_acronym_val * tavp)
5068 {
5069     const struct acronym2tuple * ap;
5070 
5071     for (ap = ecs_a2t_arr; ap->acron; ++ ap) {
5072         if (strcase_eq(tavp->acron, ap->acron))
5073             break;
5074     }
5075     return ap->acron;
5076 }
5077 
5078 static bool
is_acronym_in_threshold(const struct tuple_acronym_val * tavp)5079 is_acronym_in_threshold(const struct tuple_acronym_val * tavp)
5080 {
5081     const struct acronym2tuple * ap;
5082 
5083     for (ap = th_a2t_arr; ap->acron; ++ ap) {
5084         if (strcase_eq(tavp->acron, ap->acron))
5085             break;
5086     }
5087     return ap->acron;
5088 }
5089 
5090 static bool
is_acronym_in_additional(const struct tuple_acronym_val * tavp)5091 is_acronym_in_additional(const struct tuple_acronym_val * tavp)
5092 {
5093     const struct acronym2tuple * ap;
5094 
5095     for (ap = ae_sas_a2t_arr; ap->acron; ++ ap) {
5096         if (strcase_eq(tavp->acron, ap->acron))
5097             break;
5098     }
5099     return ap->acron;
5100 }
5101 
5102 /* ENC_STATUS_DPC  ENC_CONTROL_DPC
5103  * Do clear/get/set (cgs) on Enclosure Control/Status page. Return 0 for ok
5104  * -2 for acronym not found, else -1 . */
5105 static int
cgs_enc_ctl_stat(struct sg_pt_base * ptvp,struct join_row_t * jrp,const struct tuple_acronym_val * tavp,const struct opts_t * op,bool last)5106 cgs_enc_ctl_stat(struct sg_pt_base * ptvp, struct join_row_t * jrp,
5107                  const struct tuple_acronym_val * tavp,
5108                  const struct opts_t * op, bool last)
5109 {
5110     int s_byte, s_bit, n_bits;
5111     const struct acronym2tuple * ap;
5112 
5113     if (NULL == tavp->acron) {
5114         s_byte = tavp->start_byte;
5115         s_bit = tavp->start_bit;
5116         n_bits = tavp->num_bits;
5117     }
5118     if (tavp->acron) {
5119         for (ap = ecs_a2t_arr; ap->acron; ++ ap) {
5120             if (((jrp->etype == ap->etype) || (-1 == ap->etype)) &&
5121                 strcase_eq(tavp->acron, ap->acron))
5122                 break;
5123         }
5124         if (ap->acron) {
5125             s_byte = ap->start_byte;
5126             s_bit = ap->start_bit;
5127             n_bits = ap->num_bits;
5128         } else {
5129             if (-1 != ap->etype) {
5130                 for (ap = ecs_a2t_arr; ap->acron; ++ap) {
5131                     if (0 == strcase_eq(tavp->acron, ap->acron)) {
5132                         pr2serr(">>> Found %s acronym but not for element "
5133                                 "type %d\n", tavp->acron, jrp->etype);
5134                         break;
5135                     }
5136                 }
5137             }
5138             return -2;
5139         }
5140     }
5141     if (op->verbose > 1)
5142         pr2serr("  s_byte=%d, s_bit=%d, n_bits=%d\n", s_byte, s_bit, n_bits);
5143     if (GET_OPT == tavp->cgs_sel) {
5144         uint64_t ui = sg_get_big_endian(jrp->enc_statp + s_byte, s_bit,
5145                                         n_bits);
5146 
5147         if (op->do_hex)
5148             printf("0x%" PRIx64 "\n", ui);
5149         else
5150             printf("%" PRId64 "\n", (int64_t)ui);
5151     } else {    /* --set or --clear */
5152         int len;
5153 
5154         if ((! op->mask_ign) && (jrp->etype < NUM_ETC)) {
5155             int k;
5156 
5157             if (op->verbose > 2)
5158                 pr2serr("Applying mask to element status [etc=%d] prior to "
5159                         "modify then write\n", jrp->etype);
5160             for (k = 0; k < 4; ++k)
5161                 jrp->enc_statp[k] &= ses3_element_cmask_arr[jrp->etype][k];
5162         } else
5163             jrp->enc_statp[0] &= 0x40;  /* keep PRDFAIL is set in byte 0 */
5164         /* next we modify requested bit(s) */
5165         sg_set_big_endian((uint64_t)tavp->val,
5166                           jrp->enc_statp + s_byte, s_bit, n_bits);
5167         jrp->enc_statp[0] |= 0x80;  /* set SELECT bit */
5168         if (op->byte1_given)
5169             enc_stat_rsp[1] = op->byte1;
5170         len = sg_get_unaligned_be16(enc_stat_rsp + 2) + 4;
5171         if (last) {
5172             int ret = do_senddiag(ptvp, enc_stat_rsp, len, ! op->quiet,
5173                                   op->verbose);
5174 
5175             if (ret) {
5176                 pr2serr("couldn't send Enclosure Control page\n");
5177                 return -1;
5178             }
5179         }
5180     }
5181     return 0;
5182 }
5183 
5184 /* THRESHOLD_DPC
5185  * Do clear/get/set (cgs) on Threshold In/Out page. Return 0 for ok,
5186  * -2 for acronym not found, else -1 . */
5187 static int
cgs_threshold(struct sg_pt_base * ptvp,const struct join_row_t * jrp,const struct tuple_acronym_val * tavp,const struct opts_t * op,bool last)5188 cgs_threshold(struct sg_pt_base * ptvp, const struct join_row_t * jrp,
5189               const struct tuple_acronym_val * tavp,
5190               const struct opts_t * op, bool last)
5191 {
5192     int s_byte, s_bit, n_bits;
5193     const struct acronym2tuple * ap;
5194 
5195     if (NULL == jrp->thresh_inp) {
5196         pr2serr("No Threshold In/Out element available\n");
5197         return -1;
5198     }
5199     if (NULL == tavp->acron) {
5200         s_byte = tavp->start_byte;
5201         s_bit = tavp->start_bit;
5202         n_bits = tavp->num_bits;
5203     }
5204     if (tavp->acron) {
5205         for (ap = th_a2t_arr; ap->acron; ++ap) {
5206             if (((jrp->etype == ap->etype) || (-1 == ap->etype)) &&
5207                 strcase_eq(tavp->acron, ap->acron))
5208                 break;
5209         }
5210         if (ap->acron) {
5211             s_byte = ap->start_byte;
5212             s_bit = ap->start_bit;
5213             n_bits = ap->num_bits;
5214         } else
5215             return -2;
5216     }
5217     if (GET_OPT == tavp->cgs_sel) {
5218         uint64_t ui = sg_get_big_endian(jrp->thresh_inp + s_byte, s_bit,
5219                                          n_bits);
5220 
5221         if (op->do_hex)
5222             printf("0x%" PRIx64 "\n", ui);
5223         else
5224             printf("%" PRId64 "\n", (int64_t)ui);
5225     } else {
5226         int len;
5227 
5228         sg_set_big_endian((uint64_t)tavp->val,
5229                           jrp->thresh_inp + s_byte, s_bit, n_bits);
5230         if (op->byte1_given)
5231             threshold_rsp[1] = op->byte1;
5232         len = sg_get_unaligned_be16(threshold_rsp + 2) + 4;
5233         if (last) {
5234             int ret = do_senddiag(ptvp, threshold_rsp, len, ! op->quiet,
5235                                   op->verbose);
5236 
5237             if (ret) {
5238                 pr2serr("couldn't send Threshold Out page\n");
5239                 return -1;
5240             }
5241         }
5242     }
5243     return 0;
5244 }
5245 
5246 /* ADD_ELEM_STATUS_DPC
5247  * Do get (cgs) on Additional element status page. Return 0 for ok,
5248  * -2 for acronym not found, else -1 . */
5249 static int
cgs_additional_el(const struct join_row_t * jrp,const struct tuple_acronym_val * tavp,const struct opts_t * op)5250 cgs_additional_el(const struct join_row_t * jrp,
5251                   const struct tuple_acronym_val * tavp,
5252                   const struct opts_t * op)
5253 {
5254     int s_byte, s_bit, n_bits;
5255     const struct acronym2tuple * ap;
5256 
5257     if (NULL == jrp->ae_statp) {
5258         pr2serr("No additional element status element available\n");
5259         return -1;
5260     }
5261     if (NULL == tavp->acron) {
5262         s_byte = tavp->start_byte;
5263         s_bit = tavp->start_bit;
5264         n_bits = tavp->num_bits;
5265     }
5266     if (tavp->acron) {
5267         for (ap = ae_sas_a2t_arr; ap->acron; ++ap) {
5268             if (((jrp->etype == ap->etype) || (-1 == ap->etype)) &&
5269                 strcase_eq(tavp->acron, ap->acron))
5270                 break;
5271         }
5272         if (ap->acron) {
5273             s_byte = ap->start_byte;
5274             s_bit = ap->start_bit;
5275             n_bits = ap->num_bits;
5276         } else
5277             return -2;
5278     }
5279     if (GET_OPT == tavp->cgs_sel) {
5280         uint64_t ui = sg_get_big_endian(jrp->ae_statp + s_byte, s_bit,
5281                                          n_bits);
5282 
5283         if (op->do_hex)
5284             printf("0x%" PRIx64 "\n", ui);
5285         else
5286             printf("%" PRId64 "\n", (int64_t)ui);
5287     } else {
5288         pr2serr("--clear and --set not available for Additional Element "
5289                 "Status page\n");
5290         return -1;
5291     }
5292     return 0;
5293 }
5294 
5295 /* Do --clear, --get or --set .
5296  * Returns 0 for success, any other return value is an error. */
5297 static int
ses_cgs(struct sg_pt_base * ptvp,const struct tuple_acronym_val * tavp,struct opts_t * op,bool last)5298 ses_cgs(struct sg_pt_base * ptvp, const struct tuple_acronym_val * tavp,
5299         struct opts_t * op, bool last)
5300 {
5301     int ret, k, j, desc_len, dn_len;
5302     bool found;
5303     struct join_row_t * jrp;
5304     const uint8_t * ed_bp;
5305     char b[64];
5306 
5307     if ((NULL == ptvp) && (GET_OPT != tavp->cgs_sel)) {
5308         pr2serr("%s: --clear= and --set= only supported when DEVICE is "
5309                 "given\n", __func__);
5310         return SG_LIB_CONTRADICT;
5311     }
5312     found = false;
5313     if (NULL == tavp->acron) {
5314         if (! op->page_code_given)
5315             op->page_code = ENC_CONTROL_DPC;
5316         found = true;
5317     } else if (is_acronym_in_status_ctl(tavp)) {
5318         if (op->page_code > 0) {
5319             if (ENC_CONTROL_DPC != op->page_code)
5320                 goto inconsistent;
5321         } else
5322             op->page_code = ENC_CONTROL_DPC;
5323         found = true;
5324     } else if (is_acronym_in_threshold(tavp)) {
5325         if (op->page_code > 0) {
5326             if (THRESHOLD_DPC != op->page_code)
5327                 goto inconsistent;
5328         } else
5329             op->page_code = THRESHOLD_DPC;
5330         found = true;
5331     } else if (is_acronym_in_additional(tavp)) {
5332         if (op->page_code > 0) {
5333             if (ADD_ELEM_STATUS_DPC != op->page_code)
5334                 goto inconsistent;
5335         } else
5336             op->page_code = ADD_ELEM_STATUS_DPC;
5337         found = true;
5338     }
5339     if (! found) {
5340         pr2serr("acroynm %s not found (try '-ee' option)\n", tavp->acron);
5341         return -1;
5342     }
5343     if (false == join_done) {
5344         ret = join_work(ptvp, op, false);
5345         if (ret)
5346             return ret;
5347     }
5348     dn_len = op->desc_name ? (int)strlen(op->desc_name) : 0;
5349     for (k = 0, jrp = join_arr; ((k < MX_JOIN_ROWS) && jrp->enc_statp);
5350          ++k, ++jrp) {
5351         if (op->ind_given) {
5352             if (op->ind_th != jrp->th_i)
5353                 continue;
5354             if (! match_ind_indiv(jrp->indiv_i, op))
5355                 continue;
5356         } else if (op->desc_name) {
5357             ed_bp = jrp->elem_descp;
5358             if (NULL == ed_bp)
5359                 continue;
5360             desc_len = sg_get_unaligned_be16(ed_bp + 2);
5361             /* some element descriptor strings have trailing NULLs and
5362              * count them; adjust */
5363             while (desc_len && ('\0' == ed_bp[4 + desc_len - 1]))
5364                 --desc_len;
5365             if (desc_len != dn_len)
5366                 continue;
5367             if (0 != strncmp(op->desc_name, (const char *)(ed_bp + 4),
5368                              desc_len))
5369                 continue;
5370         } else if (op->dev_slot_num >= 0) {
5371             if (op->dev_slot_num != jrp->dev_slot_num)
5372                 continue;
5373         } else if (saddr_non_zero(op->sas_addr)) {
5374             for (j = 0; j < 8; ++j) {
5375                 if (op->sas_addr[j] != jrp->sas_addr[j])
5376                     break;
5377             }
5378             if (j < 8)
5379                 continue;
5380         }
5381         if (ENC_CONTROL_DPC == op->page_code)
5382             ret = cgs_enc_ctl_stat(ptvp, jrp, tavp, op, last);
5383         else if (THRESHOLD_DPC == op->page_code)
5384             ret = cgs_threshold(ptvp, jrp, tavp, op, last);
5385         else if (ADD_ELEM_STATUS_DPC == op->page_code)
5386             ret = cgs_additional_el(jrp, tavp, op);
5387         else {
5388             pr2serr("page %s not supported for cgs\n",
5389                     etype_str(op->page_code, b, sizeof(b)));
5390             ret = -1;
5391         }
5392         if (ret)
5393             return ret;
5394         if (op->ind_indiv_last <= op->ind_indiv)
5395             break;
5396     }   /* end of loop over join array */
5397     if ((k >= MX_JOIN_ROWS || (NULL == jrp->enc_statp))) {
5398         if (op->desc_name)
5399             pr2serr("descriptor name: %s not found (check the 'ed' page "
5400                     "[0x7])\n", op->desc_name);
5401         else if (op->dev_slot_num >= 0)
5402             pr2serr("device slot number: %d not found\n", op->dev_slot_num);
5403         else if (saddr_non_zero(op->sas_addr))
5404             pr2serr("SAS address not found\n");
5405         else {
5406             pr2serr("index: %d,%d", op->ind_th, op->ind_indiv);
5407             if (op->ind_indiv_last > op->ind_indiv)
5408                 printf("-%d not found\n", op->ind_indiv_last);
5409             else
5410                 printf(" not found\n");
5411         }
5412         return -1;
5413     }
5414     return 0;
5415 
5416 inconsistent:
5417     pr2serr("acroynm %s inconsistent with page_code=0x%x\n", tavp->acron,
5418             op->page_code);
5419     return -1;
5420 }
5421 
5422 /* Called when '--nickname=SEN' given. First calls status page to fetch
5423  * the generation code. Returns 0 for success, any other return value is
5424  * an error. */
5425 static int
ses_set_nickname(struct sg_pt_base * ptvp,struct opts_t * op)5426 ses_set_nickname(struct sg_pt_base * ptvp, struct opts_t * op)
5427 {
5428     int res, len;
5429     int resp_len = 0;
5430     uint8_t b[64];
5431     const int control_plen = 0x24;
5432 
5433     if (NULL == ptvp) {
5434         pr2serr("%s: ignored when no device name\n", __func__);
5435         return 0;
5436     }
5437     memset(b, 0, sizeof(b));
5438     /* Only after the generation code, offset 4 for 4 bytes */
5439     res = do_rec_diag(ptvp, SUBENC_NICKNAME_DPC, b, 8, op, &resp_len);
5440     if (res) {
5441         pr2serr("%s: Subenclosure nickname status page, res=%d\n", __func__,
5442                 res);
5443         return -1;
5444     }
5445     if (resp_len < 8) {
5446         pr2serr("%s: Subenclosure nickname status page, response length too "
5447                 "short: %d\n", __func__, resp_len);
5448         return -1;
5449     }
5450     if (op->verbose) {
5451         uint32_t gc;
5452 
5453         gc = sg_get_unaligned_be32(b + 4);
5454         pr2serr("%s: generation code from status page: %" PRIu32 "\n",
5455                 __func__, gc);
5456     }
5457     b[0] = (uint8_t)SUBENC_NICKNAME_DPC;  /* just in case */
5458     b[1] = (uint8_t)op->seid;
5459     sg_put_unaligned_be16((uint16_t)control_plen, b + 2);
5460     len = strlen(op->nickname_str);
5461     if (len > 32)
5462         len = 32;
5463     memcpy(b + 8, op->nickname_str, len);
5464     return do_senddiag(ptvp, b, control_plen + 4, ! op->quiet,
5465                        op->verbose);
5466 }
5467 
5468 static void
enumerate_diag_pages(void)5469 enumerate_diag_pages(void)
5470 {
5471     bool got1;
5472     const struct diag_page_code * pcdp;
5473     const struct diag_page_abbrev * ap;
5474 
5475     printf("Diagnostic pages, followed by abbreviation(s) then page code:\n");
5476     for (pcdp = dpc_arr; pcdp->desc; ++pcdp) {
5477         printf("    %s  [", pcdp->desc);
5478         for (ap = dp_abbrev, got1 = false; ap->abbrev; ++ap) {
5479             if (ap->page_code == pcdp->page_code) {
5480                 printf("%s%s", (got1 ? "," : ""), ap->abbrev);
5481                 got1 = true;
5482             }
5483         }
5484         printf("] [0x%x]\n", pcdp->page_code);
5485     }
5486 }
5487 
5488 /* Output from --enumerate or --list option. Note that the output is
5489  * different when the option is given twice. */
5490 static void
enumerate_work(const struct opts_t * op)5491 enumerate_work(const struct opts_t * op)
5492 {
5493     int num;
5494 
5495     if (op->dev_name)
5496         printf(">>> DEVICE %s ignored when --%s option given.\n",
5497                op->dev_name, (op->do_list ? "list" : "enumerate"));
5498     num = op->enumerate + (int)op->do_list;
5499     if (num < 2) {
5500         const struct element_type_t * etp;
5501 
5502         enumerate_diag_pages();
5503         printf("\nSES element type names, followed by abbreviation and "
5504                "element type code:\n");
5505         for (etp = element_type_arr; etp->desc; ++etp)
5506             printf("    %s  [%s] [0x%x]\n", etp->desc, etp->abbrev,
5507                    etp->elem_type_code);
5508     } else {
5509         bool given_et = false;
5510         const struct acronym2tuple * ap;
5511         const char * cp;
5512         char a[160];
5513         char b[64];
5514         char bb[64];
5515 
5516         /* command line has multiple --enumerate and/or --list options */
5517         printf("--clear, --get, --set acronyms for Enclosure Status/Control "
5518                "['es' or 'ec'] page");
5519         if (op->ind_given && op->ind_etp &&
5520             (cp = etype_str(op->ind_etp->elem_type_code, bb, sizeof(bb)))) {
5521             printf("\n(element type: %s)", cp);
5522             given_et = true;
5523         }
5524         printf(":\n");
5525         for (ap = ecs_a2t_arr; ap->acron; ++ap) {
5526             if (given_et && (op->ind_etp->elem_type_code != ap->etype))
5527                 continue;
5528             cp = (ap->etype < 0) ?  "*" : etype_str(ap->etype, b, sizeof(b));
5529             snprintf(a, sizeof(a), "  %s  [%s] [%d:%d:%d]", ap->acron,
5530                      (cp ? cp : "??"), ap->start_byte, ap->start_bit,
5531                      ap->num_bits);
5532             if (ap->info)
5533                 printf("%-44s  %s\n", a, ap->info);
5534             else
5535                 printf("%s\n", a);
5536         }
5537         if (given_et)
5538             return;
5539         printf("\n--clear, --get, --set acronyms for Threshold In/Out "
5540                "['th'] page:\n");
5541         for (ap = th_a2t_arr; ap->acron; ++ap) {
5542             cp = (ap->etype < 0) ? "*" : etype_str(ap->etype, b, sizeof(b));
5543             snprintf(a, sizeof(a), "  %s  [%s] [%d:%d:%d]", ap->acron,
5544                      (cp ? cp : "??"), ap->start_byte, ap->start_bit,
5545                      ap->num_bits);
5546             if (ap->info)
5547                 printf("%-34s  %s\n", a, ap->info);
5548             else
5549                 printf("%s\n", a);
5550         }
5551         printf("\n--get acronyms for Additional Element Status ['aes'] page "
5552                "(SAS EIP=1):\n");
5553         for (ap = ae_sas_a2t_arr; ap->acron; ++ap) {
5554             cp = (ap->etype < 0) ? "*" : etype_str(ap->etype, b, sizeof(b));
5555             snprintf(a, sizeof(a), "  %s  [%s] [%d:%d:%d]", ap->acron,
5556                      (cp ? cp : "??"), ap->start_byte, ap->start_bit,
5557                      ap->num_bits);
5558             if (ap->info)
5559                 printf("%-34s  %s\n", a, ap->info);
5560             else
5561                 printf("%s\n", a);
5562         }
5563     }
5564 }
5565 
5566 
5567 int
main(int argc,char * argv[])5568 main(int argc, char * argv[])
5569 {
5570     bool have_cgs = false;
5571     int k, n, d_len, res, resid, vb;
5572     int sg_fd = -1;
5573     int pd_type = 0;
5574     int ret = 0;
5575     const char * cp;
5576     struct opts_t opts;
5577     struct opts_t * op;
5578     struct tuple_acronym_val * tavp;
5579     struct cgs_cl_t * cgs_clp;
5580     uint8_t * free_enc_stat_rsp = NULL;
5581     uint8_t * free_elem_desc_rsp = NULL;
5582     uint8_t * free_add_elem_rsp = NULL;
5583     uint8_t * free_threshold_rsp = NULL;
5584     struct sg_pt_base * ptvp = NULL;
5585     struct tuple_acronym_val tav_arr[CGS_CL_ARR_MAX_SZ];
5586     char buff[128];
5587     char b[128];
5588 
5589     op = &opts;
5590     memset(op, 0, sizeof(*op));
5591     op->dev_slot_num = -1;
5592     op->ind_indiv_last = -1;
5593     op->maxlen = MX_ALLOC_LEN;
5594     res = parse_cmd_line(op, argc, argv);
5595     vb = op->verbose;
5596     if (res) {
5597         ret = SG_LIB_SYNTAX_ERROR;
5598         goto early_out;
5599     }
5600     if (op->do_help) {
5601         usage(op->do_help);
5602         goto early_out;
5603     }
5604 #ifdef DEBUG
5605     pr2serr("In DEBUG mode, ");
5606     if (op->verbose_given && op->version_given) {
5607         pr2serr("but override: '-vV' given, zero verbose and continue\n");
5608         op->verbose_given = false;
5609         op->version_given = false;
5610         op->verbose = 0;
5611     } else if (! op->verbose_given) {
5612         pr2serr("set '-vv'\n");
5613         op->verbose = 2;
5614     } else
5615         pr2serr("keep verbose=%d\n", op->verbose);
5616 #else
5617     if (op->verbose_given && op->version_given)
5618         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
5619 #endif
5620     if (op->version_given) {
5621         pr2serr("version: %s\n", version_str);
5622         goto early_out;
5623     }
5624 
5625     vb = op->verbose;   /* may have changed */
5626     if (op->enumerate || op->do_list) {
5627         enumerate_work(op);
5628         goto early_out;
5629     }
5630     enc_stat_rsp = sg_memalign(op->maxlen, 0, &free_enc_stat_rsp, false);
5631     if (NULL == enc_stat_rsp) {
5632         pr2serr("Unable to get heap for enc_stat_rsp\n");
5633         goto err_out;
5634     }
5635     enc_stat_rsp_sz = op->maxlen;
5636     elem_desc_rsp = sg_memalign(op->maxlen, 0, &free_elem_desc_rsp, false);
5637     if (NULL == elem_desc_rsp) {
5638         pr2serr("Unable to get heap for elem_desc_rsp\n");
5639         goto err_out;
5640     }
5641     elem_desc_rsp_sz = op->maxlen;
5642     add_elem_rsp = sg_memalign(op->maxlen, 0, &free_add_elem_rsp, false);
5643     if (NULL == add_elem_rsp) {
5644         pr2serr("Unable to get heap for add_elem_rsp\n");
5645         goto err_out;
5646     }
5647     add_elem_rsp_sz = op->maxlen;
5648     threshold_rsp = sg_memalign(op->maxlen, 0, &free_threshold_rsp, false);
5649     if (NULL == threshold_rsp) {
5650         pr2serr("Unable to get heap for threshold_rsp\n");
5651         goto err_out;
5652     }
5653     threshold_rsp_sz = op->maxlen;
5654 
5655     if (op->num_cgs) {
5656         have_cgs = true;
5657         if (op->page_code_given &&
5658             ! ((ENC_STATUS_DPC == op->page_code) ||
5659                (THRESHOLD_DPC == op->page_code) ||
5660                (ADD_ELEM_STATUS_DPC == op->page_code))) {
5661             pr2serr("--clear, --get or --set options only supported for the "
5662                     "Enclosure\nControl/Status, Threshold In/Out and "
5663                     "Additional Element Status pages\n");
5664             ret = SG_LIB_SYNTAX_ERROR;
5665             goto err_out;
5666         }
5667         if (! (op->ind_given || op->desc_name || (op->dev_slot_num >= 0) ||
5668                saddr_non_zero(op->sas_addr))) {
5669             pr2serr("with --clear, --get or --set option need either\n   "
5670                     "--index, --descriptor, --dev-slot-num or --sas-addr\n");
5671             ret = SG_LIB_CONTRADICT;
5672             goto err_out;
5673         }
5674         for (k = 0, cgs_clp = op->cgs_cl_arr, tavp = tav_arr; k < op->num_cgs;
5675              ++k, ++cgs_clp, ++tavp) {
5676             if (parse_cgs_str(cgs_clp->cgs_str, tavp)) {
5677                 pr2serr("unable to decode STR argument to: %s\n",
5678                         cgs_clp->cgs_str);
5679                 ret = SG_LIB_SYNTAX_ERROR;
5680                 goto err_out;
5681             }
5682             if ((GET_OPT == cgs_clp->cgs_sel) && tavp->val_str)
5683                 pr2serr("--get option ignoring =<val> at the end of STR "
5684                         "argument\n");
5685             if (NULL == tavp->val_str) {
5686                 if (CLEAR_OPT == cgs_clp->cgs_sel)
5687                     tavp->val = DEF_CLEAR_VAL;
5688                 if (SET_OPT == cgs_clp->cgs_sel)
5689                     tavp->val = DEF_SET_VAL;
5690             }
5691             if (!strcmp(cgs_clp->cgs_str, "sas_addr") &&
5692                 op->dev_slot_num < 0) {
5693                 pr2serr("--get=sas_addr requires --dev-slot-num.  For "
5694                         "expander SAS address, use exp_sas_addr instead.\n");
5695                 ret = SG_LIB_SYNTAX_ERROR;
5696                 goto err_out;
5697             }
5698             tavp->cgs_sel = cgs_clp->cgs_sel;
5699         }
5700         /* keep this descending for loop directly after ascending for loop */
5701         for (--k, --cgs_clp; k >= 0; --k, --cgs_clp) {
5702             if ((CLEAR_OPT == cgs_clp->cgs_sel) ||
5703                 (SET_OPT == cgs_clp->cgs_sel)) {
5704                 cgs_clp->last_cs = true;
5705                 break;
5706             }
5707         }
5708     }
5709 
5710 #ifdef SG_LIB_WIN32
5711 #ifdef SG_LIB_WIN32_DIRECT
5712     if (vb > 4)
5713         pr2serr("Initial win32 SPT interface state: %s\n",
5714                 scsi_pt_win32_spt_state() ? "direct" : "indirect");
5715     if (op->maxlen >= 16384)
5716         scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
5717 #endif
5718 #endif
5719 
5720 #if 0
5721     pr2serr("Debug dump of input parameters:\n");
5722     pr2serr("  index option given: %d, ind_th=%d, ind_indiv=%d, "
5723             "ind_indiv_last=%d\n", op->ind_given, op->ind_th,
5724             op->ind_indiv, op->ind_indiv_last);
5725     pr2serr("  num_cgs=%d, contents:\n", op->num_cgs);
5726     for (k = 0, tavp = tav_arr, cgs_clp = op->cgs_cl_arr;
5727          k < op->num_cgs; ++k, ++tavp, ++cgs_clp) {
5728         pr2serr("  k=%d, cgs_sel=%d, last_cs=%d, tavp=%p str: %s\n",
5729                 k, (int)cgs_clp->cgs_sel, (int)cgs_clp->last_cs, tavp,
5730                 cgs_clp->cgs_str);
5731     }
5732 #endif
5733 
5734     if (op->dev_name) {
5735         sg_fd = sg_cmds_open_device(op->dev_name, op->o_readonly, vb);
5736         if (sg_fd < 0) {
5737             if (vb)
5738                 pr2serr("open error: %s: %s\n", op->dev_name,
5739                         safe_strerror(-sg_fd));
5740             ret = sg_convert_errno(-sg_fd);
5741             goto early_out;
5742         }
5743         ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb);
5744         if (NULL == ptvp) {
5745             pr2serr("construct pt_base failed, probably out of memory\n");
5746             ret = sg_convert_errno(ENOMEM);
5747             goto err_out;
5748         }
5749         if (! (op->do_raw || have_cgs || (op->do_hex > 2))) {
5750             uint8_t inq_rsp[36];
5751 
5752             memset(inq_rsp, 0, sizeof(inq_rsp));
5753             if ((ret = sg_ll_inquiry_pt(ptvp, false, 0, inq_rsp, 36,
5754                                         0, &resid, ! op->quiet, vb))) {
5755                 pr2serr("%s doesn't respond to a SCSI INQUIRY\n",
5756                         op->dev_name);
5757                 goto err_out;
5758             } else {
5759                 if (resid > 0)
5760                     pr2serr("Short INQUIRY response, not looking good\n");
5761                 printf("  %.8s  %.16s  %.4s\n", inq_rsp + 8, inq_rsp + 16,
5762                        inq_rsp + 32);
5763                 pd_type = PDT_MASK & inq_rsp[0];
5764                 cp = sg_get_pdt_str(pd_type, sizeof(buff), buff);
5765                 if (0xd == pd_type) {
5766                     if (vb)
5767                         printf("    enclosure services device\n");
5768                 } else if (0x40 & inq_rsp[6])
5769                     printf("    %s device has EncServ bit set\n", cp);
5770                 else {
5771                     if (0 != memcmp("NVMe", inq_rsp + 8, 4))
5772                         printf("    %s device (not an enclosure)\n", cp);
5773                 }
5774             }
5775             clear_scsi_pt_obj(ptvp);
5776         }
5777     } else if (op->do_control) {
5778         pr2serr("Cannot do SCSI Send diagnostic command without a DEVICE\n");
5779         return SG_LIB_SYNTAX_ERROR;
5780     }
5781 
5782 #if (HAVE_NVME && (! IGNORE_NVME))
5783     if (ptvp && pt_device_is_nvme(ptvp) && (enc_stat_rsp_sz > 4095)) {
5784         /* Fetch VPD 0xde (vendor specific: sg3_utils) for Identify ctl */
5785         ret = sg_ll_inquiry_pt(ptvp, true, 0xde, enc_stat_rsp, 4096, 0,
5786                                &resid, ! op->quiet, vb);
5787         if (ret) {
5788             if (vb)
5789                 pr2serr("Fetch VPD page 0xde (NVMe Identify ctl) failed, "
5790                         "continue\n");
5791         } else if (resid > 0) {
5792             if (vb)
5793                 pr2serr("VPD page 0xde (NVMe Identify ctl) less than 4096 "
5794                         "bytes, continue\n");
5795         } else {
5796             uint8_t nvmsr;
5797             uint16_t oacs;
5798 
5799             nvmsr = enc_stat_rsp[253];
5800             oacs = sg_get_unaligned_le16(enc_stat_rsp + 256);   /* N.B. LE */
5801             if (vb > 3)
5802                 pr2serr("NVMe Identify ctl response: nvmsr=%u, oacs=0x%x\n",
5803                         nvmsr, oacs);
5804             if (! ((0x2 & nvmsr) && (0x40 & oacs))) {
5805                 pr2serr(">>> Warning: A NVMe enclosure needs both the "
5806                         "enclosure bit and support for\n");
5807                 pr2serr(">>> MI Send+Receive commands bit set; current "
5808                         "state: %s, %s\n", (0x2 & nvmsr) ? "set" : "clear",
5809                         (0x40 & oacs) ? "set" : "clear");
5810             }
5811         }
5812         clear_scsi_pt_obj(ptvp);
5813         memset(enc_stat_rsp, 0, enc_stat_rsp_sz);
5814     }
5815 #endif
5816 
5817     if (ptvp) {
5818         n = (enc_stat_rsp_sz < REQUEST_SENSE_RESP_SZ) ? enc_stat_rsp_sz :
5819                                                         REQUEST_SENSE_RESP_SZ;
5820         ret = sg_ll_request_sense_pt(ptvp, false, enc_stat_rsp, n,
5821                                      ! op->quiet, vb);
5822         if (0 == ret) {
5823             int sense_len = n - get_scsi_pt_resid(ptvp);
5824             struct sg_scsi_sense_hdr ssh;
5825 
5826             if ((sense_len > 7) && sg_scsi_normalize_sense(enc_stat_rsp,
5827                                         sense_len, &ssh)) {
5828                 const char * aa_str = sg_get_asc_ascq_str(ssh.asc, ssh.ascq,
5829                                                           sizeof(b), b);
5830 
5831                 /* Ignore the possibility that multiple UAs queued up */
5832                 if (SPC_SK_UNIT_ATTENTION == ssh.sense_key)
5833                     pr2serr("Unit attention detected: %s\n  ... continue\n",
5834                             aa_str);
5835                 else {
5836                     if (vb) {
5837                         pr2serr("Request Sense near startup detected "
5838                                 "something:\n");
5839                         pr2serr("  Sense key: %s, additional: %s\n  ... "
5840                                 "continue\n",
5841                                 sg_get_sense_key_str(ssh.sense_key,
5842                                          sizeof(buff), buff), aa_str);
5843                     }
5844                 }
5845             }
5846         } else {
5847             if (vb)
5848                 pr2serr("Request sense failed (res=%d), most likely "
5849                         " problems ahead\n", ret);
5850         }
5851         clear_scsi_pt_obj(ptvp);
5852         memset(enc_stat_rsp, 0, enc_stat_rsp_sz);
5853     }
5854 
5855     if (op->nickname_str)
5856         ret = ses_set_nickname(ptvp, op);
5857     else if (have_cgs) {
5858         for (k = 0, tavp = tav_arr, cgs_clp = op->cgs_cl_arr;
5859              k < op->num_cgs; ++k, ++tavp, ++cgs_clp) {
5860             ret = ses_cgs(ptvp, tavp, op,  cgs_clp->last_cs);
5861             if (ret)
5862                 break;
5863         }
5864     } else if (op->do_join)
5865         ret = join_work(ptvp, op, true);
5866     else if (op->do_status)
5867         ret = process_status_page_s(ptvp, op);
5868     else { /* control page requested */
5869         op->data_arr[0] = op->page_code;
5870         op->data_arr[1] = op->byte1;
5871         d_len = op->arr_len + DATA_IN_OFF;
5872         sg_put_unaligned_be16((uint16_t)op->arr_len, op->data_arr + 2);
5873         switch (op->page_code) {
5874         case ENC_CONTROL_DPC:  /* Enclosure Control diagnostic page [0x2] */
5875             printf("Sending Enclosure Control [0x%x] page, with page "
5876                    "length=%d bytes\n", op->page_code, op->arr_len);
5877             ret = do_senddiag(ptvp, op->data_arr, d_len, ! op->quiet, vb);
5878             if (ret) {
5879                 pr2serr("couldn't send Enclosure Control page\n");
5880                 goto err_out;
5881             }
5882             break;
5883         case STRING_DPC:       /* String Out diagnostic page [0x4] */
5884             printf("Sending String Out [0x%x] page, with page length=%d "
5885                    "bytes\n", op->page_code, op->arr_len);
5886             ret = do_senddiag(ptvp, op->data_arr, d_len, ! op->quiet, vb);
5887             if (ret) {
5888                 pr2serr("couldn't send String Out page\n");
5889                 goto err_out;
5890             }
5891             break;
5892         case THRESHOLD_DPC:       /* Threshold Out diagnostic page [0x5] */
5893             printf("Sending Threshold Out [0x%x] page, with page length=%d "
5894                    "bytes\n", op->page_code, op->arr_len);
5895             ret = do_senddiag(ptvp, op->data_arr, d_len, ! op->quiet, vb);
5896             if (ret) {
5897                 pr2serr("couldn't send Threshold Out page\n");
5898                 goto err_out;
5899             }
5900             break;
5901         case ARRAY_CONTROL_DPC:   /* Array control diagnostic page [0x6] */
5902             printf("Sending Array Control [0x%x] page, with page "
5903                    "length=%d bytes\n", op->page_code, op->arr_len);
5904             ret = do_senddiag(ptvp, op->data_arr, d_len, ! op->quiet, vb);
5905             if (ret) {
5906                 pr2serr("couldn't send Array Control page\n");
5907                 goto err_out;
5908             }
5909             break;
5910         case SUBENC_STRING_DPC: /* Subenclosure String Out page [0xc] */
5911             printf("Sending Subenclosure String Out [0x%x] page, with page "
5912                    "length=%d bytes\n", op->page_code, op->arr_len);
5913             ret = do_senddiag(ptvp, op->data_arr, d_len, ! op->quiet, vb);
5914             if (ret) {
5915                 pr2serr("couldn't send Subenclosure String Out page\n");
5916                 goto err_out;
5917             }
5918             break;
5919         case DOWNLOAD_MICROCODE_DPC: /* Download Microcode Control [0xe] */
5920             printf("Sending Download Microcode Control [0x%x] page, with "
5921                    "page length=%d bytes\n", op->page_code, d_len);
5922             printf("  Perhaps it would be better to use the sg_ses_microcode "
5923                    "utility\n");
5924             ret = do_senddiag(ptvp, op->data_arr, d_len, ! op->quiet, vb);
5925             if (ret) {
5926                 pr2serr("couldn't send Download Microcode Control page\n");
5927                 goto err_out;
5928             }
5929             break;
5930         case SUBENC_NICKNAME_DPC: /* Subenclosure Nickname Control [0xf] */
5931             printf("Sending Subenclosure Nickname Control [0x%x] page, with "
5932                    "page length=%d bytes\n", op->page_code, d_len);
5933             ret = do_senddiag(ptvp, op->data_arr, d_len, ! op->quiet, vb);
5934             if (ret) {
5935                 pr2serr("couldn't send Subenclosure Nickname Control page\n");
5936                 goto err_out;
5937             }
5938             break;
5939         default:
5940             pr2serr("Setting SES control page 0x%x not supported by this "
5941                     "utility\n", op->page_code);
5942             pr2serr("That can be done with the sg_senddiag utility with its "
5943                     "'--raw=' option\n");
5944             ret = SG_LIB_SYNTAX_ERROR;
5945             break;
5946         }
5947     }
5948 
5949 err_out:
5950     if (! op->do_status) {
5951         sg_get_category_sense_str(ret, sizeof(b), b, vb);
5952         pr2serr("    %s\n", b);
5953     }
5954     if (free_enc_stat_rsp)
5955         free(free_enc_stat_rsp);
5956     if (free_elem_desc_rsp)
5957         free(free_elem_desc_rsp);
5958     if (free_add_elem_rsp)
5959         free(free_add_elem_rsp);
5960     if (free_threshold_rsp)
5961         free(free_threshold_rsp);
5962 
5963 early_out:
5964     if (sg_fd >= 0) {
5965         res = sg_cmds_close_device(sg_fd);
5966         if (res < 0) {
5967             pr2serr("close error: %s\n", safe_strerror(-res));
5968             if (0 == ret)
5969                 ret = sg_convert_errno(-res);
5970         }
5971     }
5972     if (ptvp)
5973         destruct_scsi_pt_obj(ptvp);
5974     if ((0 == vb) && (! op->quiet)) {
5975         if (! sg_if_can2stderr("sg_ses failed: ", ret))
5976             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
5977                     "more information\n");
5978         else if ((SG_LIB_SYNTAX_ERROR == ret) && (0 == vb))
5979             pr2serr("Add '-h' to command line for usage information\n");
5980     }
5981     if (op->free_data_arr)
5982         free(op->free_data_arr);
5983     if (free_config_dp_resp)
5984         free(free_config_dp_resp);
5985     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
5986 }
5987