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