• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* A utility program originally written for the Linux OS SCSI subsystem.
2  *  Copyright (C) 2000-2022 D. Gilbert
3  *  This program is free software; you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation; either version 2, or (at your option)
6  *  any later version.
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  *
10  * This program outputs information provided by a SCSI LOG SENSE command
11  * and in some cases issues a LOG SELECT command.
12  *
13  */
14 
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <getopt.h>
24 #define __STDC_FORMAT_MACROS 1
25 #include <inttypes.h>
26 #include <errno.h>
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 #include "sg_lib.h"
32 #include "sg_lib_names.h"
33 #include "sg_cmds_basic.h"
34 #ifdef SG_LIB_WIN32
35 #include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
36 #endif
37 #include "sg_unaligned.h"
38 #include "sg_pr2serr.h"
39 
40 static const char * version_str = "2.08 20221112";    /* spc6r06 + sbc5r03 */
41 
42 #define MY_NAME "sg_logs"
43 
44 #define MX_ALLOC_LEN (0xfffc)
45 #define MX_INLEN_ALLOC_LEN (0x800000)
46 #define DEF_INLEN_ALLOC_LEN (0x40000)
47 #define SHORT_RESP_LEN 128
48 
49 #define SUPP_PAGES_LPAGE 0x0
50 #define BUFF_OVER_UNDER_LPAGE 0x1
51 #define WRITE_ERR_LPAGE 0x2
52 #define READ_ERR_LPAGE 0x3
53 #define READ_REV_ERR_LPAGE 0x4
54 #define VERIFY_ERR_LPAGE 0x5
55 #define NON_MEDIUM_LPAGE 0x6
56 #define LAST_N_ERR_LPAGE 0x7
57 #define FORMAT_STATUS_LPAGE 0x8
58 #define LAST_N_DEFERRED_LPAGE 0xb
59 #define LB_PROV_LPAGE 0xc
60 #define TEMPERATURE_LPAGE 0xd
61 #define START_STOP_LPAGE 0xe
62 #define APP_CLIENT_LPAGE 0xf
63 #define SELF_TEST_LPAGE 0x10
64 #define SOLID_STATE_MEDIA_LPAGE 0x11
65 #define REQ_RECOVERY_LPAGE 0x13
66 #define DEVICE_STATS_LPAGE 0x14
67 #define BACKGROUND_SCAN_LPAGE 0x15
68 #define SAT_ATA_RESULTS_LPAGE 0x16
69 #define PROTO_SPECIFIC_LPAGE 0x18
70 #define STATS_LPAGE 0x19
71 #define PCT_LPAGE 0x1a
72 #define TAPE_ALERT_LPAGE 0x2e
73 #define IE_LPAGE 0x2f
74 #define NOT_SPG_SUBPG 0x0                       /* any page: no subpages */
75 #define SUPP_SPGS_SUBPG 0xff                    /* all subpages of ... */
76 #define PENDING_DEFECTS_SUBPG 0x1               /* page 0x15 */
77 #define BACKGROUND_OP_SUBPG 0x2                 /* page 0x15 */
78 #define CACHE_STATS_SUBPG 0x20                  /* page 0x19 */
79 #define CMD_DUR_LIMITS_SUBPG 0x21               /* page 0x19 */
80 #define ENV_REPORTING_SUBPG 0x1                 /* page 0xd */
81 #define UTILIZATION_SUBPG 0x1                   /* page 0xe */
82 #define ENV_LIMITS_SUBPG 0x2                    /* page 0xd */
83 #define LPS_MISALIGNMENT_SUBPG 0x3              /* page 0x15 */
84 #define ZONED_BLOCK_DEV_STATS_SUBPG 0x1         /* page 0x14 */
85 #define LAST_N_INQUIRY_DATA_CH_SUBPG 0x1        /* page 0xb */
86 #define LAST_N_MODE_PG_DATA_CH_SUBPG 0x2        /* page 0xb */
87 
88 /* Vendor product numbers/identifiers */
89 #define VP_NONE   (-1)
90 #define VP_SEAG   0
91 #define VP_HITA   1
92 #define VP_TOSH   2
93 #define VP_LTO5   3
94 #define VP_LTO6   4
95 #define VP_ALL    99
96 
97 #define MVP_OFFSET 8
98 
99 /* Vendor product masks
100  * MVP_STD OR-ed with MVP_<vendor> is a T10 defined lpage with vendor
101  * specific parameter codes (e.g. Information Exceptions lpage [0x2f]) */
102 #define MVP_STD    (1 << (MVP_OFFSET - 1))
103 #define MVP_SEAG   (1 << (VP_SEAG + MVP_OFFSET))
104 #define MVP_HITA   (1 << (VP_HITA + MVP_OFFSET))
105 #define MVP_TOSH   (1 << (VP_TOSH + MVP_OFFSET))
106 #define MVP_LTO5   (1 << (VP_LTO5 + MVP_OFFSET))
107 #define MVP_LTO6   (1 << (VP_LTO6 + MVP_OFFSET))
108 
109 #define OVP_LTO    (MVP_LTO5 | MVP_LTO6)
110 #define OVP_ALL    (~0)
111 
112 
113 #define PCB_STR_LEN 128
114 
115 #define LOG_SENSE_PROBE_ALLOC_LEN 4
116 #define LOG_SENSE_DEF_TIMEOUT 64        /* seconds */
117 
118 static uint8_t * rsp_buff;
119 static uint8_t * free_rsp_buff;
120 static int rsp_buff_sz = MX_ALLOC_LEN + 4;
121 static const int parr_sz = 4096;
122 
123 static const char * const unknown_s = "unknown";
124 static const char * const not_avail = "not available";
125 static const char * const param_c = "Parameter code";
126 static const char * const param_c_sn = "parameter_code";
127 static const char * const as_s_s = "as_string";
128 static const char * const rstrict_s = "restricted";
129 static const char * const rsv_s = "reserved";
130 static const char * const vend_spec = "vendor specific";
131 static const char * const not_rep = "not reported";
132 static const char * const in_hex = "in hex";
133 static const char * const s_key = "sense key";
134 
135 static struct option long_options[] = {
136         {"All", no_argument, 0, 'A'},   /* equivalent to '-aa' */
137         {"ALL", no_argument, 0, 'A'},   /* equivalent to '-aa' */
138         {"all", no_argument, 0, 'a'},
139         {"brief", no_argument, 0, 'b'},
140         {"control", required_argument, 0, 'c'},
141         {"enumerate", no_argument, 0, 'e'},
142         {"exclude", no_argument, 0, 'E'},
143         {"filter", required_argument, 0, 'f'},
144         {"full", no_argument, 0, 'F'},
145         {"help", no_argument, 0, 'h'},
146         {"hex", no_argument, 0, 'H'},
147         {"in", required_argument, 0, 'i'},
148         {"inhex", required_argument, 0, 'i'},
149         {"json", optional_argument, 0, 'j'},
150         {"list", no_argument, 0, 'l'},
151         {"maxlen", required_argument, 0, 'm'},
152         {"name", no_argument, 0, 'n'},
153         {"new", no_argument, 0, 'N'},
154         {"no_inq", no_argument, 0, 'x'},
155         {"no-inq", no_argument, 0, 'x'},
156         {"old", no_argument, 0, 'O'},
157         {"page", required_argument, 0, 'p'},
158         {"paramp", required_argument, 0, 'P'},
159         {"pcb", no_argument, 0, 'q'},
160         {"ppc", no_argument, 0, 'Q'},
161         {"pdt", required_argument, 0, 'D'},
162         {"raw", no_argument, 0, 'r'},
163         {"readonly", no_argument, 0, 'X'},
164         {"reset", no_argument, 0, 'R'},
165         {"sp", no_argument, 0, 's'},
166         {"select", no_argument, 0, 'S'},
167         {"temperature", no_argument, 0, 't'},
168         {"transport", no_argument, 0, 'T'},
169         {"undefined", no_argument, 0, 'u'},
170         {"vendor", required_argument, 0, 'M'},
171         {"verbose", no_argument, 0, 'v'},
172         {"version", no_argument, 0, 'V'},
173         {0, 0, 0, 0},
174 };
175 
176 struct opts_t {
177     bool do_full;
178     bool do_name;
179     bool do_pcb;
180     bool do_ppc;
181     bool do_raw;
182     bool do_pcreset;
183     bool do_select;
184     bool do_sp;
185     bool do_temperature;
186     bool do_transport;
187     bool exclude_vendor;
188     bool filter_given;
189     bool maxlen_given;
190     bool o_readonly;
191     bool opt_new;
192     bool verbose_given;
193     bool version_given;
194     int do_all;
195     int do_brief;
196     int do_enumerate;
197     int do_help;
198     int do_hex;
199     int do_list;
200     int dstrhex_no_ascii;       /* value for dStrHex() no_ascii argument */
201     int hex2str_oformat;        /* value for hex2str() oformat argument */
202     int vend_prod_num;  /* one of the VP_* constants or -1 (def) */
203     int deduced_vpn;    /* deduced vendor_prod_num; from INQUIRY, etc */
204     int verbose;
205     int filter;
206     int page_control;
207     int maxlen;
208     int pg_code;
209     int subpg_code;
210     int paramp;
211     int no_inq;
212     int dev_pdt;        /* from device or --pdt=DT */
213     int decod_subpg_code;
214     int undefined_hex;  /* hex format of undefined/unrecognized fields */
215     const char * device_name;
216     const char * in_fn;
217     const char * pg_arg;
218     const char * vend_prod;
219     const struct log_elem * lep;
220     sgj_state json_st;
221 };
222 
223 
224 struct log_elem {
225     int pg_code;
226     int subpg_code;     /* only unless subpg_high>0 then this is only */
227     int subpg_high;     /* when >0 this is high end of subpage range */
228     int pdt;            /* -1 for all */
229     int flags;          /* bit mask; or-ed with MVP_* constants */
230     const char * name;
231     const char * acron;
232     bool (*show_pagep)(const uint8_t * resp, int len,
233                        struct opts_t * op, sgj_opaque_p jop);
234                         /* Returns true if done */
235 };
236 
237 struct vp_name_t {
238     int vend_prod_num;       /* vendor/product identifier */
239     const char * acron;
240     const char * name;
241     const char * t10_vendorp;
242     const char * t10_productp;
243 };
244 
245 static const char * ls_s = "log_sense: ";
246 
247 static bool show_supported_pgs_page(const uint8_t * resp, int len,
248                                     struct opts_t * op, sgj_opaque_p jop);
249 static bool show_supported_pgs_sub_page(const uint8_t * resp, int len,
250                                         struct opts_t * op, sgj_opaque_p jop);
251 static bool show_buffer_over_under_run_page(const uint8_t * resp, int len,
252                                             struct opts_t * op,
253                                             sgj_opaque_p jop);
254 static bool show_error_counter_page(const uint8_t * resp, int len,
255                                     struct opts_t * op, sgj_opaque_p jop);
256 static bool show_non_medium_error_page(const uint8_t * resp, int len,
257                                        struct opts_t * op, sgj_opaque_p jop);
258 static bool show_last_n_error_page(const uint8_t * resp, int len,
259                                    struct opts_t * op, sgj_opaque_p jop);
260 static bool show_format_status_page(const uint8_t * resp, int len,
261                                     struct opts_t * op, sgj_opaque_p jop);
262 static bool show_last_n_deferred_error_page(const uint8_t * resp, int len,
263                                             struct opts_t * op,
264                                             sgj_opaque_p jop);
265 static bool show_last_n_inq_data_ch_page(const uint8_t * resp, int len,
266                                          struct opts_t * op,
267                                          sgj_opaque_p jop);
268 static bool show_last_n_mode_pg_data_ch_page(const uint8_t * resp, int len,
269                                              struct opts_t * op,
270                                              sgj_opaque_p jop);
271 static bool show_lb_provisioning_page(const uint8_t * resp, int len,
272                                       struct opts_t * op, sgj_opaque_p jop);
273 static bool show_sequential_access_page(const uint8_t * resp, int len,
274                                         struct opts_t * op, sgj_opaque_p jop);
275 static bool show_temperature_page(const uint8_t * resp, int len,
276                                   struct opts_t * op, sgj_opaque_p jop);
277 static bool show_start_stop_page(const uint8_t * resp, int len,
278                                  struct opts_t * op, sgj_opaque_p jop);
279 static bool show_utilization_page(const uint8_t * resp, int len,
280                                   struct opts_t * op, sgj_opaque_p jop);
281 static bool show_app_client_page(const uint8_t * resp, int len,
282                                  struct opts_t * op, sgj_opaque_p jop);
283 static bool show_self_test_page(const uint8_t * resp, int len,
284                                 struct opts_t * op, sgj_opaque_p jop);
285 static bool show_solid_state_media_page(const uint8_t * resp, int len,
286                                         struct opts_t * op, sgj_opaque_p jop);
287 static bool show_device_stats_page(const uint8_t * resp, int len,
288                                    struct opts_t * op, sgj_opaque_p jop);
289 static bool show_media_stats_page(const uint8_t * resp, int len,
290                                   struct opts_t * op, sgj_opaque_p jop);
291 static bool show_dt_device_status_page(const uint8_t * resp, int len,
292                                        struct opts_t * op, sgj_opaque_p jop);
293 static bool show_tapealert_response_page(const uint8_t * resp, int len,
294                                          struct opts_t * op,
295                                          sgj_opaque_p jop);
296 static bool show_requested_recovery_page(const uint8_t * resp, int len,
297                                          struct opts_t * op,
298                                          sgj_opaque_p jop);
299 static bool show_background_scan_results_page(const uint8_t * resp, int len,
300                                               struct opts_t * op,
301                                               sgj_opaque_p jop);
302 static bool show_zoned_block_dev_stats(const uint8_t * resp, int len,
303                                        struct opts_t * op, sgj_opaque_p jop);
304 static bool show_pending_defects_page(const uint8_t * resp, int len,
305                                       struct opts_t * op, sgj_opaque_p jop);
306 static bool show_background_op_page(const uint8_t * resp, int len,
307                                     struct opts_t * op, sgj_opaque_p jop);
308 static bool show_lps_misalignment_page(const uint8_t * resp, int len,
309                                        struct opts_t * op, sgj_opaque_p jop);
310 static bool show_element_stats_page(const uint8_t * resp, int len,
311                                     struct opts_t * op, sgj_opaque_p jop);
312 static bool show_service_buffer_info_page(const uint8_t * resp, int len,
313                                           struct opts_t * op,
314                                           sgj_opaque_p jop);
315 static bool show_ata_pt_results_page(const uint8_t * resp, int len,
316                                      struct opts_t * op, sgj_opaque_p jop);
317 static bool show_tape_diag_data_page(const uint8_t * resp, int len,
318                                      struct opts_t * op, sgj_opaque_p jop);
319 static bool show_mchanger_diag_data_page(const uint8_t * resp, int len,
320                                          struct opts_t * op,
321                                          sgj_opaque_p jop);
322 static bool show_non_volatile_cache_page(const uint8_t * resp, int len,
323                                          struct opts_t * op,
324                                          sgj_opaque_p jop);
325 static bool show_volume_stats_pages(const uint8_t * resp, int len,
326                                     struct opts_t * op, sgj_opaque_p jop);
327 static bool show_protocol_specific_port_page(const uint8_t * resp, int len,
328                                              struct opts_t * op,
329                                              sgj_opaque_p jop);
330 static bool show_stats_perform_pages(const uint8_t * resp, int len,
331                                      struct opts_t * op, sgj_opaque_p jop);
332 static bool show_cache_stats_page(const uint8_t * resp, int len,
333                                   struct opts_t * op, sgj_opaque_p jop);
334 static bool show_power_condition_transitions_page(const uint8_t * resp,
335                                  int len, struct opts_t * op,
336                                                   sgj_opaque_p jop);
337 static bool show_environmental_reporting_page(const uint8_t * resp, int len,
338                                               struct opts_t * op,
339                                               sgj_opaque_p jop);
340 static bool show_environmental_limits_page(const uint8_t * resp, int len,
341                                            struct opts_t * op,
342                                            sgj_opaque_p jop);
343 static bool show_cmd_dur_limits_page(const uint8_t * resp, int len,
344                                      struct opts_t * op, sgj_opaque_p jop);
345 static bool show_data_compression_page(const uint8_t * resp, int len,
346                                        struct opts_t * op, sgj_opaque_p jop);
347 static bool show_tape_alert_ssc_page(const uint8_t * resp, int len,
348                                      struct opts_t * op, sgj_opaque_p jop);
349 static bool show_ie_page(const uint8_t * resp, int len,
350                          struct opts_t * op, sgj_opaque_p jop);
351 static bool show_tape_usage_page(const uint8_t * resp, int len,
352                                  struct opts_t * op, sgj_opaque_p jop);
353 static bool show_tape_capacity_page(const uint8_t * resp, int len,
354                                      struct opts_t * op, sgj_opaque_p jop);
355 static bool show_seagate_cache_page(const uint8_t * resp, int len,
356                                     struct opts_t * op, sgj_opaque_p jop);
357 static bool show_seagate_factory_page(const uint8_t * resp, int len,
358                                       struct opts_t * op, sgj_opaque_p jop);
359 static bool show_hgst_perf_page(const uint8_t * resp, int len,
360                                 struct opts_t * op, sgj_opaque_p jop);
361 static bool show_hgst_misc_page(const uint8_t * resp, int len,
362                                 struct opts_t * op, sgj_opaque_p jop);
363 
364 /* elements in page_number/subpage_number order */
365 static struct log_elem log_arr[] = {
366     {SUPP_PAGES_LPAGE, 0, 0, -1, MVP_STD, "Supported log pages", "sp",
367      show_supported_pgs_page},          /* 0, 0 */
368     {SUPP_PAGES_LPAGE, SUPP_SPGS_SUBPG, 0, -1, MVP_STD, "Supported log pages "
369      "and subpages", "ssp", show_supported_pgs_sub_page}, /* 0, 0xff */
370     {BUFF_OVER_UNDER_LPAGE, 0, 0, -1, MVP_STD, "Buffer over-run/under-run",
371      "bou", show_buffer_over_under_run_page},  /* 0x1, 0x0 */
372     {WRITE_ERR_LPAGE, 0, 0, -1, MVP_STD, "Write error counters", "we",
373      show_error_counter_page},          /* 0x2, 0x0 */
374     {READ_ERR_LPAGE, 0, 0, -1, MVP_STD, "Read error counters", "re",
375      show_error_counter_page},          /* 0x3, 0x0 */
376     {READ_REV_ERR_LPAGE, 0, 0, -1, MVP_STD, "Read reverse error counters",
377      "rre", show_error_counter_page},          /* 0x4, 0x0 */
378     {VERIFY_ERR_LPAGE, 0, 0, -1, MVP_STD, "Verify error counters", "ve",
379      show_error_counter_page},          /* 0x5, 0x0 */
380     {NON_MEDIUM_LPAGE, 0, 0, -1, MVP_STD, "Non medium", "nm",
381      show_non_medium_error_page},       /* 0x6, 0x0 */
382     {LAST_N_ERR_LPAGE, 0, 0, -1, MVP_STD, "Last n error", "lne",
383      show_last_n_error_page},           /* 0x7, 0x0 */
384     {FORMAT_STATUS_LPAGE, 0, 0, 0, MVP_STD, "Format status", "fs",
385      show_format_status_page},          /* 0x8, 0x0  SBC */
386     {LAST_N_DEFERRED_LPAGE, 0, 0, -1, MVP_STD, "Last n deferred error", "lnd",
387      show_last_n_deferred_error_page},  /* 0xb, 0x0 */
388     {LAST_N_DEFERRED_LPAGE, LAST_N_INQUIRY_DATA_CH_SUBPG, 0, -1, MVP_STD,
389      "Last n inquiry data changed", "lnic",
390      show_last_n_inq_data_ch_page},     /* 0xb, 0x1 */
391     {LAST_N_DEFERRED_LPAGE, LAST_N_MODE_PG_DATA_CH_SUBPG, 0, -1, MVP_STD,
392      "Last n mode page data changed", "lnmc",
393      show_last_n_mode_pg_data_ch_page}, /* 0xb, 0x2 */
394     {LB_PROV_LPAGE, 0, 0, 0, MVP_STD, "Logical block provisioning", "lbp",
395      show_lb_provisioning_page},        /* 0xc, 0x0  SBC */
396     {0xc, 0, 0, PDT_TAPE, MVP_STD, "Sequential access device", "sad",
397      show_sequential_access_page},      /* 0xc, 0x0  SSC */
398     {TEMPERATURE_LPAGE, 0, 0, -1, MVP_STD, "Temperature", "temp",
399      show_temperature_page},            /* 0xd, 0x0 */
400     {TEMPERATURE_LPAGE, ENV_REPORTING_SUBPG, 0, -1, MVP_STD,  /* 0xd, 0x1 */
401      "Environmental reporting", "enr", show_environmental_reporting_page},
402     {TEMPERATURE_LPAGE, ENV_LIMITS_SUBPG, 0, -1, MVP_STD,     /* 0xd, 0x2 */
403      "Environmental limits", "enl", show_environmental_limits_page},
404     {START_STOP_LPAGE, 0, 0, -1, MVP_STD, "Start-stop cycle counter", "sscc",
405      show_start_stop_page},             /* 0xe, 0x0 */
406     {START_STOP_LPAGE, UTILIZATION_SUBPG, 0, 0, MVP_STD, "Utilization",
407      "util", show_utilization_page},    /* 0xe, 0x1 SBC */    /* sbc4r04 */
408     {APP_CLIENT_LPAGE, 0, 0, -1, MVP_STD, "Application client", "ac",
409      show_app_client_page},             /* 0xf, 0x0 */
410     {SELF_TEST_LPAGE, 0, 0, -1, MVP_STD, "Self test results", "str",
411      show_self_test_page},              /* 0x10, 0x0 */
412     {SOLID_STATE_MEDIA_LPAGE, 0, 0, 0, MVP_STD, "Solid state media", "ssm",
413      show_solid_state_media_page},      /* 0x11, 0x0  SBC */
414     {0x11, 0, 0, PDT_TAPE, MVP_STD, "DT Device status", "dtds",
415      show_dt_device_status_page},       /* 0x11, 0x0  SSC,ADC */
416     {0x12, 0, 0, PDT_TAPE, MVP_STD, "Tape alert response", "tar",
417      show_tapealert_response_page},      /* 0x12, 0x0  SSC,ADC */
418     {REQ_RECOVERY_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Requested recovery", "rr",
419      show_requested_recovery_page},     /* 0x13, 0x0  SSC,ADC */
420     {DEVICE_STATS_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Device statistics", "ds",
421      show_device_stats_page},           /* 0x14, 0x0  SSC,ADC */
422     {DEVICE_STATS_LPAGE, 0, 0, PDT_MCHANGER, MVP_STD,   /* 0x14, 0x0  SMC */
423      "Media changer statistics", "mcs", show_media_stats_page},
424     {DEVICE_STATS_LPAGE, ZONED_BLOCK_DEV_STATS_SUBPG,   /* 0x14,0x1 zbc2r01 */
425      0, 0, MVP_STD, "Zoned block device statistics", "zbds",
426      show_zoned_block_dev_stats},
427     {BACKGROUND_SCAN_LPAGE, 0, 0, 0, MVP_STD, "Background scan results",
428      "bsr", show_background_scan_results_page}, /* 0x15, 0x0  SBC */
429     {BACKGROUND_SCAN_LPAGE, BACKGROUND_OP_SUBPG, 0, 0, MVP_STD,
430      "Background operation", "bop", show_background_op_page},
431                                         /* 0x15, 0x2  SBC */
432     {BACKGROUND_SCAN_LPAGE, LPS_MISALIGNMENT_SUBPG, 0, 0, MVP_STD,
433      "LPS misalignment", "lps", show_lps_misalignment_page},
434                                         /* 0x15, 0x3  SBC-4 */
435     {0x15, 0, 0, PDT_MCHANGER, MVP_STD, "Element statistics", "els",
436      show_element_stats_page},          /* 0x15, 0x0  SMC */
437     {0x15, 0, 0, PDT_ADC, MVP_STD, "Service buffers information", "sbi",
438      show_service_buffer_info_page},    /* 0x15, 0x0  ADC */
439     {BACKGROUND_SCAN_LPAGE, PENDING_DEFECTS_SUBPG, 0, 0, MVP_STD,
440      "Pending defects", "pd", show_pending_defects_page}, /* 0x15, 0x1  SBC */
441     {SAT_ATA_RESULTS_LPAGE, 0, 0, 0, MVP_STD, "ATA pass-through results",
442      "aptr", show_ata_pt_results_page}, /* 0x16, 0x0  SAT */
443     {0x16, 0, 0, PDT_TAPE, MVP_STD, "Tape diagnostic data", "tdd",
444      show_tape_diag_data_page},         /* 0x16, 0x0  SSC */
445     {0x16, 0, 0, PDT_MCHANGER, MVP_STD, "Media changer diagnostic data",
446      "mcdd", show_mchanger_diag_data_page}, /* 0x16, 0x0  SMC */
447     {0x17, 0, 0, 0, MVP_STD, "Non volatile cache", "nvc",
448      show_non_volatile_cache_page},     /* 0x17, 0x0  SBC */
449     {0x17, 0, 0xf, PDT_TAPE, MVP_STD, "Volume statistics", "vs",
450      show_volume_stats_pages},          /* 0x17, 0x0...0xf  SSC */
451     {PROTO_SPECIFIC_LPAGE, 0, 0, -1, MVP_STD, "Protocol specific port",
452      "psp", show_protocol_specific_port_page},  /* 0x18, 0x0  */
453     {STATS_LPAGE, 0, 0, -1, MVP_STD, "General Statistics and Performance",
454      "gsp", show_stats_perform_pages},  /* 0x19, 0x0  */
455     {STATS_LPAGE, 0x1, 0x1f, -1, MVP_STD, "Group Statistics and Performance",
456      "grsp", show_stats_perform_pages}, /* 0x19, 0x1...0x1f  */
457     {STATS_LPAGE, CACHE_STATS_SUBPG, 0, -1, MVP_STD,    /* 0x19, 0x20  */
458      "Cache memory statistics", "cms", show_cache_stats_page},
459     {STATS_LPAGE, CMD_DUR_LIMITS_SUBPG, 0, -1, MVP_STD, /* 0x19, 0x21  */
460      "Command duration limits statistics", "cdl",
461      show_cmd_dur_limits_page /* spc6r01 */ },
462     {PCT_LPAGE, 0, 0, -1, MVP_STD, "Power condition transitions", "pct",
463      show_power_condition_transitions_page}, /* 0x1a, 0  */
464     {0x1b, 0, 0, PDT_TAPE, MVP_STD, "Data compression", "dc",
465      show_data_compression_page},       /* 0x1b, 0  SSC */
466     {0x2d, 0, 0, PDT_TAPE, MVP_STD, "Current service information", "csi",
467      NULL},                             /* 0x2d, 0  SSC */
468     {TAPE_ALERT_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Tape alert", "ta",
469      show_tape_alert_ssc_page},         /* 0x2e, 0  SSC */
470     {IE_LPAGE, 0, 0, -1, (MVP_STD | MVP_HITA),
471      "Informational exceptions", "ie", show_ie_page},       /* 0x2f, 0  */
472 /* vendor specific */
473     {0x30, 0, 0, PDT_DISK, MVP_HITA, "Performance counters (Hitachi)",
474      "pc_hi", show_hgst_perf_page},     /* 0x30, 0  SBC */
475     {0x30, 0, 0, PDT_TAPE, OVP_LTO, "Tape usage (lto-5, 6)", "tu_",
476      show_tape_usage_page},             /* 0x30, 0  SSC */
477     {0x31, 0, 0, PDT_TAPE, OVP_LTO, "Tape capacity (lto-5, 6)",
478      "tc_", show_tape_capacity_page},   /* 0x31, 0  SSC */
479     {0x32, 0, 0, PDT_TAPE, MVP_LTO5, "Data compression (lto-5)",
480      "dc_", show_data_compression_page}, /* 0x32, 0  SSC; redirect to 0x1b */
481     {0x33, 0, 0, PDT_TAPE, MVP_LTO5, "Write errors (lto-5)", "we_",
482      NULL},                             /* 0x33, 0  SSC */
483     {0x34, 0, 0, PDT_TAPE, MVP_LTO5, "Read forward errors (lto-5)",
484      "rfe_", NULL},                             /* 0x34, 0  SSC */
485     {0x35, 0, 0, PDT_TAPE, OVP_LTO, "DT Device Error (lto-5, 6)",
486      "dtde_", NULL},                             /* 0x35, 0  SSC */
487     {0x37, 0, 0, PDT_DISK, MVP_SEAG, "Cache (seagate)", "c_se",
488      show_seagate_cache_page},          /* 0x37, 0  SBC */
489     {0x37, 0, 0, PDT_DISK, MVP_HITA, "Miscellaneous (hitachi)", "mi_hi",
490      show_hgst_misc_page},                             /* 0x37, 0  SBC */
491     {0x37, 0, 0, PDT_TAPE, MVP_LTO5, "Performance characteristics "
492      "(lto-5)", "pc_", NULL},                             /* 0x37, 0  SSC */
493     {0x38, 0, 0, PDT_TAPE, MVP_LTO5, "Blocks/bytes transferred "
494      "(lto-5)", "bbt_", NULL},                             /* 0x38, 0  SSC */
495     {0x39, 0, 0, PDT_TAPE, MVP_LTO5, "Host port 0 interface errors "
496      "(lto-5)", "hp0_", NULL},                             /* 0x39, 0  SSC */
497     {0x3a, 0, 0, PDT_TAPE, MVP_LTO5, "Drive control verification "
498      "(lto-5)", "dcv_", NULL},                             /* 0x3a, 0  SSC */
499     {0x3b, 0, 0, PDT_TAPE, MVP_LTO5, "Host port 1 interface errors "
500      "(lto-5)", "hp1_", NULL},                             /* 0x3b, 0  SSC */
501     {0x3c, 0, 0, PDT_TAPE, MVP_LTO5, "Drive usage information "
502      "(lto-5)", "dui_", NULL},                             /* 0x3c, 0  SSC */
503     {0x3d, 0, 0, PDT_TAPE, MVP_LTO5, "Subsystem statistics (lto-5)",
504      "ss_", NULL},                             /* 0x3d, 0  SSC */
505     {0x3e, 0, 0, PDT_DISK, MVP_SEAG, "Factory (seagate)", "f_se",
506      show_seagate_factory_page},        /* 0x3e, 0  SBC */
507     {0x3e, 0, 0, PDT_DISK, MVP_HITA, "Factory (hitachi)", "f_hi",
508      NULL},                             /* 0x3e, 0  SBC */
509     {0x3e, 0, 0, PDT_TAPE, OVP_LTO, "Device Status (lto-5, 6)",
510      "ds_", NULL},                             /* 0x3e, 0  SSC */
511 
512     {-1, -1, -1, -1, 0, NULL, "zzzzz", NULL},           /* end sentinel */
513 };
514 
515 /* Supported vendor product codes */
516 /* Arrange in alphabetical order by acronym */
517 static struct vp_name_t vp_arr[] = {
518     {VP_SEAG, "sea", "Seagate", "SEAGATE", NULL},
519     {VP_HITA, "hit", "Hitachi", "HGST", NULL},
520     {VP_HITA, "wdc", "WDC/Hitachi", "WDC", NULL},
521     {VP_TOSH, "tos", "Toshiba", "TOSHIBA", NULL},
522     {VP_LTO5, "lto5", "LTO-5 (tape drive consortium)", NULL, NULL},
523     {VP_LTO6, "lto6", "LTO-6 (tape drive consortium)", NULL, NULL},
524     {VP_ALL, "all", "enumerate all vendor specific", NULL, NULL},
525     {0, NULL, NULL, NULL, NULL},
526 };
527 
528 static char t10_vendor_str[10];
529 static char t10_product_str[18];
530 
531 #ifdef SG_LIB_WIN32
532 static bool win32_spt_init_state = false;
533 static bool win32_spt_curr_state = false;
534 #endif
535 
536 
537 static void
usage(int hval)538 usage(int hval)
539 {
540     if (1 == hval) {
541         pr2serr(
542            "Usage: sg_logs [-ALL] [--all] [--brief] [--control=PC] "
543            "[--enumerate]\n"
544            "               [--exclude] [--filter=FL] [--full] [--help] "
545            "[--hex]\n"
546            "               [--in=FN] [--json[=JO]] [--list] [--maxlen=LEN] "
547            "[--name]\n"
548            "               [--no_inq] [--page=PG] [--paramp=PP] [--pcb] "
549            "[--ppc]\n"
550            "               [--pdt=DT] [--raw] [--readonly] [--reset] "
551            "[--select]\n"
552            "               [--sp] [--temperature] [--transport] "
553            "[--undefined]\n"
554            "               [--vendor=VP] [--verbose] [--version] DEVICE\n"
555            "  where the main options are:\n"
556            "    --ALL|-A        fetch and decode all log pages and "
557            "subpages\n"
558            "    --all|-a        fetch and decode all log pages, but not "
559            "subpages; use\n"
560            "                    twice to fetch and decode all log pages "
561            "and subpages\n"
562            "    --brief|-b      shorten the output of some log pages\n"
563            "    --enumerate|-e    enumerate known pages, ignore DEVICE. "
564            "Sort order,\n"
565            "                      '-e': all by acronym; '-ee': non-vendor "
566            "by acronym;\n"
567            "                      '-eee': all numerically; '-eeee': "
568            "non-v numerically\n"
569            "    --filter=FL|-f FL    FL is parameter code to display (def: "
570            "all);\n"
571            "                         with '-e' then FL>=0 enumerate that "
572            "pdt + spc\n"
573            "                         FL=-1 all (default), FL=-2 spc only\n"
574            "    --full|-F       drill down in application client log page\n"
575            "    --help|-h       print usage message then exit. Use twice "
576            "for more help\n"
577            "    --hex|-H        output response in hex (default: decode if "
578            "known)\n"
579            "    --in=FN|-i FN    FN is a filename containing a log page "
580            "in ASCII hex\n"
581            "                     or binary if --raw also given. --inhex=FN "
582            "also accepted\n"
583            "    --json[=JO]|-j[JO]    output in JSON instead of human "
584             "readable\n"
585             "                          test. Use --json=? for JSON help\n"
586            "    --list|-l       list supported log pages; twice: list "
587            "supported log\n"
588            "                    pages and subpages page; thrice: merge of "
589            "both pages\n"
590            "    --page=PG|-p PG    PG is either log page acronym, PGN or "
591            "PGN,SPGN\n"
592            "                       where (S)PGN is a (sub) page number\n");
593         pr2serr(
594            "    --raw|-r        either output response in binary to stdout "
595            "or, if\n"
596            "                    '--in=FN' is given, FN is decoded as "
597            "binary\n"
598            "    --temperature|-t    decode temperature (log page 0xd or "
599            "0x2f)\n"
600            "    --transport|-T    decode transport (protocol specific port "
601            "0x18) page\n"
602            "    --vendor=VP|-M VP    vendor/product abbreviation [or "
603            "number]\n"
604            "    --verbose|-v    increase verbosity\n\n"
605            "Performs a SCSI LOG SENSE (or LOG SELECT) command and decodes "
606            "the response.\nIf only DEVICE is given then '-p sp' (supported "
607            "pages) is assumed. Use\n'-e' to see known pages and their "
608            "acronyms. For more help use '-hh'.\n");
609     } else if (hval > 1) {
610         pr2serr(
611            "  where sg_logs' lesser used options are:\n"
612            "    --control=PC|-c PC    page control(PC) (default: 1)\n"
613            "                          0: current threshold, 1: current "
614            "cumulative\n"
615            "                          2: default threshold, 3: default "
616            "cumulative\n"
617            "    --exclude|-E    exclude vendor specific pages and "
618            "parameters\n"
619            "    --list|-l       list supported log page names (equivalent to "
620            "'-p sp')\n"
621            "                    use twice to list supported log page and "
622            "subpage names\n"
623            "    --maxlen=LEN|-m LEN    max response length (def: 0 "
624            "-> everything)\n"
625            "                           when > 1 will request LEN bytes\n"
626            "    --name|-n       decode some pages into multiple name=value "
627            "lines\n"
628            "    --no_inq|-x     no initial INQUIRY output (twice: and no "
629            "INQUIRY call)\n"
630            "    --old|-O        use old interface (use as first option)\n"
631            "    --paramp=PP|-P PP    place PP in parameter pointer field in "
632            "cdb (def: 0)\n"
633            "    --pcb|-q        show parameter control bytes in decoded "
634            "output\n"
635            "    --ppc|-Q        set the Parameter Pointer Control (PPC) bit "
636            "(def: 0)\n"
637            "    --pdt=DT|-D DT    DT is peripheral device type to use with "
638            "'--in=FN'\n"
639            "                      or when '--no_inq' is used\n"
640            "    --readonly|-X    open DEVICE read-only (def: first "
641            "read-write then if\n"
642            "                     fails try open again read-only)\n"
643            "    --reset|-R      reset log parameters (takes PC and SP into "
644            "account)\n"
645            "                    (uses PCR bit in LOG SELECT)\n"
646            "    --select|-S     perform LOG SELECT (def: LOG SENSE)\n"
647            "    --sp|-s         set the Saving Parameters (SP) bit (def: "
648            "0)\n"
649            "    --undefined|-u    hex format for undefined/unrecognized "
650            "fields,\n"
651            "                      use one or more times; format as per "
652            "--hex\n"
653            "    --version|-V    output version string then exit\n\n"
654            "If DEVICE and --select are given, a LOG SELECT command will be "
655            "issued.\nIf DEVICE is not given and '--in=FN' is given then FN "
656            "will decoded as if\nit were a log page. The contents of FN "
657            "generated by either a prior\n'sg_logs -HHH ...' invocation or "
658            "by a text editor.\nLog pages defined in SPC are common "
659            "to all device types.\n");
660     }
661 }
662 
663 static void
usage_old()664 usage_old()
665 {
666     printf("Usage: sg_logs [-a] [-A] [-b] [-c=PC] [-D=DT] [-e] [-E] [-f=FL] "
667            "[-F]\n"
668            "               [-h] [-H] [-i=FN] [-l] [-L] [-m=LEN] [-M=VP] "
669            "[-n] [-p=PG]\n"
670            "               [-paramp=PP] [-pcb] [-ppc] [-r] [-select] [-sp] "
671            "[-t] [-T]\n"
672            "               [-u] [-v] [-V] [-x] [-X] [-?] DEVICE\n"
673            "  where:\n"
674            "    -a     fetch and decode all log pages\n"
675            "    -A     fetch and decode all log pages and subpages\n"
676            "    -b     shorten the output of some log pages\n"
677            "    -c=PC    page control(PC) (default: 1)\n"
678            "                  0: current threshold, 1: current cumulative\n"
679            "                  2: default threshold, 3: default cumulative\n"
680            "    -e     enumerate known log pages\n"
681            "    -D=DT    DT is peripheral device type to use with "
682            "'--in=FN'\n"
683            "    -E     exclude vendor specific pages and parameters\n"
684            "    -f=FL    filter match parameter code or pdt\n"
685            "    -F     drill down in application client log page\n"
686            "    -h     output in hex (default: decode if known)\n"
687            "    -H     output in hex (same as '-h')\n"
688            "    -i=FN    FN is a filename containing a log page "
689            "in ASCII hex.\n"
690            "    -l     list supported log page names (equivalent to "
691            "'-p=0')\n"
692            "    -L     list supported log page and subpages names "
693            "(equivalent to\n"
694            "           '-p=0,ff')\n"
695            "    -m=LEN   max response length (decimal) (def: 0 "
696            "-> everything)\n"
697            "    -M=VP    vendor/product abbreviation [or number]\n"
698            "    -n       decode some pages into multiple name=value "
699            "lines\n"
700            "    -N|--new    use new interface\n"
701            "    -p=PG    PG is an acronym (def: 'sp')\n"
702            "    -p=PGN    page code in hex (def: 0)\n"
703            "    -p=PGN,SPGN    page and subpage codes in hex, (defs: 0,0)\n"
704            "    -paramp=PP   (in hex) (def: 0)\n"
705            "    -pcb     show parameter control bytes in decoded "
706            "output\n");
707     printf("    -ppc     set the Parameter Pointer Control (PPC) bit "
708            "(def: 0)\n"
709            "    -r       reset log parameters (takes PC and SP into "
710            "account)\n"
711            "             (uses PCR bit in LOG SELECT)\n"
712            "    -select  perform LOG SELECT (def: LOG SENSE)\n"
713            "    -sp      set the Saving Parameters (SP) bit (def: 0)\n"
714            "    -t       outputs temperature log page (0xd)\n"
715            "    -T       outputs transport (protocol specific port) log "
716            "page (0x18)\n"
717            "    -u       hex format for undefined/unrecognized fields\n"
718            "    -v       increase verbosity\n"
719            "    -V       output version string\n"
720            "    -x       no initial INQUIRY output (twice: no INQUIRY call)\n"
721            "    -X       open DEVICE read-only (def: first read-write then "
722            "if fails\n"
723            "             try open again with read-only)\n"
724            "    -?       output this usage message\n\n"
725            "Performs a SCSI LOG SENSE (or LOG SELECT) command\n");
726 }
727 
728 /* Return vendor product mask given vendor product number */
729 static int
get_vp_mask(int vpn)730 get_vp_mask(int vpn)
731 {
732     if (vpn < 0)
733         return 0;
734     else
735         return (vpn >= (32 - MVP_OFFSET)) ?  OVP_ALL :
736                                              (1 << (vpn + MVP_OFFSET));
737 }
738 
739 static int
asort_comp(const void * lp,const void * rp)740 asort_comp(const void * lp, const void * rp)
741 {
742     const struct log_elem * const * lepp =
743                 (const struct log_elem * const *)lp;
744     const struct log_elem * const * repp =
745                 (const struct log_elem * const *)rp;
746 
747     return strcmp((*lepp)->acron, (*repp)->acron);
748 }
749 
750 static void
enumerate_helper(const struct log_elem * lep,bool first,const struct opts_t * op)751 enumerate_helper(const struct log_elem * lep, bool first,
752                  const struct opts_t * op)
753 {
754     char b[80];
755     char bb[80];
756     const char * cp;
757     bool vendor_lpage = ! (MVP_STD & lep->flags);
758 
759     if (first) {
760         if (1 == op->verbose) {
761             printf("acronym   pg[,spg]        name\n");
762             printf("===============================================\n");
763         } else if (2 == op->verbose) {
764             printf("acronym   pg[,spg]        pdt   name\n");
765             printf("===================================================\n");
766         }
767     }
768     if ((0 == (op->do_enumerate % 2)) && vendor_lpage)
769         return;     /* if do_enumerate is even then skip vendor pages */
770     else if ((! op->filter_given) || (-1 == op->filter))
771         ;           /* otherwise enumerate all lpages if no --filter= */
772     else if (-2 == op->filter) {   /* skip non-SPC pages */
773         if (lep->pdt >= 0)
774             return;
775     } else if (-10 == op->filter) {   /* skip non-disk like pages */
776         if (sg_lib_pdt_decay(lep->pdt) != 0)
777             return;
778     } else if (-11 == op->filter) {   /* skip tape like device pages */
779         if (sg_lib_pdt_decay(lep->pdt) != 1)
780             return;
781     } else if ((op->filter >= 0) && (op->filter <= 0x1f)) {
782         if ((lep->pdt >= 0) && (lep->pdt != op->filter) &&
783             (lep->pdt != sg_lib_pdt_decay(op->filter)))
784             return;
785     }
786     if (op->vend_prod_num >= 0) {
787         if (! (lep->flags & get_vp_mask(op->vend_prod_num)))
788             return;
789     }
790     if (op->deduced_vpn >= 0) {
791         if (! (lep->flags & get_vp_mask(op->deduced_vpn)))
792             return;
793     }
794     if (lep->subpg_high > 0)
795         snprintf(b, sizeof(b), "0x%x,0x%x->0x%x", lep->pg_code,
796                  lep->subpg_code, lep->subpg_high);
797     else if (lep->subpg_code > 0)
798         snprintf(b, sizeof(b), "0x%x,0x%x", lep->pg_code,
799                  lep->subpg_code);
800     else
801         snprintf(b, sizeof(b), "0x%x", lep->pg_code);
802     snprintf(bb, sizeof(bb), "%-16s", b);
803     cp = (op->verbose && (! lep->show_pagep)) ? " [hex only]" : "";
804     if (op->verbose > 1) {
805         if (lep->pdt < 0)
806             printf("  %-8s%s-     %s%s\n", lep->acron, bb, lep->name, cp);
807         else
808             printf("  %-8s%s0x%02x  %s%s\n", lep->acron, bb, lep->pdt,
809                    lep->name, cp);
810     } else
811         printf("  %-8s%s%s%s\n", lep->acron, bb, lep->name, cp);
812 }
813 
814 static void
enumerate_pages(const struct opts_t * op)815 enumerate_pages(const struct opts_t * op)
816 {
817     int j;
818     struct log_elem * lep;
819     struct log_elem ** lep_arr;
820 
821     if (op->do_enumerate < 3) { /* -e, -ee: sort by acronym */
822         int k;
823         struct log_elem ** lepp;
824 
825         for (k = 0, lep = log_arr; lep->pg_code >=0; ++lep, ++k)
826             ;
827         ++k;
828         lep_arr = (struct log_elem **)calloc(k, sizeof(struct log_elem *));
829         if (NULL == lep_arr) {
830             pr2serr("%s: out of memory\n", __func__);
831             return;
832         }
833         for (k = 0, lep = log_arr; lep->pg_code >=0; ++lep, ++k)
834             lep_arr[k] = lep;
835         lep_arr[k++] = lep;     /* put sentinel on end */
836         qsort(lep_arr, k, sizeof(struct log_elem *), asort_comp);
837         printf("Known log pages in acronym order:\n");
838         for (lepp = lep_arr, j = 0; (*lepp)->pg_code >=0; ++lepp, ++j)
839             enumerate_helper(*lepp, (0 == j), op);
840         free(lep_arr);
841     } else {    /* -eee, -eeee numeric sort (as per table) */
842         printf("Known log pages in numerical order:\n");
843         for (lep = log_arr, j = 0; lep->pg_code >=0; ++lep, ++j)
844             enumerate_helper(lep, (0 == j), op);
845     }
846 }
847 
848 static const struct log_elem *
acron_search(const char * acron)849 acron_search(const char * acron)
850 {
851     const struct log_elem * lep;
852 
853     for (lep = log_arr; lep->pg_code >=0; ++lep) {
854         if (0 == strcmp(acron, lep->acron))
855             return lep;
856     }
857     return NULL;
858 }
859 
860 static int
find_vpn_by_acron(const char * vp_ap)861 find_vpn_by_acron(const char * vp_ap)
862 {
863     const struct vp_name_t * vpp;
864 
865     for (vpp = vp_arr; vpp->acron; ++vpp) {
866         size_t k;
867         size_t len = strlen(vpp->acron);
868 
869         for (k = 0; k < len; ++k) {
870             if (tolower((uint8_t)vp_ap[k]) != (uint8_t)vpp->acron[k])
871                 break;
872         }
873         if (k < len)
874             continue;
875         return vpp->vend_prod_num;
876     }
877     return VP_NONE;
878 }
879 
880 /* Find vendor product number using T10 VENDOR and PRODUCT ID fields in a
881    INQUIRY response. */
882 static int
find_vpn_by_inquiry(void)883 find_vpn_by_inquiry(void)
884 {
885     size_t len;
886     size_t t10_v_len = strlen(t10_vendor_str);
887     size_t t10_p_len = strlen(t10_product_str);
888     const struct vp_name_t * vpp;
889 
890     if ((0 == t10_v_len) && (0 == t10_p_len))
891         return VP_NONE;
892     for (vpp = vp_arr; vpp->acron; ++vpp) {
893         bool matched = false;
894 
895         if (vpp->t10_vendorp && (t10_v_len > 0)) {
896             len = strlen(vpp->t10_vendorp);
897             len = (len > t10_v_len) ? t10_v_len : len;
898             if (strncmp(vpp->t10_vendorp, t10_vendor_str, len))
899                 continue;
900             matched = true;
901         }
902         if (vpp->t10_productp && (t10_p_len > 0)) {
903             len = strlen(vpp->t10_productp);
904             len = (len > t10_p_len) ? t10_p_len : len;
905             if (strncmp(vpp->t10_productp, t10_product_str, len))
906                 continue;
907             matched = true;
908         }
909         if (matched)
910             return vpp->vend_prod_num;
911     }
912     return VP_NONE;
913 }
914 
915 static void
enumerate_vp(void)916 enumerate_vp(void)
917 {
918     const struct vp_name_t * vpp;
919     bool seen = false;
920 
921     for (vpp = vp_arr; vpp->acron; ++vpp) {
922         if (vpp->name) {
923             if (! seen) {
924                 printf("\nVendor/product identifiers:\n");
925                 seen = true;
926             }
927             printf("  %-10s %d      %s\n", vpp->acron,
928                    vpp->vend_prod_num, vpp->name);
929         }
930     }
931 }
932 
933 static const struct log_elem *
pg_subpg_pdt_search(int pg_code,int subpg_code,int pdt,int vpn)934 pg_subpg_pdt_search(int pg_code, int subpg_code, int pdt, int vpn)
935 {
936     const struct log_elem * lep;
937     int d_pdt;
938     int vp_mask = get_vp_mask(vpn);
939 
940     d_pdt = sg_lib_pdt_decay(pdt);
941     for (lep = log_arr; lep->pg_code >=0; ++lep) {
942         if (pg_code == lep->pg_code) {
943             if (subpg_code == lep->subpg_code) {
944                 if ((MVP_STD & lep->flags) || (0 == vp_mask) ||
945                     (vp_mask & lep->flags))
946                     ;
947                 else
948                     continue;
949                 if ((lep->pdt < 0) || (pdt == lep->pdt) || (pdt < 0))
950                     return lep;
951                 else if (d_pdt == lep->pdt)
952                     return lep;
953                 else if (pdt == sg_lib_pdt_decay(lep->pdt))
954                     return lep;
955             } else if ((lep->subpg_high > 0) &&
956                      (subpg_code > lep->subpg_code) &&
957                      (subpg_code <= lep->subpg_high))
958                 return lep;
959         }
960     }
961     return NULL;
962 }
963 
964 static void
js_snakenv_ihexstr_nex(sgj_state * jsp,sgj_opaque_p jop,const char * conv2sname,int64_t val_i,bool hex_as_well,const char * str_name,const char * val_s,const char * nex_s)965 js_snakenv_ihexstr_nex(sgj_state * jsp, sgj_opaque_p jop,
966                        const char * conv2sname, int64_t val_i,
967                        bool hex_as_well, const char * str_name,
968                        const char * val_s, const char * nex_s)
969 {
970 
971     if ((NULL == jsp) || (NULL == jop))
972         return;
973     if (sgj_is_snake_name(conv2sname))
974         sgj_js_nv_ihexstr_nex(jsp, jop, conv2sname, val_i, hex_as_well,
975                               str_name, val_s, nex_s);
976     else {
977         char b[128];
978 
979         sgj_convert_to_snake_name(conv2sname, b, sizeof(b));
980         sgj_js_nv_ihexstr_nex(jsp, jop, b, val_i, hex_as_well, str_name,
981                               val_s, nex_s);
982     }
983 }
984 
985 static void
usage_for(int hval,const struct opts_t * op)986 usage_for(int hval, const struct opts_t * op)
987 {
988     if (op->opt_new)
989         usage(hval);
990     else
991         usage_old();
992 }
993 
994 /* Processes command line options according to new option format. Returns
995  * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
996 static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])997 new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
998 {
999     while (1) {
1000         int c, n;
1001         int option_index = 0;
1002 
1003         c = getopt_long(argc, argv, "aAbc:D:eEf:FhHi:j::lLm:M:nNOp:P:qQrRsStT"
1004                         "uvVxX", long_options, &option_index);
1005         if (c == -1)
1006             break;
1007 
1008         switch (c) {
1009         case 'a':
1010             ++op->do_all;
1011             break;
1012         case 'A':
1013             op->do_all += 2;
1014             break;
1015         case 'b':
1016             ++op->do_brief;
1017             break;
1018         case 'c':
1019             n = sg_get_num(optarg);
1020             if ((n < 0) || (n > 3)) {
1021                 pr2serr("bad argument to '--control='\n");
1022                 usage(2);
1023                 return SG_LIB_SYNTAX_ERROR;
1024             }
1025             op->page_control = n;
1026             break;
1027         case 'D':
1028             if (0 == memcmp("-1", optarg, 3))
1029                 op->dev_pdt = -1;
1030             else {
1031                 n = sg_get_num(optarg);
1032                 if ((n < 0) || (n > 31)) {
1033                     pr2serr("bad argument to '--pdt='\n");
1034                     return SG_LIB_SYNTAX_ERROR;
1035                 }
1036                 op->dev_pdt = n;
1037             }
1038             break;
1039         case 'e':
1040             ++op->do_enumerate;
1041             break;
1042         case 'E':
1043             op->exclude_vendor = true;
1044             break;
1045         case 'f':
1046             if ('-' == optarg[0]) {
1047                 n = sg_get_num(optarg + 1);
1048                 if ((n < 0) || (n > 0x30)) {
1049                     pr2serr("bad negated argument to '--filter='\n");
1050                     return SG_LIB_SYNTAX_ERROR;
1051                 }
1052                 op->filter = -n;
1053             } else {
1054                 n = sg_get_num(optarg);
1055                 if ((n < 0) || (n > 0xffff)) {
1056                     pr2serr("bad argument to '--filter='\n");
1057                     usage(1);
1058                     return SG_LIB_SYNTAX_ERROR;
1059                 }
1060                 op->filter = n;
1061             }
1062             op->filter_given = true;
1063             break;
1064         case 'F':
1065             op->do_full = true;
1066             break;
1067         case 'h':
1068         case '?':
1069             ++op->do_help;
1070             break;
1071         case 'H':
1072             ++op->do_hex;
1073             break;
1074         case 'i':
1075             op->in_fn = optarg;
1076             break;
1077         case 'j':
1078            if (! sgj_init_state(&op->json_st, optarg)) {
1079                 int bad_char = op->json_st.first_bad_char;
1080                 char e[1500];
1081 
1082                 if (bad_char) {
1083                     pr2serr("bad argument to --json= option, unrecognized "
1084                             "character '%c'\n\n", bad_char);
1085                 }
1086                 sg_json_usage(0, e, sizeof(e));
1087                 pr2serr("%s", e);
1088                 return SG_LIB_SYNTAX_ERROR;
1089             }
1090             break;
1091         case 'l':
1092             ++op->do_list;
1093             break;
1094         case 'L':
1095             op->do_list += 2;
1096             break;
1097         case 'm':
1098             n = sg_get_num(optarg);
1099             if ((n < 0) || (1 == n)) {
1100                 pr2serr("bad argument to '--maxlen=', from 2 and up "
1101                         "expected\n");
1102                 usage(2);
1103                 return SG_LIB_SYNTAX_ERROR;
1104             } else if (n < 4) {
1105                 pr2serr("Warning: setting '--maxlen' to 4\n");
1106                 n = 4;
1107             }
1108             op->maxlen = n;
1109             op->maxlen_given = true;
1110             break;
1111         case 'M':
1112             if (op->vend_prod) {
1113                 pr2serr("only one '--vendor=' option permitted\n");
1114                 usage(2);
1115                 return SG_LIB_SYNTAX_ERROR;
1116             } else
1117                 op->vend_prod = optarg;
1118             break;
1119         case 'n':
1120             op->do_name = true;
1121             break;
1122         case 'N':
1123             break;      /* ignore */
1124         case 'O':
1125             op->opt_new = false;
1126             return 0;
1127         case 'p':
1128             op->pg_arg = optarg;
1129             break;
1130         case 'P':
1131             n = sg_get_num(optarg);
1132             if (n < 0) {
1133                 pr2serr("bad argument to '--paramp='\n");
1134                 usage(2);
1135                 return SG_LIB_SYNTAX_ERROR;
1136             }
1137             op->paramp = n;
1138             break;
1139         case 'q':
1140             op->do_pcb = true;
1141             break;
1142         case 'Q':       /* N.B. PPC bit obsoleted in SPC-4 rev 18 */
1143             op->do_ppc = true;
1144             break;
1145         case 'r':
1146             op->do_raw = true;
1147             break;
1148         case 'R':
1149             op->do_pcreset = true;
1150             op->do_select = true;
1151             break;
1152         case 's':
1153             op->do_sp = true;
1154             break;
1155         case 'S':
1156             op->do_select = true;
1157             break;
1158         case 't':
1159             op->do_temperature = true;
1160             break;
1161         case 'T':
1162             op->do_transport = true;
1163             break;
1164         case 'u':
1165             ++op->undefined_hex;
1166             break;
1167         case 'v':
1168             op->verbose_given = true;
1169             ++op->verbose;
1170             break;
1171         case 'V':
1172             op->version_given = true;
1173             break;
1174         case 'x':
1175             ++op->no_inq;
1176             break;
1177         case 'X':
1178             op->o_readonly = true;
1179             break;
1180         default:
1181             pr2serr("unrecognised option code %c [0x%x]\n", c, c);
1182             if (op->do_help)
1183                 break;
1184             usage(1);
1185             return SG_LIB_SYNTAX_ERROR;
1186         }
1187     }
1188     if (optind < argc) {
1189         if (NULL == op->device_name) {
1190             op->device_name = argv[optind];
1191             ++optind;
1192         }
1193         if (optind < argc) {
1194             for (; optind < argc; ++optind)
1195                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
1196             usage(1);
1197             return SG_LIB_SYNTAX_ERROR;
1198         }
1199     }
1200     return 0;
1201 }
1202 
1203 /* Processes command line options according to old option format. Returns
1204  * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
1205 static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])1206 old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
1207 {
1208     bool jmp_out;
1209     int k, num, n;
1210     unsigned int u, uu;
1211     const char * cp;
1212 
1213     for (k = 1; k < argc; ++k) {
1214         int plen;
1215 
1216         cp = argv[k];
1217         plen = strlen(cp);
1218         if (plen <= 0)
1219             continue;
1220         if ('-' == *cp) {
1221             for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
1222                 switch (*cp) {
1223                 case 'a':
1224                     ++op->do_all;
1225                     break;
1226                 case 'A':
1227                     op->do_all += 2;
1228                     break;
1229                 case 'b':
1230                     ++op->do_brief;
1231                     break;
1232                 case 'e':
1233                     ++op->do_enumerate;
1234                     break;
1235                 case 'E':
1236                     op->exclude_vendor = true;
1237                     break;
1238                 case 'F':
1239                     op->do_full = true;
1240                     break;
1241                 case 'h':
1242                 case 'H':
1243                     ++op->do_hex;
1244                     break;
1245                 case 'l':
1246                     ++op->do_list;
1247                     break;
1248                 case 'L':
1249                     op->do_list += 2;
1250                     break;
1251                 case 'n':
1252                     op->do_name = true;
1253                     break;
1254                 case 'N':
1255                     op->opt_new = true;
1256                     return 0;
1257                 case 'O':
1258                     break;
1259                 case 'r':
1260                     op->do_pcreset = true;
1261                     op->do_select = true;
1262                     break;
1263                 case 't':
1264                     op->do_temperature = true;
1265                     break;
1266                 case 'T':
1267                     op->do_transport = true;
1268                     break;
1269                 case 'u':
1270                     ++op->undefined_hex;
1271                     break;
1272                 case 'v':
1273                     op->verbose_given = true;
1274                     ++op->verbose;
1275                     break;
1276                 case 'V':
1277                     op->version_given = true;
1278                     break;
1279                 case 'x':
1280                     ++op->no_inq;
1281                     break;
1282                 case 'X':
1283                     op->o_readonly = true;
1284                     break;
1285                 case '?':
1286                     ++op->do_help;
1287                     break;
1288                 case '-':
1289                     ++cp;
1290                     jmp_out = true;
1291                     break;
1292                 default:
1293                     jmp_out = true;
1294                     break;
1295                 }
1296                 if (jmp_out)
1297                     break;
1298             }
1299             if (plen <= 0)
1300                 continue;
1301             if (0 == strncmp("c=", cp, 2)) {
1302                 num = sscanf(cp + 2, "%6x", &u);
1303                 if ((1 != num) || (u > 3)) {
1304                     pr2serr("Bad page control after '-c=' option [0..3]\n");
1305                     usage_old();
1306                     return SG_LIB_SYNTAX_ERROR;
1307                 }
1308                 op->page_control = u;
1309             } else if (0 == strncmp("D=", cp, 2)) {
1310                 n = sg_get_num(cp + 2);
1311                 if ((n < 0) || (n > 31)) {
1312                     pr2serr("Bad argument after '-D=' option\n");
1313                     usage_old();
1314                     return SG_LIB_SYNTAX_ERROR;
1315                 }
1316                 op->dev_pdt = n;
1317             } else if (0 == strncmp("f=", cp, 2)) {
1318                 n = sg_get_num(cp + 2);
1319                 if ((n < 0) || (n > 0xffff)) {
1320                     pr2serr("Bad argument after '-f=' option\n");
1321                     usage_old();
1322                     return SG_LIB_SYNTAX_ERROR;
1323                 }
1324                 op->filter = n;
1325                 op->filter_given = true;
1326             } else if (0 == strncmp("i=", cp, 2))
1327                 op->in_fn = cp + 2;
1328             else if (0 == strncmp("m=", cp, 2)) {
1329                 num = sscanf(cp + 2, "%8d", &n);
1330                 if ((1 != num) || (n < 0)) {
1331                     pr2serr("Bad maximum response length after '-m=' "
1332                             "option\n");
1333                     usage_old();
1334                     return SG_LIB_SYNTAX_ERROR;
1335                 }
1336                 op->maxlen_given = true;
1337                 op->maxlen = n;
1338             } else if (0 == strncmp("M=", cp, 2)) {
1339                 if (op->vend_prod) {
1340                     pr2serr("only one '-M=' option permitted\n");
1341                     usage(2);
1342                     return SG_LIB_SYNTAX_ERROR;
1343                 } else
1344                     op->vend_prod = cp + 2;
1345             } else if (0 == strncmp("p=", cp, 2)) {
1346                 const char * ccp = cp + 2;
1347                 const struct log_elem * lep;
1348 
1349                 if (isalpha((uint8_t)ccp[0])) {
1350                     char * xp;
1351                     char b[80];
1352 
1353                     if (strlen(ccp) >= (sizeof(b) - 1)) {
1354                         pr2serr("argument to '-p=' is too long\n");
1355                         return SG_LIB_SYNTAX_ERROR;
1356                     }
1357                     strcpy(b, ccp);
1358                     xp = (char *)strchr(b, ',');
1359                     if (xp)
1360                         *xp = '\0';
1361                     lep = acron_search(b);
1362                     if (NULL == lep) {
1363                         pr2serr("bad argument to '--page=' no acronyn match "
1364                                 "to '%s'\n", b);
1365                         pr2serr("  Try using '-e' or'-ee' to see available "
1366                                 "acronyns\n");
1367                         return SG_LIB_SYNTAX_ERROR;
1368                     }
1369                     op->lep = lep;
1370                     op->pg_code = lep->pg_code;
1371                     if (xp) {
1372                         n = sg_get_num_nomult(xp + 1);
1373                         if ((n < 0) || (n > 255)) {
1374                             pr2serr("Bad second value in argument to "
1375                                     "'--page='\n");
1376                             return SG_LIB_SYNTAX_ERROR;
1377                         }
1378                         op->subpg_code = n;
1379                     } else
1380                         op->subpg_code = lep->subpg_code;
1381                 } else {
1382                     /* numeric arg: either 'pg_num' or 'pg_num,subpg_num' */
1383                     if (NULL == strchr(cp + 2, ',')) {
1384                         num = sscanf(cp + 2, "%6x", &u);
1385                         if ((1 != num) || (u > 63)) {
1386                             pr2serr("Bad page code value after '-p=' "
1387                                     "option\n");
1388                             usage_old();
1389                             return SG_LIB_SYNTAX_ERROR;
1390                         }
1391                         op->pg_code = u;
1392                     } else if (2 == sscanf(cp + 2, "%4x,%4x", &u, &uu)) {
1393                         if (uu > 255) {
1394                             pr2serr("Bad sub page code value after '-p=' "
1395                                     "option\n");
1396                             usage_old();
1397                             return SG_LIB_SYNTAX_ERROR;
1398                         }
1399                         op->pg_code = u;
1400                         op->subpg_code = uu;
1401                     } else {
1402                         pr2serr("Bad page code, subpage code sequence after "
1403                                 "'-p=' option\n");
1404                         usage_old();
1405                         return SG_LIB_SYNTAX_ERROR;
1406                     }
1407                 }
1408             } else if (0 == strncmp("paramp=", cp, 7)) {
1409                 num = sscanf(cp + 7, "%8x", &u);
1410                 if ((1 != num) || (u > 0xffff)) {
1411                     pr2serr("Bad parameter pointer after '-paramp=' "
1412                             "option\n");
1413                     usage_old();
1414                     return SG_LIB_SYNTAX_ERROR;
1415                 }
1416                 op->paramp = u;
1417             } else if (0 == strncmp("pcb", cp, 3))
1418                 op->do_pcb = true;
1419             else if (0 == strncmp("ppc", cp, 3))
1420                 op->do_ppc = true;
1421             else if (0 == strncmp("select", cp, 6))
1422                 op->do_select = true;
1423             else if (0 == strncmp("sp", cp, 2))
1424                 op->do_sp = true;
1425             else if (0 == strncmp("old", cp, 3))
1426                 ;
1427             else if (jmp_out) {
1428                 pr2serr("Unrecognized option: %s\n", cp);
1429                 usage_old();
1430                 return SG_LIB_SYNTAX_ERROR;
1431             }
1432         } else if (0 == op->device_name)
1433             op->device_name = cp;
1434         else {
1435             pr2serr("too many arguments, got: %s, not expecting: %s\n",
1436                     op->device_name, cp);
1437             usage_old();
1438             return SG_LIB_SYNTAX_ERROR;
1439         }
1440     }
1441     return 0;
1442 }
1443 
1444 /* Process command line options. First check using new option format unless
1445  * the SG3_UTILS_OLD_OPTS environment variable is defined which causes the
1446  * old option format to be checked first. Both new and old format can be
1447  * countermanded by a '-O' and '-N' options respectively. As soon as either
1448  * of these options is detected (when processing the other format), processing
1449  * stops and is restarted using the other format. Clear? */
1450 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])1451 parse_cmd_line(struct opts_t * op, int argc, char * argv[])
1452 {
1453     int res;
1454     char * cp;
1455 
1456     cp = getenv("SG3_UTILS_OLD_OPTS");
1457     if (cp) {
1458         op->opt_new = false;
1459         res = old_parse_cmd_line(op, argc, argv);
1460         if ((0 == res) && op->opt_new)
1461             res = new_parse_cmd_line(op, argc, argv);
1462     } else {
1463         op->opt_new = true;
1464         res = new_parse_cmd_line(op, argc, argv);
1465         if ((0 == res) && (0 == op->opt_new))
1466             res = old_parse_cmd_line(op, argc, argv);
1467     }
1468     return res;
1469 }
1470 
1471 static void
dStrRaw(const uint8_t * str,int len)1472 dStrRaw(const uint8_t * str, int len)
1473 {
1474     int k;
1475 
1476     for (k = 0; k < len; ++k)
1477         printf("%c", str[k]);
1478 }
1479 
1480 /* Returns 'xp' with "unknown" if all bits set; otherwise decoded (big endian)
1481  * number in 'xp'. Number rendered in decimal if pr_in_hex=false otherwise in
1482  * hex with leading '0x' prepended. */
1483 static char *
num_or_unknown(const uint8_t * xp,int num_bytes,bool pr_in_hex,char * b,int blen)1484 num_or_unknown(const uint8_t * xp, int num_bytes /* max is 8 */,
1485                bool pr_in_hex, char * b, int blen)
1486 {
1487     if (sg_all_ffs(xp, num_bytes))
1488         snprintf(b, blen, "%s", unknown_s);
1489     else {
1490         uint64_t num = sg_get_unaligned_be(num_bytes, xp);
1491 
1492         if (pr_in_hex)
1493             snprintf(b, blen, "0x%" PRIx64, num);
1494         else
1495             snprintf(b, blen, "%" PRIu64, num);
1496     }
1497     return b;
1498 }
1499 
1500 /* Call LOG SENSE twice: the first time ask for 4 byte response to determine
1501    actual length of response; then a second time requesting the
1502    min(actual_len, mx_resp_len) bytes. If the calculated length for the
1503    second fetch is odd then it is incremented (perhaps should be made modulo
1504    4 in the future for SAS). Returns 0 if ok, SG_LIB_CAT_INVALID_OP for
1505    log_sense not supported, SG_LIB_CAT_ILLEGAL_REQ for bad field in log sense
1506    command, SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION,
1507    SG_LIB_CAT_ABORTED_COMMAND and -1 for other errors. */
1508 static int
do_logs(int sg_fd,uint8_t * resp,int mx_resp_len,const struct opts_t * op)1509 do_logs(int sg_fd, uint8_t * resp, int mx_resp_len,
1510         const struct opts_t * op)
1511 {
1512     int calc_len, request_len, res, resid, vb;
1513 
1514 #ifdef SG_LIB_WIN32
1515 #ifdef SG_LIB_WIN32_DIRECT
1516     if (! win32_spt_init_state) {
1517         if (win32_spt_curr_state) {
1518             if (mx_resp_len < 16384) {
1519                 scsi_pt_win32_direct(0);
1520                 win32_spt_curr_state = false;
1521             }
1522         } else {
1523             if (mx_resp_len >= 16384) {
1524                 scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT direct */);
1525                 win32_spt_curr_state = true;
1526             }
1527         }
1528     }
1529 #endif
1530 #endif
1531     memset(resp, 0, mx_resp_len);
1532     vb = op->verbose;
1533     if (op->maxlen > 1)
1534         request_len = mx_resp_len;
1535     else {
1536         request_len = LOG_SENSE_PROBE_ALLOC_LEN;
1537         if ((res = sg_ll_log_sense_v2(sg_fd, op->do_ppc, op->do_sp,
1538                                       op->page_control, op->pg_code,
1539                                       op->subpg_code, op->paramp,
1540                                       resp, request_len, LOG_SENSE_DEF_TIMEOUT,
1541                                       &resid, true /* noisy */, vb)))
1542             return res;
1543         if (resid > 0) {
1544             res = SG_LIB_WILD_RESID;
1545             goto resid_err;
1546         }
1547         calc_len = sg_get_unaligned_be16(resp + 2) + 4;
1548         if ((! op->do_raw) && (vb > 1)) {
1549             pr2serr("  Log sense (find length) response:\n");
1550             hex2stderr(resp, LOG_SENSE_PROBE_ALLOC_LEN, 1);
1551             pr2serr("  hence calculated response length=%d\n", calc_len);
1552         }
1553         if (op->pg_code != (0x3f & resp[0])) {
1554             if (vb)
1555                 pr2serr("Page code does not appear in first byte of "
1556                         "response so it's suspect\n");
1557             if (calc_len > 0x40) {
1558                 calc_len = 0x40;
1559                 if (vb)
1560                     pr2serr("Trim response length to 64 bytes due to "
1561                             "suspect response format\n");
1562             }
1563         }
1564         /* Some HBAs don't like odd transfer lengths */
1565         if (calc_len % 2)
1566             calc_len += 1;
1567         if (calc_len > mx_resp_len)
1568             calc_len = mx_resp_len;
1569         request_len = calc_len;
1570     }
1571     if ((res = sg_ll_log_sense_v2(sg_fd, op->do_ppc, op->do_sp,
1572                                   op->page_control, op->pg_code,
1573                                   op->subpg_code, op->paramp,
1574                                   resp, request_len,
1575                                   LOG_SENSE_DEF_TIMEOUT, &resid,
1576                                   true /* noisy */, vb)))
1577         return res;
1578     if (resid > 0) {
1579         request_len -= resid;
1580         if (request_len < 4) {
1581             request_len += resid;
1582             res = SG_LIB_WILD_RESID;
1583             goto resid_err;
1584         }
1585     }
1586     if ((! op->do_raw) && (vb > 1)) {
1587         pr2serr("  Log sense response:\n");
1588         hex2stderr(resp, request_len, 1);
1589     }
1590     return 0;
1591 resid_err:
1592     pr2serr("%s: request_len=%d, resid=%d, problems\n", __func__, request_len,
1593             resid);
1594     request_len -= resid;
1595     if ((request_len > 0) && (! op->do_raw) && (vb > 1)) {
1596         pr2serr("  Log sense (resid_err) response:\n");
1597         hex2stderr(resp, request_len, 1);
1598     }
1599     return res;
1600 }
1601 
1602 sgj_opaque_p
sg_log_js_hdr(sgj_state * jsp,sgj_opaque_p jop,const char * name,const uint8_t * log_hdrp)1603 sg_log_js_hdr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
1604               const uint8_t * log_hdrp)
1605 {
1606     bool ds = !! (log_hdrp[0] & 0x80);
1607     bool spf = !! (log_hdrp[0] & 0x40);
1608     int pg = log_hdrp[0] & 0x3f;
1609     int subpg = log_hdrp[1];
1610     size_t nlen = strlen(name);
1611     sgj_opaque_p jo2p;
1612     char b[80];
1613 
1614     if ((nlen < 4) || (0 != strcmp("age", name + nlen - 3))) {
1615         memcpy(b, name, nlen);
1616         memcpy(b + nlen, " log page", 10);
1617         jo2p = sgj_snake_named_subobject_r(jsp, jop, b);
1618     } else
1619         jo2p = sgj_snake_named_subobject_r(jsp, jop, name);
1620 
1621     sgj_js_nv_ihex_nex(jsp, jo2p, "ds", (int)ds, false, "Did not Save");
1622     sgj_js_nv_ihex_nex(jsp, jo2p, "spf", (int)spf, NULL, "SubPage Format");
1623     sgj_js_nv_ihex(jsp, jo2p, "page_code", pg);
1624     sgj_js_nv_ihex(jsp, jo2p, "subpage_code", subpg);
1625     return jo2p;
1626 }
1627 
1628 
1629 
1630 /* DS made obsolete in spc4r03; TMC and ETC made obsolete in spc5r03. */
1631 static char *
get_pcb_str(int pcb,char * outp,int maxoutlen)1632 get_pcb_str(int pcb, char * outp, int maxoutlen)
1633 {
1634     char buff[PCB_STR_LEN];
1635     int n;
1636 
1637     n = sprintf(buff, "du=%d [ds=%d] tsd=%d [etc=%d] ", ((pcb & 0x80) ? 1 : 0),
1638                 ((pcb & 0x40) ? 1 : 0), ((pcb & 0x20) ? 1 : 0),
1639                 ((pcb & 0x10) ? 1 : 0));
1640     if (pcb & 0x10)
1641         n += sprintf(buff + n, "[tmc=%d] ", ((pcb & 0xc) >> 2));
1642 #if 1
1643     n += sprintf(buff + n, "format+linking=%d  [0x%.2x]", pcb & 3,
1644                  pcb);
1645 #else
1646     if (pcb & 0x1)
1647         n += sprintf(buff + n, "lbin=%d ", ((pcb & 0x2) >> 1));
1648     n += sprintf(buff + n, "lp=%d  [0x%.2x]", pcb & 0x1, pcb);
1649 #endif
1650     if (outp && (n < maxoutlen)) {
1651         memcpy(outp, buff, n);
1652         outp[n] = '\0';
1653     } else if (outp && (maxoutlen > 0))
1654         outp[0] = '\0';
1655     return outp;
1656 }
1657 
1658 static void
js_pcb(sgj_state * jsp,sgj_opaque_p jop,int pcb)1659 js_pcb(sgj_state * jsp, sgj_opaque_p jop, int pcb)
1660 {
1661     sgj_opaque_p jo2p = sgj_snake_named_subobject_r(jsp, jop,
1662                                                     "parameter_control_byte");
1663 
1664     sgj_js_nv_ihex_nex(jsp, jo2p, "du", (pcb & 0x80) ? 1 : 0, false,
1665                        "Disable Update");
1666     sgj_js_nv_ihex_nex(jsp, jo2p, "ds", (pcb & 0x40) ? 1 : 0, false,
1667                        "Disable Save [obsolete]");
1668     sgj_js_nv_ihex_nex(jsp, jo2p, "tsd", (pcb & 0x20) ? 1 : 0, false,
1669                        "Target Save Disable");
1670     sgj_js_nv_ihex_nex(jsp, jo2p, "etc", (pcb & 0x10) ? 1 : 0, false,
1671                        "Enable Threshold Comparison [obsolete]");
1672     sgj_js_nv_ihex_nex(jsp, jo2p, "tmc", (pcb & 0xc) >> 2, false,
1673                        "Threshold Met Criteria [obsolete]");
1674     sgj_js_nv_ihex_nex(jsp, jo2p, "format_and_linking", pcb & 0x3, false,
1675                        NULL);
1676 }
1677 
1678 /* SUPP_PAGES_LPAGE [0x0,0x0] <sp> */
1679 static bool
show_supported_pgs_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)1680 show_supported_pgs_page(const uint8_t * resp, int len,
1681                         struct opts_t * op, sgj_opaque_p jop)
1682 {
1683     int num, k;
1684     const uint8_t * bp;
1685     sgj_state * jsp = &op->json_st;
1686     sgj_opaque_p jo2p, jo3p;
1687     sgj_opaque_p jap = NULL;
1688     char b[64];
1689     static const char * slpgs = "Supported log pages";
1690 
1691     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
1692         sgj_pr_hr(jsp, "%s  [0x0]:\n", slpgs);  /* introduced in: SPC-2 */
1693     num = len - 4;
1694     bp = &resp[0] + 4;
1695     if ((op->do_hex > 0) || op->do_raw) {
1696         if (op->do_raw)
1697             dStrRaw(resp, len);
1698         else
1699             hex2stdout(resp, len, op->dstrhex_no_ascii);
1700         return true;
1701     }
1702     if (jsp->pr_as_json) {
1703         jo2p = sg_log_js_hdr(jsp, jop, slpgs, resp);
1704         jap = sgj_named_subarray_r(jsp, jo2p, "supported_pages_list");
1705     }
1706 
1707     for (k = 0; k < num; ++k) {
1708         int pg_code = bp[k] & 0x3f;
1709         const struct log_elem * lep;
1710 
1711         snprintf(b, sizeof(b) - 1, "  0x%02x        ", pg_code);
1712         lep = pg_subpg_pdt_search(pg_code, 0, op->dev_pdt, -1);
1713         if (lep) {
1714             if (op->do_brief > 1)
1715                 sgj_pr_hr(jsp, "    %s\n", lep->name);
1716             else if (op->do_brief)
1717                 sgj_pr_hr(jsp, "%s%s\n", b, lep->name);
1718             else
1719                 sgj_pr_hr(jsp, "%s%s [%s]\n", b, lep->name, lep->acron);
1720         } else
1721             sgj_pr_hr(jsp, "%s\n", b);
1722         if (jsp->pr_as_json) {
1723             jo3p = sgj_new_unattached_object_r(jsp);
1724             sgj_js_nv_ihex(jsp, jo3p, "page_code", pg_code);
1725             sgj_js_nv_s(jsp, jo3p, "name", lep ? lep->name : unknown_s);
1726             sgj_js_nv_s(jsp, jo3p, "acronym", lep ? lep->acron : unknown_s);
1727             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
1728         }
1729     }
1730     return true;
1731 }
1732 
1733 /* SUPP_PAGES_LPAGE,SUPP_SPGS_SUBPG [0x0,0xff] <ssp> or all subpages of a
1734  * given page code: [<pg_code>,0xff] where <pg_code> > 0 */
1735 static bool
show_supported_pgs_sub_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)1736 show_supported_pgs_sub_page(const uint8_t * resp, int len,
1737                             struct opts_t * op, sgj_opaque_p jop)
1738 {
1739     int num, k;
1740     const uint8_t * bp;
1741     const struct log_elem * lep = NULL;
1742     sgj_state * jsp = &op->json_st;
1743     sgj_opaque_p jo2p, jo3p;
1744     sgj_opaque_p jap = NULL;
1745     char b[64];
1746     static const char * slpass = "Supported log pages and subpages";
1747     static const char * sss = "Supported subpages";
1748 
1749     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
1750         if (op->pg_code > 0)
1751             sgj_pr_hr(jsp, "%s  [0x%x, 0xff]:\n", sss, op->pg_code);
1752         else
1753             sgj_pr_hr(jsp, "%s  [0x0, 0xff]:\n", sss);
1754     }
1755     num = len - 4;
1756     bp = &resp[0] + 4;
1757     if ((op->do_hex > 0) || op->do_raw) {
1758         if (op->do_raw)
1759             dStrRaw(resp, len);
1760         else
1761             hex2stdout(resp, len, op->dstrhex_no_ascii);
1762         return true;
1763     }
1764     if (jsp->pr_as_json) {
1765         if (op->pg_code > 0) {
1766             jo2p = sg_log_js_hdr(jsp, jop, sss, resp);
1767             jap = sgj_named_subarray_r(jsp, jo2p,
1768                                        "supported_subpage_descriptors");
1769         } else {
1770             jo2p = sg_log_js_hdr(jsp, jop, slpass, resp);
1771             jap = sgj_named_subarray_r(jsp, jo2p,
1772                                "supported_page_subpage_descriptors");
1773         }
1774     }
1775 
1776     for (k = 0; k < num; k += 2) {
1777         bool pr_name = true;
1778         int pg_code = bp[k];
1779         int subpg_code = bp[k + 1];
1780 
1781         /* formerly ignored [pg, 0xff] when pg > 0, don't know why */
1782         if (NOT_SPG_SUBPG == subpg_code)
1783             snprintf(b, sizeof(b) - 1, "  0x%02x        ", pg_code);
1784         else
1785             snprintf(b, sizeof(b) - 1, "  0x%02x,0x%02x   ", pg_code,
1786                      subpg_code);
1787         if ((pg_code > 0) && (subpg_code == 0xff)) {
1788             sgj_pr_hr(jsp, "%s\n", b);
1789             pr_name = false;
1790         } else {
1791             lep = pg_subpg_pdt_search(pg_code, subpg_code, op->dev_pdt, -1);
1792             if (lep) {
1793                 if (op->do_brief > 1)
1794                     sgj_pr_hr(jsp, "    %s\n", lep->name);
1795                 else if (op->do_brief)
1796                     sgj_pr_hr(jsp, "%s%s\n", b, lep->name);
1797                 else
1798                     sgj_pr_hr(jsp, "%s%s [%s]\n", b, lep->name, lep->acron);
1799             } else
1800                 sgj_pr_hr(jsp, "%s\n", b);
1801         }
1802         if (jsp->pr_as_json) {
1803             jo3p = sgj_new_unattached_object_r(jsp);
1804             sgj_js_nv_ihex(jsp, jo3p, "page_code", pg_code);
1805             sgj_js_nv_ihex(jsp, jo3p, "subpage_code", subpg_code);
1806             if (pr_name) {
1807                 sgj_js_nv_s(jsp, jo3p, "name", lep ? lep->name : unknown_s);
1808                 sgj_js_nv_s(jsp, jo3p, "acronym", lep ? lep->acron :
1809                                                         unknown_s);
1810             }
1811             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
1812         }
1813     }
1814     return true;
1815 }
1816 
1817 /* BUFF_OVER_UNDER_LPAGE [0x1] <bou>  introduced: SPC-2 */
1818 static bool
show_buffer_over_under_run_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)1819 show_buffer_over_under_run_page(const uint8_t * resp, int len,
1820                                 struct opts_t * op, sgj_opaque_p jop)
1821 {
1822     int num, pl, pc;
1823     uint64_t count;
1824     const uint8_t * bp;
1825     const char * cp;
1826     sgj_state * jsp = &op->json_st;
1827     sgj_opaque_p jo2p;
1828     sgj_opaque_p jo3p = NULL;
1829     sgj_opaque_p jap = NULL;
1830     char str[PCB_STR_LEN];
1831     static const char * bourlp = "Buffer over-run/under-run log page";
1832     static const char * orurc = "over_run_under_run_counter";
1833 
1834     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
1835         sgj_pr_hr(jsp, "%s  [0x1]\n", bourlp);
1836     num = len - 4;
1837     bp = &resp[0] + 4;
1838     if (jsp->pr_as_json) {
1839         jo2p = sg_log_js_hdr(jsp, jop, bourlp, resp);
1840         jap = sgj_named_subarray_r(jsp, jo2p,
1841                         "buffer_over_run_under_run_log_parameters");
1842     }
1843     while (num > 3) {
1844         cp = NULL;
1845         pl = bp[3] + 4;
1846         count = (pl > 4) ? sg_get_unaligned_be(pl - 4, bp + 4) : 0;
1847         pc = sg_get_unaligned_be16(bp + 0);
1848         if (op->filter_given) {
1849             if (pc != op->filter)
1850                 goto skip;
1851         }
1852         if (op->do_raw) {
1853             dStrRaw(bp, pl);
1854             break;
1855         } else if (op->do_hex) {
1856             hex2stdout(bp, pl, op->dstrhex_no_ascii);
1857             break;
1858         }
1859         if (jsp->pr_as_json) {
1860             jo3p = sgj_new_unattached_object_r(jsp);
1861             if (op->do_pcb)
1862                 js_pcb(jsp, jo3p, bp[2]);
1863         }
1864 
1865         switch (pc) {
1866         case 0x0:
1867             cp = "under-run";
1868             break;
1869         case 0x1:
1870             cp = "over-run";
1871             break;
1872         case 0x2:
1873             cp = "service delivery subsystem busy, under-run";
1874             break;
1875         case 0x3:
1876             cp = "service delivery subsystem busy, over-run";
1877             break;
1878         case 0x4:
1879             cp = "transfer too slow, under-run";
1880             break;
1881         case 0x5:
1882             cp = "transfer too slow, over-run";
1883             break;
1884         case 0x20:
1885             cp = "command, under-run";
1886             break;
1887         case 0x21:
1888             cp = "command, over-run";
1889             break;
1890         case 0x22:
1891             cp = "command, service delivery subsystem busy, under-run";
1892             break;
1893         case 0x23:
1894             cp = "command, service delivery subsystem busy, over-run";
1895             break;
1896         case 0x24:
1897             cp = "command, transfer too slow, under-run";
1898             break;
1899         case 0x25:
1900             cp = "command, transfer too slow, over-run";
1901             break;
1902         case 0x40:
1903             cp = "I_T nexus, under-run";
1904             break;
1905         case 0x41:
1906             cp = "I_T nexus, over-run";
1907             break;
1908         case 0x42:
1909             cp = "I_T nexus, service delivery subsystem busy, under-run";
1910             break;
1911         case 0x43:
1912             cp = "I_T nexus, service delivery subsystem busy, over-run";
1913             break;
1914         case 0x44:
1915             cp = "I_T nexus, transfer too slow, under-run";
1916             break;
1917         case 0x45:
1918             cp = "I_T nexus, transfer too slow, over-run";
1919             break;
1920         case 0x80:
1921             cp = "time, under-run";
1922             break;
1923         case 0x81:
1924             cp = "time, over-run";
1925             break;
1926         case 0x82:
1927             cp = "time, service delivery subsystem busy, under-run";
1928             break;
1929         case 0x83:
1930             cp = "time, service delivery subsystem busy, over-run";
1931             break;
1932         case 0x84:
1933             cp = "time, transfer too slow, under-run";
1934             break;
1935         case 0x85:
1936             cp = "time, transfer too slow, over-run";
1937             break;
1938         default:
1939             pr2serr("  undefined %s [0x%x], count = %" PRIu64 "\n",
1940                     param_c, pc, count);
1941             break;
1942         }
1943             sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc);
1944         sgj_pr_hr(jsp, "  %s=0x%x\n", param_c, pc);
1945         if (cp) {
1946             sgj_pr_hr(jsp, "    %s = %" PRIu64 "\n", cp, count);
1947             js_snakenv_ihexstr_nex(jsp, jo3p, param_c, pc, true,
1948                                    NULL, cp, NULL);
1949             sgj_js_nv_ihex(jsp, jo3p, orurc, count);
1950         } else
1951             sgj_pr_hr(jsp, "    counter = %" PRIu64 "\n", count);
1952 
1953         if (op->do_pcb)
1954             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
1955                       str, sizeof(str)));
1956         if (jsp->pr_as_json)
1957             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
1958         if (op->filter_given)
1959             break;
1960 skip:
1961         num -= pl;
1962         bp += pl;
1963     }
1964     return true;
1965 }
1966 
1967 /* WRITE_ERR_LPAGE; READ_ERR_LPAGE; READ_REV_ERR_LPAGE; VERIFY_ERR_LPAGE */
1968 /* [0x2, 0x3, 0x4, 0x5] <we, re, rre, ve>  introduced: SPC-3 */
1969 static bool
show_error_counter_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)1970 show_error_counter_page(const uint8_t * resp, int len,
1971                         struct opts_t * op, sgj_opaque_p jop)
1972 {
1973     bool skip_out = false;
1974     bool evsm_output = false;
1975     int n, num, pl, pc, pg_code;
1976     uint64_t val;
1977     const uint8_t * bp;
1978     const char * pg_cp = NULL;
1979     const char * par_cp = NULL;
1980     sgj_state * jsp = &op->json_st;
1981     sgj_opaque_p jo2p;
1982     sgj_opaque_p jo3p = NULL;
1983     sgj_opaque_p jap = NULL;
1984     char str[PCB_STR_LEN];
1985     char b[128] SG_C_CPP_ZERO_INIT;
1986     char d[128];
1987     char e[64];
1988     static const char * wec = "Write error counter";
1989     static const char * rec = "Read error counter";
1990     static const char * rrec = "Read reverse error counter";
1991     static const char * vec = "Verify error counter";
1992 
1993     pg_code = resp[0] & 0x3f;
1994     switch(pg_code) {
1995     case WRITE_ERR_LPAGE:
1996         pg_cp = wec;
1997         break;
1998     case READ_ERR_LPAGE:
1999         pg_cp = rec;
2000         break;
2001     case READ_REV_ERR_LPAGE:
2002         pg_cp = rrec;
2003         break;
2004     case VERIFY_ERR_LPAGE:
2005         pg_cp = vec;
2006         break;
2007     default:
2008         pr2serr("expecting error counter page, got page = 0x%x\n",
2009                 pg_code);
2010         return false;
2011     }
2012     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
2013         sgj_pr_hr(jsp, "%s log page  [0x%x]\n", pg_cp, pg_code);
2014     if (jsp->pr_as_json) {
2015         n = strlen(pg_cp);
2016         memcpy(b, pg_cp, n);
2017         memcpy(b + n, " log", 4);
2018         n = strlen(b);
2019         memcpy(b + n, " page", 5);
2020         jo2p = sg_log_js_hdr(jsp, jop, b, resp);
2021         memcpy(b + n, " parameters", 11);
2022         sgj_convert_to_snake_name(b, d, sizeof(d) - 1);
2023         jap = sgj_named_subarray_r(jsp, jo2p, d);
2024     }
2025     num = len - 4;
2026     bp = &resp[0] + 4;
2027     while (num > 3) {
2028         pc = sg_get_unaligned_be16(bp + 0);
2029         pl = bp[3] + 4;
2030         if (op->filter_given) {
2031             if (pc != op->filter)
2032                 goto skip;
2033         }
2034         if (op->do_raw) {
2035             dStrRaw(bp, pl);
2036             break;
2037         } else if (op->do_hex) {
2038             hex2stdout(bp, pl, op->dstrhex_no_ascii);
2039             break;
2040         }
2041         if (jsp->pr_as_json) {
2042             jo3p = sgj_new_unattached_object_r(jsp);
2043             if (op->do_pcb)
2044                 js_pcb(jsp, jo3p, bp[2]);
2045         }
2046 
2047         par_cp = NULL;
2048         switch (pc) {
2049         case 0:
2050             par_cp = "Errors corrected without substantial delay";
2051             break;
2052         case 1:
2053             par_cp = "Errors corrected with possible delays";
2054             break;
2055         case 2:
2056             par_cp = "Total rewrites or rereads";
2057             break;
2058         case 3:
2059             par_cp = "Total errors corrected";
2060             break;
2061         case 4:
2062             par_cp = "Total times correction algorithm processed";
2063             break;
2064         case 5:
2065             par_cp = "Total bytes processed";
2066             break;
2067         case 6:
2068             par_cp = "Total uncorrected errors";
2069             break;
2070         default:
2071             if (op->exclude_vendor) {
2072                 skip_out = true;
2073                 if ((op->verbose > 0) && (0 == op->do_brief) &&
2074                     (! evsm_output)) {
2075                     evsm_output = true;
2076                     pr2serr("  %s parameter(s) being ignored\n", vend_spec);
2077                 }
2078             } else {
2079                 if (0x8009 == pc)
2080                     par_cp = "Track following errors [Hitachi]";
2081                 else if (0x8015 == pc)
2082                     par_cp = "Positioning errors [Hitachi]";
2083                 else {
2084                     snprintf(e, sizeof(e), "Reserved or %s [0x%x]", vend_spec,
2085                              pc);
2086                     par_cp = e;
2087                 }
2088             }
2089             break;
2090         }
2091 
2092         if (skip_out)
2093             skip_out = false;
2094         else if (par_cp) {
2095             val = sg_get_unaligned_be(pl - 4, bp + 4);
2096             if (val > ((uint64_t)1 << 40))
2097                 snprintf(d, sizeof(d), "%" PRIu64 " [%" PRIu64 " TB]",
2098                          val, (val / (1000UL * 1000 * 1000 * 1000)));
2099             else if (val > ((uint64_t)1 << 30))
2100                 snprintf(d, sizeof(d), "%" PRIu64 " [%" PRIu64 " GB]",
2101                          val, (val / (1000UL * 1000 * 1000)));
2102             else
2103                 snprintf(d, sizeof(d), "%" PRIu64, val);
2104             sgj_pr_hr(jsp, "  %s = %s\n", par_cp, d);
2105             if (jsp->pr_as_json) {
2106                 js_snakenv_ihexstr_nex(jsp, jo3p, param_c, pc, true,
2107                                        NULL, par_cp, NULL);
2108                 sgj_convert_to_snake_name(pg_cp, e, sizeof(e) - 1);
2109                 n = strlen(e);
2110                 memcpy(e + n, "_counter", 9); /* take trailing null */
2111                 sgj_js_nv_ihexstr(jsp, jo3p, e, val, as_s_s, d);
2112             }
2113         }
2114         if (op->do_pcb)
2115             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
2116                       str, sizeof(str)));
2117         if (jsp->pr_as_json)
2118             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
2119         if (op->filter_given)
2120             break;
2121 skip:
2122         num -= pl;
2123         bp += pl;
2124     }
2125     return true;
2126 }
2127 
2128 /* NON_MEDIUM_LPAGE [0x6] <nm>  introduced: SPC-2 */
2129 static bool
show_non_medium_error_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)2130 show_non_medium_error_page(const uint8_t * resp, int len,
2131                            struct opts_t * op, sgj_opaque_p jop)
2132 {
2133     bool skip_out = false;
2134     bool evsm_output = false;
2135     int num, pl, pc;
2136     uint64_t count;
2137     const uint8_t * bp;
2138     sgj_state * jsp = &op->json_st;
2139     sgj_opaque_p jo2p;
2140     sgj_opaque_p jo3p = NULL;
2141     sgj_opaque_p jap = NULL;
2142     char str[PCB_STR_LEN];
2143     char b[128] SG_C_CPP_ZERO_INIT;
2144     static const char * nmelp = "Non-medium error log page";
2145     static const char * nmec = "Non-medium error count";
2146 
2147     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
2148         sgj_pr_hr(jsp, "%s  [0x6]\n", nmelp);
2149     num = len - 4;
2150     bp = &resp[0] + 4;
2151     if (jsp->pr_as_json) {
2152         jo2p = sg_log_js_hdr(jsp, jop, nmelp, resp);
2153         jap = sgj_named_subarray_r(jsp, jo2p,
2154                         "non_medium_error_log_parameters");
2155     }
2156     while (num > 3) {
2157         pc = sg_get_unaligned_be16(bp + 0);
2158         pl = bp[3] + 4;
2159         if (op->filter_given) {
2160             if (pc != op->filter)
2161                 goto skip;
2162         }
2163         if (op->do_raw) {
2164             dStrRaw(bp, pl);
2165             break;
2166         } else if (op->do_hex) {
2167             hex2stdout(bp, pl, op->dstrhex_no_ascii);
2168             break;
2169         }
2170         if (jsp->pr_as_json) {
2171             jo3p = sgj_new_unattached_object_r(jsp);
2172             if (op->do_pcb)
2173                 js_pcb(jsp, jo3p, bp[2]);
2174         }
2175 
2176         switch (pc) {
2177         case 0:
2178             snprintf(b, sizeof(b), "%s", nmec);
2179             break;
2180         default:
2181             if (pc <= 0x7fff)
2182                 snprintf(b, sizeof(b), "  Reserved [0x%x]", pc);
2183             else {
2184                 if (op->exclude_vendor) {
2185                     skip_out = true;
2186                     if ((op->verbose > 0) && (0 == op->do_brief) &&
2187                         (! evsm_output)) {
2188                         evsm_output = true;
2189                         pr2serr("  %s parameter(s) being ignored\n",
2190                                 vend_spec);
2191                     }
2192                 } else
2193                     snprintf(b, sizeof(b), "%s [0x%x]", vend_spec, pc);
2194             }
2195             break;
2196         }
2197         if (skip_out)
2198             skip_out = false;
2199         else {
2200             count = sg_get_unaligned_be(pl - 4, bp + 4);
2201             sgj_pr_hr(jsp, "  %s = %" PRIu64 "\n", b, count);
2202             js_snakenv_ihexstr_nex(jsp, jo3p, param_c, pc, true,
2203                                    NULL, b, NULL);
2204             js_snakenv_ihexstr_nex(jsp, jo3p, nmec, count, true, NULL, NULL,
2205                                    NULL);
2206         }
2207         if (op->do_pcb)
2208             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
2209                       str, sizeof(str)));
2210         if (jsp->pr_as_json)
2211             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
2212         if (op->filter_given)
2213             break;
2214 skip:
2215         num -= pl;
2216         bp += pl;
2217     }
2218     return true;
2219 }
2220 
2221 /* PCT_LPAGE [0x1a] <pct>  introduced: SPC-4 */
2222 static bool
show_power_condition_transitions_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)2223 show_power_condition_transitions_page(const uint8_t * resp, int len,
2224                                       struct opts_t * op, sgj_opaque_p jop)
2225 {
2226     bool partial;
2227     int num, pl, pc;
2228     uint64_t count;
2229     const uint8_t * bp;
2230     const char * cp;
2231     sgj_state * jsp = &op->json_st;
2232     sgj_opaque_p jo2p;
2233     sgj_opaque_p jo3p = NULL;
2234     sgj_opaque_p jap = NULL;
2235     char str[PCB_STR_LEN];
2236     char b[128];
2237     char bb[64];
2238     static const char * pctlp = "Power condition transitions log page";
2239     static const char * att = "Accumulated transitions to";
2240 
2241     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
2242         sgj_pr_hr(jsp, "%s  [0x1a]\n", pctlp);
2243     num = len - 4;
2244     bp = &resp[0] + 4;
2245     if (jsp->pr_as_json) {
2246         jo2p = sg_log_js_hdr(jsp, jop, pctlp, resp);
2247         jap = sgj_named_subarray_r(jsp, jo2p,
2248                         "power_condition_transition_log_parameters");
2249     }
2250 
2251     while (num > 3) {
2252         pc = sg_get_unaligned_be16(bp + 0);
2253         pl = bp[3] + 4;
2254         if (op->filter_given) {
2255             if (pc != op->filter)
2256                 goto skip;
2257         }
2258         if (op->do_raw) {
2259             dStrRaw(bp, pl);
2260             break;
2261         } else if (op->do_hex) {
2262             hex2stdout(bp, pl, op->dstrhex_no_ascii);
2263             break;
2264         }
2265         if (jsp->pr_as_json) {
2266             jo3p = sgj_new_unattached_object_r(jsp);
2267             if (op->do_pcb)
2268                 js_pcb(jsp, jo3p, bp[2]);
2269         }
2270 
2271         cp = NULL;
2272         partial = true;
2273         switch (pc) {
2274         case 1:
2275             cp = "active";
2276             break;
2277         case 2:
2278             cp = "idle_a";
2279             break;
2280         case 3:
2281             cp = "idle_b";
2282             break;
2283         case 4:
2284             cp = "idle_c";
2285             break;
2286         case 8:
2287             cp = "standby_z";
2288             break;
2289         case 9:
2290             cp = "standby_y";
2291             break;
2292         default:
2293             snprintf(bb, sizeof(bb), "Reserved [0x%x]", pc);
2294             cp = bb;
2295             partial = false;
2296             break;
2297         }
2298         if (partial) {
2299             snprintf(b, sizeof(b), "%s %s", att, cp);
2300             cp = b;
2301         }
2302         count = sg_get_unaligned_be(pl - 4, bp + 4);
2303         sgj_pr_hr(jsp, "  %s = %" PRIu64 "\n", cp, count);
2304         if (op->do_pcb)
2305             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
2306                       str, sizeof(str)));
2307         if (jsp->pr_as_json) {
2308             js_snakenv_ihexstr_nex(jsp, jo3p, cp, count, true,
2309                                    NULL, NULL, "saturating counter");
2310             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
2311         }
2312         if (op->filter_given)
2313             break;
2314 skip:
2315         num -= pl;
2316         bp += pl;
2317     }
2318     return true;
2319 }
2320 
2321 static char *
temperature_str(int8_t t,bool reporting,char * b,int blen)2322 temperature_str(int8_t t, bool reporting, char * b, int blen)
2323 {
2324     if (-128 == t) {
2325         if (reporting)
2326             snprintf(b, blen, "%s", not_avail);
2327         else
2328             snprintf(b, blen, "no limit");
2329     } else
2330         snprintf(b, blen, "%d C", t);
2331     return b;
2332 }
2333 
2334 static char *
humidity_str(uint8_t h,bool reporting,char * b,int blen)2335 humidity_str(uint8_t h, bool reporting, char * b, int blen)
2336 {
2337     if (255 == h) {
2338         if (reporting)
2339             snprintf(b, blen, "%s", not_avail);
2340         else
2341             snprintf(b, blen, "no limit");
2342     } else if (h <= 100)
2343         snprintf(b, blen, "%u %%", h);
2344     else
2345         snprintf(b, blen, "%s value [%u]", rsv_s, h);
2346     return b;
2347 }
2348 
2349 /* ENV_REPORTING_SUBPG [0xd,0x1] <env> introduced: SPC-5 (rev 02). "mounted"
2350  * changed to "other" in spc5r11 */
2351 static bool
show_environmental_reporting_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)2352 show_environmental_reporting_page(const uint8_t * resp, int len,
2353                                   struct opts_t * op, sgj_opaque_p jop)
2354 {
2355     int num, pl, pc, blen;
2356     bool other_valid;
2357     const uint8_t * bp;
2358     sgj_state * jsp = &op->json_st;
2359     sgj_opaque_p jo2p;
2360     sgj_opaque_p jo3p = NULL;
2361     sgj_opaque_p jap = NULL;
2362     char str[PCB_STR_LEN];
2363     char b[32];
2364     static const char * erlp = "Environmental reporting log page";
2365     static const char * temp = "Temperature";
2366     static const char * lmaxt = "Lifetime maximum temperature";
2367     static const char * lmint = "Lifetime minimum temperature";
2368     static const char * maxtspo = "Maximum temperature since power on";
2369     static const char * mintspo = "Minimum temperature since power on";
2370     static const char * maxot = "Maximum other temperature";
2371     static const char * minot = "Minimum other temperature";
2372     static const char * relhum = "Relative humidity";
2373     static const char * lmaxrh = "Lifetime maximum relative humidity";
2374     static const char * lminrh = "Lifetime minimum relative humidity";
2375     static const char * maxrhspo = "Maximum relative humidity since power on";
2376     static const char * minrhspo = "Minimum relative humidity since power on";
2377     static const char * maxorh = "Maximum other relative humidity";
2378     static const char * minorh = "Minimum other relative humidity";
2379 
2380     blen = sizeof(b);
2381     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
2382         sgj_pr_hr(jsp, "%s  [0xd,0x1]\n", erlp);
2383     if (jsp->pr_as_json) {
2384         jo2p = sg_log_js_hdr(jsp, jop, erlp, resp);
2385         jap = sgj_named_subarray_r(jsp, jo2p,
2386                                    "environmental_reporting_log_parameters");
2387     }
2388     num = len - 4;
2389     bp = &resp[0] + 4;
2390     while (num > 3) {
2391         pc = sg_get_unaligned_be16(bp + 0);
2392         pl = bp[3] + 4;
2393         if (op->filter_given) {
2394             if (pc != op->filter)
2395                 goto skip;
2396         }
2397         if (op->do_raw) {
2398             dStrRaw(bp, pl);
2399             break;
2400         } else if (op->do_hex) {
2401             hex2stdout(bp, pl, op->dstrhex_no_ascii);
2402             break;
2403         }
2404         if (jsp->pr_as_json) {
2405             jo3p = sgj_new_unattached_object_r(jsp);
2406             if (op->do_pcb)
2407                 js_pcb(jsp, jo3p, bp[2]);
2408         }
2409         other_valid = !!(bp[4] & 1);
2410         if (pc < 0x100) {
2411             if (pl < 12)  {
2412                 pr2serr("  <<expect parameter 0x%x to be at least 12 bytes "
2413                         "long, got %d, skip>>\n", pc, pl);
2414                 goto inner;
2415             }
2416             sgj_pr_hr(jsp, "  %s=0x%x\n", param_c, pc);
2417             sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc);
2418             sgj_pr_hr(jsp, "    OTV=%d\n", (int)other_valid);
2419             sgj_js_nv_ihex_nex(jsp, jo3p, "otv",  (int)other_valid,
2420                                false, "Other Temperature Valid");
2421 
2422             temperature_str(bp[5], true, b, blen);
2423             sgj_pr_hr(jsp, "    %s: %s\n", temp, b);
2424             js_snakenv_ihexstr_nex(jsp, jo3p, temp, bp[5], false,
2425                                    NULL, b, "current [Celsius]");
2426             temperature_str(bp[6], true, b, blen);
2427             sgj_pr_hr(jsp, "    %s: %s\n", lmaxt, b);
2428             js_snakenv_ihexstr_nex(jsp, jo3p, lmaxt, bp[6], false,
2429                                    NULL, b, NULL);
2430             temperature_str(bp[7], true, b, blen);
2431             sgj_pr_hr(jsp, "    %s: %s\n", lmint, b);
2432             js_snakenv_ihexstr_nex(jsp, jo3p, lmint, bp[7], false,
2433                                    NULL, b, NULL);
2434             temperature_str(bp[8], true, b, blen);
2435             sgj_pr_hr(jsp, "    %s: %s\n", maxtspo, b);
2436             js_snakenv_ihexstr_nex(jsp, jo3p, maxtspo, bp[8], false,
2437                                    NULL, b, NULL);
2438             temperature_str(bp[9], true, b, blen);
2439             sgj_pr_hr(jsp, "    %s: %s\n", mintspo, b);
2440             js_snakenv_ihexstr_nex(jsp, jo3p, mintspo, bp[9], false,
2441                                    NULL, b, NULL);
2442             if (other_valid) {
2443                 temperature_str(bp[10], true, b, blen);
2444                 sgj_pr_hr(jsp, "    %s: %s\n", maxot, b);
2445                 js_snakenv_ihexstr_nex(jsp, jo3p, maxot, bp[10], false,
2446                                        NULL, b, NULL);
2447                 temperature_str(bp[11], true, b, blen);
2448                 sgj_pr_hr(jsp, "    %s: %s\n", minot, b);
2449                 js_snakenv_ihexstr_nex(jsp, jo3p, minot, bp[11], false,
2450                                        NULL, b, NULL);
2451             }
2452         } else if (pc < 0x200) {
2453             if (pl < 12)  {
2454                 pr2serr("  <<expect parameter 0x%x to be at least 12 bytes "
2455                         "long, got %d, skip>>\n", pc, pl);
2456                 goto inner;
2457             }
2458             sgj_pr_hr(jsp, "  %s=0x%x\n", param_c, pc);
2459             sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc);
2460             sgj_pr_hr(jsp, "    ORHV=%d\n", (int)other_valid);
2461             sgj_js_nv_ihex_nex(jsp, jo3p, "orhv",  (int)other_valid,
2462                                false, "Other Relative Humidity Valid");
2463 
2464             humidity_str(bp[5], true, b, blen);
2465             sgj_pr_hr(jsp, "    %s: %s\n", relhum, b);
2466             js_snakenv_ihexstr_nex(jsp, jo3p, relhum, bp[5], false,
2467                                    NULL, b, NULL);
2468             humidity_str(bp[6], true, b, blen);
2469             sgj_pr_hr(jsp, "    %s: %s\n", lmaxrh, b);
2470             js_snakenv_ihexstr_nex(jsp, jo3p, lmaxrh, bp[6], false,
2471                                    NULL, b, NULL);
2472             humidity_str(bp[7], true, b, blen);
2473             sgj_pr_hr(jsp, "    %s: %s\n", lminrh, b);
2474             js_snakenv_ihexstr_nex(jsp, jo3p, lminrh, bp[7], false,
2475                                    NULL, b, NULL);
2476             humidity_str(bp[8], true, b, blen);
2477             sgj_pr_hr(jsp, "    %s: %s\n", maxrhspo, b);
2478             js_snakenv_ihexstr_nex(jsp, jo3p, maxrhspo, bp[8], false,
2479                                    NULL, b, NULL);
2480             humidity_str(bp[9], true, b, blen);
2481             sgj_pr_hr(jsp, "    %s: %s\n", minrhspo, b);
2482             js_snakenv_ihexstr_nex(jsp, jo3p, minrhspo, bp[9], false,
2483                                    NULL, b, NULL);
2484             if (other_valid) {
2485                 humidity_str(bp[10], true, b, blen);
2486                 sgj_pr_hr(jsp, "    %s: %s\n", maxorh, b);
2487                 js_snakenv_ihexstr_nex(jsp, jo3p, maxorh, bp[10], false,
2488                                        NULL, b, NULL);
2489                 humidity_str(bp[11], true, b, blen);
2490                 sgj_pr_hr(jsp, "    %s: %s\n", minorh, b);
2491                 js_snakenv_ihexstr_nex(jsp, jo3p, minorh, bp[11], false,
2492                                        NULL, b, NULL);
2493             }
2494         } else
2495             sgj_pr_hr(jsp, "  <<unexpected %s 0x%x\n", param_c, pc);
2496         if (op->do_pcb)
2497             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
2498                       sizeof(str)));
2499 inner:
2500         if (jsp->pr_as_json)
2501             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
2502         if (op->filter_given)
2503             break;
2504 skip:
2505         num -= pl;
2506         bp += pl;
2507     }
2508     return true;
2509 }
2510 
2511 /* ENV_LIMITS_SUBPG [0xd,0x2] <enl> introduced: SPC-5 (rev 02) */
2512 static bool
show_environmental_limits_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)2513 show_environmental_limits_page(const uint8_t * resp, int len,
2514                                struct opts_t * op, sgj_opaque_p jop)
2515 {
2516     int num, pl, pc, blen;
2517     const uint8_t * bp;
2518     sgj_state * jsp = &op->json_st;
2519     sgj_opaque_p jo2p;
2520     sgj_opaque_p jo3p = NULL;
2521     sgj_opaque_p jap = NULL;
2522     char str[PCB_STR_LEN];
2523     char b[32];
2524     static const char * ellp = "Environmental limits log page";
2525     static const char * hctlt = "High critical temperature limit trigger";
2526     static const char * hctlr = "High critical temperature limit reset";
2527     static const char * lctlr = "High critical temperature limit reset";
2528     static const char * lctlt = "High critical temperature limit trigger";
2529     static const char * hotlt = "High operating temperature limit trigger";
2530     static const char * hotlr = "High operating temperature limit reset";
2531     static const char * lotlr = "High operating temperature limit reset";
2532     static const char * lotlt = "High operating temperature limit trigger";
2533     static const char * hcrhlt =
2534                 "High critical relative humidity limit trigger";
2535     static const char * hcrhlr =
2536                 "High critical relative humidity limit reset";
2537     static const char * lcrhlr =
2538                 "High critical relative humidity limit reset";
2539     static const char * lcrhlt =
2540                 "High critical relative humidity limit trigger";
2541     static const char * horhlt =
2542                 "High operating relative humidity limit trigger";
2543     static const char * horhlr =
2544                 "High operating relative humidity limit reset";
2545     static const char * lorhlr =
2546                 "High operating relative humidity limit reset";
2547     static const char * lorhlt =
2548                 "High operating relative humidity limit trigger";
2549 
2550     blen = sizeof(b);
2551     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
2552         sgj_pr_hr(jsp, "%s  [0xd,0x2]\n", ellp);
2553     if (jsp->pr_as_json) {
2554         jo2p = sg_log_js_hdr(jsp, jop, ellp, resp);
2555         jap = sgj_named_subarray_r(jsp, jo2p,
2556                                    "environmental_limits_log_parameters");
2557     }
2558     num = len - 4;
2559     bp = &resp[0] + 4;
2560     while (num > 3) {
2561         pc = sg_get_unaligned_be16(bp + 0);
2562         pl = bp[3] + 4;
2563         if (op->filter_given) {
2564             if (pc != op->filter)
2565                 goto skip;
2566         }
2567         if (op->do_raw) {
2568             dStrRaw(bp, pl);
2569             break;
2570         } else if (op->do_hex) {
2571             hex2stdout(bp, pl, op->dstrhex_no_ascii);
2572             break;
2573         }
2574         if (jsp->pr_as_json) {
2575             jo3p = sgj_new_unattached_object_r(jsp);
2576             if (op->do_pcb)
2577                 js_pcb(jsp, jo3p, bp[2]);
2578         }
2579         if (pc < 0x100) {
2580             if (pl < 12)  {
2581                 pr2serr("  <<expect parameter 0x%x to be at least 12 bytes "
2582                         "long, got %d, skip>>\n", pc, pl);
2583                 goto inner;
2584             }
2585             sgj_pr_hr(jsp, "  %s=0x%x\n", param_c, pc);
2586             sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc);
2587 
2588             temperature_str(bp[4], true, b, blen);
2589             sgj_pr_hr(jsp, "    %s: %s\n", hctlt, b);
2590             js_snakenv_ihexstr_nex(jsp, jo3p, hctlt, bp[4], false,
2591                                    NULL, b, "[Celsius]");
2592             temperature_str(bp[5], true, b, blen);
2593             sgj_pr_hr(jsp, "    %s: %s\n", hctlr, b);
2594             js_snakenv_ihexstr_nex(jsp, jo3p, hctlr, bp[5], false,
2595                                    NULL, b, NULL);
2596             temperature_str(bp[6], true, b, blen);
2597             sgj_pr_hr(jsp, "    %s: %s\n", lctlr, b);
2598             js_snakenv_ihexstr_nex(jsp, jo3p, lctlr, bp[6], false,
2599                                    NULL, b, NULL);
2600             temperature_str(bp[7], true, b, blen);
2601             sgj_pr_hr(jsp, "    %s: %s\n", lctlt, b);
2602             js_snakenv_ihexstr_nex(jsp, jo3p, lctlt, bp[7], false,
2603                                    NULL, b, NULL);
2604             temperature_str(bp[8], true, b, blen);
2605             sgj_pr_hr(jsp, "    %s: %s\n", hotlt, b);
2606             js_snakenv_ihexstr_nex(jsp, jo3p, hotlt, bp[8], false,
2607                                    NULL, b, NULL);
2608             temperature_str(bp[9], true, b, blen);
2609             sgj_pr_hr(jsp, "    %s: %s\n", hotlr, b);
2610             js_snakenv_ihexstr_nex(jsp, jo3p, hotlr, bp[9], false,
2611                                    NULL, b, NULL);
2612             temperature_str(bp[10], true, b, blen);
2613             sgj_pr_hr(jsp, "    %s: %s\n", lotlr, b);
2614             js_snakenv_ihexstr_nex(jsp, jo3p, lotlr, bp[10], false,
2615                                    NULL, b, NULL);
2616             temperature_str(bp[11], true, b, blen);
2617             sgj_pr_hr(jsp, "    %s: %s\n", lotlt, b);
2618             js_snakenv_ihexstr_nex(jsp, jo3p, lotlt, bp[11], false,
2619                                    NULL, b, NULL);
2620         } else if (pc < 0x200) {
2621             if (pl < 12)  {
2622                 pr2serr("  <<expect parameter 0x%x to be at least 12 bytes "
2623                         "long, got %d, skip>>\n", pc, pl);
2624                 goto inner;
2625             }
2626             sgj_pr_hr(jsp, "  %s=0x%x\n", param_c, pc);
2627             sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc);
2628 
2629             humidity_str(bp[4], true, b, blen);
2630             sgj_pr_hr(jsp, "    %s: %s\n", hcrhlt, b);
2631             js_snakenv_ihexstr_nex(jsp, jo3p, hcrhlt, bp[4], false,
2632                                    NULL, b, "[percentage]");
2633             humidity_str(bp[5], true, b, blen);
2634             sgj_pr_hr(jsp, "    %s: %s\n", hcrhlr, b);
2635             js_snakenv_ihexstr_nex(jsp, jo3p, hcrhlr, bp[5], false,
2636                                    NULL, b, NULL);
2637             humidity_str(bp[6], true, b, blen);
2638             sgj_pr_hr(jsp, "    %s: %s\n", lcrhlr, b);
2639             js_snakenv_ihexstr_nex(jsp, jo3p, lcrhlr, bp[6], false,
2640                                    NULL, b, NULL);
2641             humidity_str(bp[7], true, b, blen);
2642             sgj_pr_hr(jsp, "    %s: %s\n", lcrhlt, b);
2643             js_snakenv_ihexstr_nex(jsp, jo3p, lcrhlt, bp[7], false,
2644                                    NULL, b, NULL);
2645             humidity_str(bp[8], true, b, blen);
2646             sgj_pr_hr(jsp, "    %s: %s\n", horhlt, b);
2647             js_snakenv_ihexstr_nex(jsp, jo3p, horhlt, bp[8], false,
2648                                    NULL, b, NULL);
2649             humidity_str(bp[9], true, b, blen);
2650             sgj_pr_hr(jsp, "    %s: %s\n", horhlr, b);
2651             js_snakenv_ihexstr_nex(jsp, jo3p, horhlr, bp[9], false,
2652                                    NULL, b, NULL);
2653             humidity_str(bp[10], true, b, blen);
2654             sgj_pr_hr(jsp, "    %s: %s\n", lorhlr, b);
2655             js_snakenv_ihexstr_nex(jsp, jo3p, lorhlr, bp[10], false,
2656                                    NULL, b, NULL);
2657             humidity_str(bp[11], true, b, blen);
2658             sgj_pr_hr(jsp, "    %s: %s\n", lorhlt, b);
2659             js_snakenv_ihexstr_nex(jsp, jo3p, lorhlt, bp[11], false,
2660                                    NULL, b, NULL);
2661         } else
2662              sgj_pr_hr(jsp, "  <<unexpected %s 0x%x\n", param_c, pc);
2663         if (op->do_pcb)
2664             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
2665                       str, sizeof(str)));
2666 inner:
2667         if (jsp->pr_as_json)
2668             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
2669         if (op->filter_given)
2670             break;
2671 skip:
2672         num -= pl;
2673         bp += pl;
2674     }
2675     return true;
2676 }
2677 
2678 /* CMD_DUR_LIMITS_SUBPG [0x19,0x21] <cdl>
2679  * introduced: SPC-6 rev 1, significantly changed rev 6 */
2680 static bool
show_cmd_dur_limits_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)2681 show_cmd_dur_limits_page(const uint8_t * resp, int len,
2682                          struct opts_t * op, sgj_opaque_p jop)
2683 {
2684     int num, pl, pc;
2685     uint32_t count, noitmc_v, noatmc_v, noitatmc_v, noc_v;
2686     const uint8_t * bp;
2687     const char * cp;
2688     const char * thp;
2689     sgj_state * jsp = &op->json_st;
2690     sgj_opaque_p jo2p;
2691     sgj_opaque_p jo3p = NULL;
2692     sgj_opaque_p jap = NULL;
2693     char str[PCB_STR_LEN];
2694     char b[144];
2695     static const char * cdllp = "Command duration limits statistics log page";
2696     static const char * t2cdld = "T2 command duration limit descriptor";
2697     static const char * cdlt2amp = "CDL T2A mode page";
2698     static const char * cdlt2bmp = "CDL T2B mode page";
2699     static const char * first_7[] = {"First", "Second", "Third", "Fourth",
2700                                      "Fifth", "Sixth", "Seventh"};
2701     static const char * noitmc = "Number of inactive target miss commands";
2702     static const char * noatmc = "Number of active target miss commands";
2703     static const char * noitatmc =
2704         "Number of inactive target and active target miss commands";
2705     static const char * noc = "Number of commands";
2706 
2707     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
2708         sgj_pr_hr(jsp, "%s  [0x19,0x21]\n", cdllp);
2709     num = len - 4;
2710     bp = &resp[0] + 4;
2711     if (jsp->pr_as_json) {
2712         jo2p = sg_log_js_hdr(jsp, jop, cdllp, resp);
2713         jap = sgj_named_subarray_r(jsp, jo2p,
2714                         "command_duration_limits_statistcs_log_parameters");
2715     }
2716 
2717     while (num > 3) {
2718         pc = sg_get_unaligned_be16(bp + 0);
2719         pl = bp[3] + 4;         /* parameter length */
2720         if (op->filter_given) {
2721             if (pc != op->filter)
2722                 goto skip;
2723         }
2724         if (op->do_raw) {
2725             dStrRaw(bp, pl);
2726             break;
2727         } else if (op->do_hex) {
2728             hex2stdout(bp, pl, op->dstrhex_no_ascii);
2729             break;
2730         }
2731         if (jsp->pr_as_json) {
2732             jo3p = sgj_new_unattached_object_r(jsp);
2733             if (op->do_pcb)
2734                 js_pcb(jsp, jo3p, bp[2]);
2735         }
2736 
2737         switch (pc) {
2738         case 0x1:
2739             /* spc6r06: table 349 name "Number of READ commands" seems to
2740              * be wrong. Use what surrounding text and table 347 suggest */
2741             cp = "Achievable latency target";
2742             count =  sg_get_unaligned_be32(bp + 4);
2743             sgj_pr_hr(jsp, "  %s = %" PRIu32 "\n", cp, count);
2744             if (jsp->pr_as_json) {
2745                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, cp);
2746                 js_snakenv_ihexstr_nex(jsp, jop, cp, count, true, NULL, NULL,
2747                                        "unit: microsecond");
2748             }
2749             break;
2750         case 0x11:
2751         case 0x12:
2752         case 0x13:
2753         case 0x14:
2754         case 0x15:
2755         case 0x16:
2756         case 0x17:
2757             sgj_pr_hr(jsp, "  %s code 0x%x restricted\n", param_c, pc);
2758             if (jsp->pr_as_json)
2759                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, rstrict_s);
2760             break;
2761         case 0x21:
2762         case 0x22:
2763         case 0x23:
2764         case 0x24:
2765         case 0x25:
2766         case 0x26:
2767         case 0x27:
2768             thp = first_7[pc - 0x21];
2769             sgj_pr_hr(jsp, "  %s %s for %s [pc=0x%x]:\n", thp, t2cdld,
2770                       cdlt2amp, pc);
2771             noitmc_v = sg_get_unaligned_be32(bp + 4);
2772             sgj_pr_hr(jsp, "    %s = %u\n", noitmc, noitmc_v);
2773             noatmc_v = sg_get_unaligned_be32(bp + 8);
2774             sgj_pr_hr(jsp, "    %s = %u\n", noatmc, noatmc_v);
2775             noitatmc_v = sg_get_unaligned_be32(bp + 12);
2776             sgj_pr_hr(jsp, "    %s = %u\n", noitatmc, noitatmc_v);
2777             noc_v = sg_get_unaligned_be32(bp + 16);
2778             sgj_pr_hr(jsp, "    %s = %u\n", noc, noc_v);
2779             if (jsp->pr_as_json) {
2780                 snprintf(b, sizeof(b), "%s %s for %s", thp, t2cdld, cdlt2amp);
2781                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, b);
2782 
2783                 js_snakenv_ihexstr_nex(jsp, jop, noitmc, noitmc_v, true, NULL,
2784                                        NULL, NULL);
2785                 js_snakenv_ihexstr_nex(jsp, jop, noatmc, noatmc_v, true, NULL,
2786                                        NULL, NULL);
2787                 js_snakenv_ihexstr_nex(jsp, jop, noitatmc, noitatmc_v, true,
2788                                        NULL, NULL, NULL);
2789                 js_snakenv_ihexstr_nex(jsp, jop, noc, noc_v, true, NULL,
2790                                        NULL, NULL);
2791             }
2792             break;
2793         case 0x31:
2794         case 0x32:
2795         case 0x33:
2796         case 0x34:
2797         case 0x35:
2798         case 0x36:
2799         case 0x37:
2800             sgj_pr_hr(jsp, "  %s 0x%x restricted\n", param_c, pc);
2801             if (jsp->pr_as_json)
2802                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, rstrict_s);
2803             break;
2804         case 0x41:
2805         case 0x42:
2806         case 0x43:
2807         case 0x44:
2808         case 0x45:
2809         case 0x46:
2810         case 0x47:
2811             /* This short form introduced in draft spc6r06 */
2812             thp = first_7[pc - 0x41];
2813             sgj_pr_hr(jsp, "  %s %s for %s [pc=0x%x]:\n", thp, t2cdld,
2814                       cdlt2bmp, pc);
2815             noitmc_v = sg_get_unaligned_be32(bp + 4);
2816             sgj_pr_hr(jsp, "    %s = %u\n", noitmc, noitmc_v);
2817             noatmc_v = sg_get_unaligned_be32(bp + 8);
2818             sgj_pr_hr(jsp, "    %s = %u\n", noatmc, noatmc_v);
2819             noitatmc_v = sg_get_unaligned_be32(bp + 12);
2820             sgj_pr_hr(jsp, "    %s = %u\n", noitatmc, noitatmc_v);
2821             noc_v = sg_get_unaligned_be32(bp + 16);
2822             sgj_pr_hr(jsp, "    %s = %u\n", noc, noc_v);
2823             if (jsp->pr_as_json) {
2824                 snprintf(b, sizeof(b), "%s %s for %s", thp, t2cdld, cdlt2amp);
2825                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, b);
2826 
2827                 js_snakenv_ihexstr_nex(jsp, jop, noitmc, noitmc_v, true, NULL,
2828                                        NULL, NULL);
2829                 js_snakenv_ihexstr_nex(jsp, jop, noatmc, noatmc_v, true, NULL,
2830                                        NULL, NULL);
2831                 js_snakenv_ihexstr_nex(jsp, jop, noitatmc, noitatmc_v, true,
2832                                        NULL, NULL, NULL);
2833                 js_snakenv_ihexstr_nex(jsp, jop, noc, noc_v, true, NULL,
2834                                        NULL, NULL);
2835             }
2836 
2837             break;
2838         default:
2839              sgj_pr_hr(jsp, "  <<unexpected %s 0x%x\n", param_c, pc);
2840             break;
2841         }
2842         if (op->do_pcb)
2843             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
2844                       str, sizeof(str)));
2845         if (jsp->pr_as_json)
2846             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
2847         if (op->filter_given)
2848             break;
2849 skip:
2850         num -= pl;
2851         bp += pl;
2852     }
2853     return true;
2854 }
2855 
2856 /* Tape usage: Vendor specific (LTO-5 and LTO-6): 0x30 */
2857 static bool
show_tape_usage_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)2858 show_tape_usage_page(const uint8_t * resp, int len, struct opts_t * op,
2859                      sgj_opaque_p jop)
2860 {
2861     int k, num, extra;
2862     unsigned int n;
2863     uint64_t ull;
2864     const uint8_t * bp;
2865     char str[PCB_STR_LEN];
2866 
2867 if (jop) { };
2868     num = len - 4;
2869     bp = &resp[0] + 4;
2870     if (num < 4) {
2871         pr2serr("badly formed tape usage page\n");
2872         return false;
2873     }
2874     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
2875         printf("Tape usage page  (LTO-5 and LTO-6 specific) [0x30]\n");
2876     for (k = num; k > 0; k -= extra, bp += extra) {
2877         int pc = sg_get_unaligned_be16(bp + 0);
2878 
2879         extra = bp[3] + 4;
2880         if (op->filter_given) {
2881             if (pc != op->filter)
2882                 continue;
2883         }
2884         if (op->do_raw) {
2885             dStrRaw(bp, extra);
2886             break;
2887         } else if (op->do_hex) {
2888             hex2stdout(bp, extra, op->dstrhex_no_ascii);
2889             break;
2890         }
2891         ull = n = 0;
2892         switch (bp[3]) {
2893         case 2:
2894             n = sg_get_unaligned_be16(bp + 4);
2895             break;
2896         case 4:
2897             n = sg_get_unaligned_be32(bp + 4);
2898             break;
2899         case 8:
2900             ull = sg_get_unaligned_be64(bp + 4);
2901             break;
2902         }
2903         switch (pc) {
2904         case 0x01:
2905             if (extra == 8)
2906                 printf("  Thread count: %u", n);
2907             break;
2908         case 0x02:
2909             if (extra == 12)
2910                 printf("  Total data sets written: %" PRIu64, ull);
2911             break;
2912         case 0x03:
2913             if (extra == 8)
2914                 printf("  Total write retries: %u", n);
2915             break;
2916         case 0x04:
2917             if (extra == 6)
2918                 printf("  Total unrecovered write errors: %u", n);
2919             break;
2920         case 0x05:
2921             if (extra == 6)
2922                 printf("  Total suspended writes: %u", n);
2923             break;
2924         case 0x06:
2925             if (extra == 6)
2926                 printf("  Total fatal suspended writes: %u", n);
2927             break;
2928         case 0x07:
2929             if (extra == 12)
2930                 printf("  Total data sets read: %" PRIu64, ull);
2931             break;
2932         case 0x08:
2933             if (extra == 8)
2934                 printf("  Total read retries: %u", n);
2935             break;
2936         case 0x09:
2937             if (extra == 6)
2938                 printf("  Total unrecovered read errors: %u", n);
2939             break;
2940         case 0x0a:
2941             if (extra == 6)
2942                 printf("  Total suspended reads: %u", n);
2943             break;
2944         case 0x0b:
2945             if (extra == 6)
2946                 printf("  Total fatal suspended reads: %u", n);
2947             break;
2948         default:
2949             printf("  unknown %s = 0x%x, contents in hex:\n", param_c, pc);
2950             hex2stdout(bp, extra, 1);
2951             break;
2952         }
2953         printf("\n");
2954         if (op->do_pcb)
2955             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
2956         if (op->filter_given)
2957             break;
2958     }
2959     return true;
2960 }
2961 
2962 /* 0x30 */
2963 static bool
show_hgst_perf_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)2964 show_hgst_perf_page(const uint8_t * resp, int len, struct opts_t * op,
2965                     sgj_opaque_p jop)
2966 {
2967     bool valid = false;
2968     int num, pl;
2969     const uint8_t * bp;
2970     char str[PCB_STR_LEN];
2971 
2972 if (jop) { };
2973     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
2974         printf("HGST/WDC performance counters page [0x30]\n");
2975     num = len - 4;
2976     if (num < 0x30) {
2977         printf("HGST/WDC performance counters page too short (%d) < 48\n",
2978                num);
2979         return valid;
2980     }
2981     bp = &resp[0] + 4;
2982     while (num > 3) {
2983         int pc = sg_get_unaligned_be16(bp + 0);
2984 
2985         pl = bp[3] + 4;
2986         if (op->filter_given) {
2987             if (pc != op->filter)
2988                 goto skip;
2989         }
2990         if (op->do_raw) {
2991             dStrRaw(bp, pl);
2992             break;
2993         } else if (op->do_hex) {
2994             hex2stdout(bp, pl, op->dstrhex_no_ascii);
2995             break;
2996         }
2997         switch (pc) {
2998         case 0:
2999             valid = true;
3000             printf("  Zero Seeks = %u\n", sg_get_unaligned_be16(bp + 4));
3001             printf("  Seeks >= 2/3 = %u\n", sg_get_unaligned_be16(bp + 6));
3002             printf("  Seeks >= 1/3 and < 2/3 = %u\n",
3003                    sg_get_unaligned_be16(bp + 8));
3004             printf("  Seeks >= 1/6 and < 1/3 = %u\n",
3005                    sg_get_unaligned_be16(bp + 10));
3006             printf("  Seeks >= 1/12 and < 1/6 = %u\n",
3007                    sg_get_unaligned_be16(bp + 12));
3008             printf("  Seeks > 0 and < 1/12 = %u\n",
3009                    sg_get_unaligned_be16(bp + 14));
3010             printf("  Overrun Counter = %u\n",
3011                    sg_get_unaligned_be16(bp + 20));
3012             printf("  Underrun Counter = %u\n",
3013                    sg_get_unaligned_be16(bp + 22));
3014             printf("  Device Cache Full Read Hits = %u\n",
3015                    sg_get_unaligned_be32(bp + 24));
3016             printf("  Device Cache Partial Read Hits = %u\n",
3017                    sg_get_unaligned_be32(bp + 28));
3018             printf("  Device Cache Write Hits = %u\n",
3019                    sg_get_unaligned_be32(bp + 32));
3020             printf("  Device Cache Fast Writes = %u\n",
3021                    sg_get_unaligned_be32(bp + 36));
3022             printf("  Device Cache Read Misses = %u\n",
3023                    sg_get_unaligned_be32(bp + 40));
3024             break;
3025         default:
3026             valid = false;
3027             printf("  Unknown HGST/WDC %s = 0x%x", param_c, pc);
3028             break;
3029         }
3030         if (op->do_pcb)
3031             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
3032         if (op->filter_given)
3033             break;
3034 skip:
3035         num -= pl;
3036         bp += pl;
3037     }
3038     return valid;
3039 }
3040 
3041 /* Tape capacity: vendor specific (LTO-5 and LTO-6 ?): 0x31 */
3042 static bool
show_tape_capacity_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3043 show_tape_capacity_page(const uint8_t * resp, int len,
3044                         struct opts_t * op, sgj_opaque_p jop)
3045 {
3046     int k, num, extra;
3047     unsigned int n;
3048     const uint8_t * bp;
3049     char str[PCB_STR_LEN];
3050 
3051 if (jop) { };
3052     num = len - 4;
3053     bp = &resp[0] + 4;
3054     if (num < 4) {
3055         pr2serr("badly formed tape capacity page\n");
3056         return false;
3057     }
3058     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
3059         printf("Tape capacity page  (LTO-5 and LTO-6 specific) [0x31]\n");
3060     for (k = num; k > 0; k -= extra, bp += extra) {
3061         int pc = sg_get_unaligned_be16(bp + 0);
3062 
3063         extra = bp[3] + 4;
3064         if (op->filter_given) {
3065             if (pc != op->filter)
3066                 continue;
3067         }
3068         if (op->do_raw) {
3069             dStrRaw(bp, extra);
3070             break;
3071         } else if (op->do_hex) {
3072             hex2stdout(bp, extra, op->dstrhex_no_ascii);
3073             break;
3074         }
3075         if (extra != 8)
3076             continue;
3077         n = sg_get_unaligned_be32(bp + 4);
3078         switch (pc) {
3079         case 0x01:
3080             printf("  Main partition remaining capacity (in MiB): %u", n);
3081             break;
3082         case 0x02:
3083             printf("  Alternate partition remaining capacity (in MiB): %u", n);
3084             break;
3085         case 0x03:
3086             printf("  Main partition maximum capacity (in MiB): %u", n);
3087             break;
3088         case 0x04:
3089             printf("  Alternate partition maximum capacity (in MiB): %u", n);
3090             break;
3091         default:
3092             printf("  unknown %s = 0x%x, contents in hex:\n", param_c, pc);
3093             hex2stdout(bp, extra, 1);
3094             break;
3095         }
3096         printf("\n");
3097         if (op->do_pcb)
3098             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
3099         if (op->filter_given)
3100             break;
3101     }
3102     return true;
3103 }
3104 
3105 /* Data compression: originally vendor specific 0x32 (LTO-5), then
3106  * ssc-4 standardizes it at 0x1b <dc> */
3107 static bool
show_data_compression_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3108 show_data_compression_page(const uint8_t * resp, int len,
3109                            struct opts_t * op, sgj_opaque_p jop)
3110 {
3111     int k, j, pl, num, extra, pc, pg_code;
3112     uint64_t n;
3113     const uint8_t * bp;
3114     char str[PCB_STR_LEN];
3115 
3116 if (jop) { };
3117     pg_code = resp[0] & 0x3f;
3118     num = len - 4;
3119     bp = &resp[0] + 4;
3120     if (num < 4) {
3121         pr2serr("badly formed data compression page\n");
3122         return false;
3123     }
3124     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
3125         if (0x1b == pg_code)
3126             printf("Data compression page  (ssc-4) [0x1b]\n");
3127         else
3128             printf("Data compression page  (LTO-5 specific) [0x%x]\n",
3129                    pg_code);
3130     }
3131     for (k = num; k > 0; k -= extra, bp += extra) {
3132         pc = sg_get_unaligned_be16(bp + 0);
3133         pl = bp[3];
3134         extra = pl + 4;
3135         if (op->filter_given) {
3136             if (pc != op->filter)
3137                 continue;
3138         }
3139         if (op->do_raw) {
3140             dStrRaw(bp, extra);
3141             break;
3142         } else if (op->do_hex) {
3143             hex2stdout(bp, extra, op->dstrhex_no_ascii);
3144             break;
3145         }
3146         if ((0 == pl) || (pl > 8)) {
3147             printf("badly formed data compression log parameter\n");
3148             printf("  %s = 0x%x, contents in hex:\n", param_c, pc);
3149             hex2stdout(bp, extra, op->dstrhex_no_ascii);
3150             goto skip_para;
3151         }
3152         /* variable length integer, max length 8 bytes */
3153         for (j = 0, n = 0; j < pl; ++j) {
3154             if (j > 0)
3155                 n <<= 8;
3156             n |= bp[4 + j];
3157         }
3158         switch (pc) {
3159         case 0x00:
3160             printf("  Read compression ratio x100: %" PRIu64 , n);
3161             break;
3162         case 0x01:
3163             printf("  Write compression ratio x100: %" PRIu64 , n);
3164             break;
3165         case 0x02:
3166             printf("  Megabytes transferred to server: %" PRIu64 , n);
3167             break;
3168         case 0x03:
3169             printf("  Bytes transferred to server: %" PRIu64 , n);
3170             break;
3171         case 0x04:
3172             printf("  Megabytes read from tape: %" PRIu64 , n);
3173             break;
3174         case 0x05:
3175             printf("  Bytes read from tape: %" PRIu64 , n);
3176             break;
3177         case 0x06:
3178             printf("  Megabytes transferred from server: %" PRIu64 , n);
3179             break;
3180         case 0x07:
3181             printf("  Bytes transferred from server: %" PRIu64 , n);
3182             break;
3183         case 0x08:
3184             printf("  Megabytes written to tape: %" PRIu64 , n);
3185             break;
3186         case 0x09:
3187             printf("  Bytes written to tape: %" PRIu64 , n);
3188             break;
3189         case 0x100:
3190             printf("  Data compression enabled: 0x%" PRIx64, n);
3191             break;
3192         default:
3193             printf("  unknown %s = 0x%x, contents in hex:\n", param_c, pc);
3194             hex2stdout(bp, extra, op->dstrhex_no_ascii);
3195             break;
3196         }
3197 skip_para:
3198         printf("\n");
3199         if (op->do_pcb)
3200             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
3201         if (op->filter_given)
3202             break;
3203     }
3204     return true;
3205 }
3206 
3207 /* LAST_N_ERR_LPAGE [0x7] <lne>  introduced: SPC-2 */
3208 static bool
show_last_n_error_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3209 show_last_n_error_page(const uint8_t * resp, int len,
3210                        struct opts_t * op, sgj_opaque_p jop)
3211 {
3212     int k, num, pl;
3213     const uint8_t * bp;
3214     sgj_state * jsp = &op->json_st;
3215     sgj_opaque_p jo2p;
3216     sgj_opaque_p jo3p = NULL;
3217     sgj_opaque_p jap = NULL;
3218     char str[PCB_STR_LEN];
3219     char b[256];
3220     static const char * lneelp = "Last n error events log page";
3221     static const char * eed = "error_event_data";
3222 
3223     num = len - 4;
3224     bp = &resp[0] + 4;
3225     if (num < 4) {
3226         sgj_pr_hr(jsp, "No error events logged\n");
3227         return true;
3228     }
3229     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
3230         sgj_pr_hr(jsp, "%s  [0x7]\n", lneelp);
3231     if (jsp->pr_as_json) {
3232         jo2p = sg_log_js_hdr(jsp, jop, lneelp, resp);
3233         jap = sgj_named_subarray_r(jsp, jo2p, "error_event_log_parameters");
3234     }
3235 
3236     for (k = num; k > 0; k -= pl, bp += pl) {
3237         uint16_t pc;
3238 
3239         if (k < 3) {
3240             pr2serr("short %s\n", lneelp);
3241             return false;
3242         }
3243         pl = bp[3] + 4;
3244         pc = sg_get_unaligned_be16(bp + 0);
3245         if (op->filter_given) {
3246             if (pc != op->filter)
3247                 continue;
3248         }
3249         if (op->do_raw) {
3250             dStrRaw(bp, pl);
3251             break;
3252         } else if (op->do_hex) {
3253             hex2stdout(bp, pl, op->dstrhex_no_ascii);
3254             break;
3255         }
3256         if (jsp->pr_as_json) {
3257             jo3p = sgj_new_unattached_object_r(jsp);
3258             if (op->do_pcb)
3259                 js_pcb(jsp, jo3p, bp[2]);
3260             sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, NULL);
3261         }
3262 
3263         sgj_pr_hr(jsp, "  Error event %u [0x%x]:\n", pc, pc);
3264         if (pl > 4) {
3265             if ((bp[2] & 0x1) && (bp[2] & 0x2)) {
3266                 sgj_pr_hr(jsp, "    [binary]:\n");
3267                 hex2str(bp + 4, pl - 4, "    ", op->hex2str_oformat,
3268                         sizeof(b), b);
3269                 sgj_pr_hr(jsp, "%s\n", b);
3270                 if (jsp->pr_as_json)
3271                     sgj_js_nv_hex_bytes(jsp, jo3p, eed, bp + 4, pl - 4);
3272             } else if (0x01 == (bp[2] & 0x3)) {  /* ASCII */
3273                 sgj_pr_hr(jsp, "    %.*s\n", pl - 4, (const char *)(bp + 4));
3274                 if (jsp->pr_as_json)
3275                     sgj_js_nv_s_len(jsp, jo3p, eed,
3276                                     (const char *)(bp + 4), pl - 4);
3277             } else {
3278                 sgj_pr_hr(jsp, "    [data counter?? (LP bit should be "
3279                           "set)]:\n");
3280                 hex2str(bp + 4, pl - 4, "    ", op->hex2str_oformat,
3281                         sizeof(b), b);
3282                 sgj_pr_hr(jsp, "%s\n", b);
3283                 if (jsp->pr_as_json)
3284                     sgj_js_nv_hex_bytes(jsp, jo3p, eed, bp + 4, pl - 4);
3285             }
3286         }
3287         if (op->do_pcb)
3288             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
3289                       str, sizeof(str)));
3290         if (jsp->pr_as_json)
3291             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
3292         if (op->filter_given)
3293             break;
3294     }
3295     return true;
3296 }
3297 
3298 /* LAST_N_DEFERRED_LPAGE [0xb] <lnd>  introduced: SPC-2 */
3299 static bool
show_last_n_deferred_error_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3300 show_last_n_deferred_error_page(const uint8_t * resp, int len,
3301                                 struct opts_t * op, sgj_opaque_p jop)
3302 {
3303     int k, n, num, pl;
3304     const uint8_t * bp;
3305     sgj_state * jsp = &op->json_st;
3306     sgj_opaque_p jo2p, jo4p;
3307     sgj_opaque_p jo3p = NULL;
3308     sgj_opaque_p jap = NULL;
3309     char str[PCB_STR_LEN];
3310     char b[512];
3311     static const char * lndeoaelp =
3312                 "Last n deferred errors or asynchronous events log page";
3313     static const char * deoae = "Deferred error or asynchronous event";
3314     static const char * sd = "sense_data";
3315 
3316     num = len - 4;
3317     bp = &resp[0] + 4;
3318     if (num < 4) {
3319         pr2serr("No deferred errors logged\n");
3320         return true;
3321     }
3322     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
3323         sgj_pr_hr(jsp, "%s  [0xb]\n", lndeoaelp);
3324     if (jsp->pr_as_json) {
3325         jo2p = sg_log_js_hdr(jsp, jop, lndeoaelp, resp);
3326         jap = sgj_named_subarray_r(jsp, jo2p,
3327                 "deferred_error_or_asynchronous_event_log_parameters");
3328     }
3329 
3330     for (k = num; k > 0; k -= pl, bp += pl) {
3331         int pc;
3332 
3333         if (k < 3) {
3334             pr2serr("short %s\n", lndeoaelp);
3335             return false;
3336         }
3337         pl = bp[3] + 4;
3338         pc = sg_get_unaligned_be16(bp + 0);
3339         if (op->filter_given) {
3340             if (pc != op->filter)
3341                 continue;
3342         }
3343         if (op->do_raw) {
3344             dStrRaw(bp, pl);
3345             break;
3346         } else if (op->do_hex) {
3347             hex2stdout(bp, pl, op->dstrhex_no_ascii);
3348             break;
3349         }
3350         if (jsp->pr_as_json) {
3351             jo3p = sgj_new_unattached_object_r(jsp);
3352             if (op->do_pcb)
3353                 js_pcb(jsp, jo3p, bp[2]);
3354             sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, deoae);
3355         }
3356         sgj_pr_hr(jsp, "  %s [0x%x]:\n", deoae, pc);
3357         if (op->do_brief > 0) {
3358             hex2stdout(bp + 4, pl - 4, op->dstrhex_no_ascii);
3359             hex2str(bp + 4, pl - 4, "    ", op->hex2str_oformat,
3360                     sizeof(b), b);
3361             sgj_pr_hr(jsp, "%s\n", b);
3362             if (jsp->pr_as_json)
3363                 sgj_js_nv_hex_bytes(jsp, jo3p, sd, bp + 4, pl - 4);
3364         } else {
3365 
3366             n = sg_get_sense_str("    ", bp + 4, pl - 4,  false, sizeof(b),
3367                                  b);
3368             sgj_pr_hr(jsp, "%.*s\n", n, b);
3369             if (jsp->pr_as_json) {
3370                 jo4p = sgj_named_subobject_r(jsp, jo3p, sd);
3371                 sgj_js_sense(jsp, jo4p, bp + 4, pl - 4);
3372             }
3373         }
3374         if (op->do_pcb)
3375             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
3376                       str, sizeof(str)));
3377         if (jsp->pr_as_json)
3378             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
3379         if (op->filter_given)
3380             break;
3381     }
3382     return true;
3383 }
3384 
3385 static const char * clgc = "Change list generation code";
3386 static const char * cgn = "Changed generation number";
3387 
3388 /* LAST_N_INQUIRY_DATA_CH_SUBPG [0xb,0x1] <lnic> introduced: SPC-5 (rev 17) */
3389 static bool
show_last_n_inq_data_ch_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3390 show_last_n_inq_data_ch_page(const uint8_t * resp, int len,
3391                              struct opts_t * op, sgj_opaque_p jop)
3392 {
3393     bool vpd;
3394     int j, num, pl, vpd_pg;
3395     uint32_t k, n;
3396     const uint8_t * bp;
3397     const char * vpd_pg_name = NULL;
3398     sgj_state * jsp = &op->json_st;
3399     sgj_opaque_p jo2p, jo4p;
3400     sgj_opaque_p jo3p = NULL;
3401     sgj_opaque_p jap = NULL;
3402     sgj_opaque_p ja2p;
3403     char str[PCB_STR_LEN];
3404     char b[128];
3405     static const char * lnidclp = "Last n inquiry data changed log page";
3406     static const char * idci = "Inquiry data changed indicator";
3407 
3408     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
3409         sgj_pr_hr(jsp, "%s  [0xb,0x1]\n", lnidclp);
3410     num = len - 4;
3411     bp = &resp[0] + 4;
3412     if (jsp->pr_as_json) {
3413         jo2p = sg_log_js_hdr(jsp, jop, lnidclp, resp);
3414         jap = sgj_named_subarray_r(jsp, jo2p,
3415                                    "inquiry_data_changed_log_parameters");
3416     }
3417 
3418     while (num > 3) {
3419         int pc = sg_get_unaligned_be16(bp + 0);
3420 
3421         pl = bp[3] + 4;
3422         if (op->filter_given) {
3423             if (pc != op->filter)
3424                 goto skip;
3425         }
3426         if (op->do_raw) {
3427             dStrRaw(bp, pl);
3428             break;
3429         } else if (op->do_hex) {
3430             hex2stdout(bp, pl, op->dstrhex_no_ascii);
3431             break;
3432         }
3433         if (jsp->pr_as_json) {
3434             jo3p = sgj_new_unattached_object_r(jsp);
3435             if (op->do_pcb)
3436                 js_pcb(jsp, jo3p, bp[2]);
3437             sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
3438                               0 == pc ? clgc : idci);
3439         }
3440         if (0 == pc) {
3441             if (pl < 8)  {
3442                 pr2serr("  <<expect parameter 0x%x to be at least 8 bytes "
3443                         "long, got %d, skip>>\n", pc, pl);
3444                 goto skip;
3445             }
3446             sgj_pr_hr(jsp, "  %s [pc=0x0]:\n", clgc);
3447             for (j = 4, k = 1; j < pl; j +=4, ++k) {
3448                 n = sg_get_unaligned_be32(bp + j);
3449                 sgj_pr_hr(jsp, "    %s [0x%x]: %u\n", cgn, k, n);
3450             }
3451             if (jsp->pr_as_json) {
3452                 ja2p = sgj_named_subarray_r(jsp, jo3p,
3453                                             "changed_generation_numbers");
3454                 for (j = 4, k = 1; j < pl; j +=4, ++k) {
3455                     jo4p = sgj_new_unattached_object_r(jsp);
3456                     n = sg_get_unaligned_be32(bp + j);
3457                     js_snakenv_ihexstr_nex(jsp, jo4p, cgn, n, true, NULL,
3458                                            NULL, NULL);
3459                     sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo4p);
3460                 }
3461             }
3462         } else {        /* pc > 0x0 */
3463             int m;
3464             const int nn = sg_lib_names_mode_len;
3465             struct sg_lib_simple_value_name_t * nvp = sg_lib_names_vpd_arr;
3466 
3467             snprintf(b, sizeof(b), "  %s 0x%x, ", param_c, pc);
3468             vpd = !! (1 & *(bp + 4));
3469             vpd_pg = *(bp + 5);
3470             if (vpd) {
3471                 for (m = 0; m < nn; ++m, ++nvp) {
3472                     if (nvp->value == vpd_pg)
3473                         break;
3474                 }
3475                 vpd_pg_name = (m < nn) ? nvp->name : NULL;
3476             } else
3477                 vpd_pg_name = "Standard INQUIRY";
3478 
3479             if (jsp->pr_as_json) {
3480                 sgj_js_nv_i(jsp, jo3p, "vpd", (int)vpd);
3481                 sgj_js_nv_ihex(jsp, jo3p, "changed_page_code", vpd_pg);
3482                 if (vpd_pg_name)
3483                     sgj_js_nv_s(jsp, jo3p, "changed_page_name", vpd_pg_name);
3484             }
3485             if (vpd) {
3486                 sgj_pr_hr(jsp, "%sVPD page 0x%x changed\n", b, vpd_pg);
3487                 if (0 == op->do_brief) {
3488                     if (vpd_pg_name)
3489                         sgj_pr_hr(jsp, "    name: %s\n", vpd_pg_name);
3490                 }
3491             } else
3492                 sgj_pr_hr(jsp, "%sStandard INQUIRY data changed\n", b);
3493         }
3494         if (op->do_pcb)
3495             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
3496                       str, sizeof(str)));
3497 skip:
3498         if (jsp->pr_as_json)
3499             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
3500         if (op->filter_given)
3501             break;
3502         num -= pl;
3503         bp += pl;
3504     }
3505     return true;
3506 }
3507 
3508 /* LAST_N_MODE_PG_DATA_CH_SUBPG [0xb,0x2] <lnmc> introduced: SPC-5 (rev 17) */
3509 static bool
show_last_n_mode_pg_data_ch_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3510 show_last_n_mode_pg_data_ch_page(const uint8_t * resp, int len,
3511                                  struct opts_t * op, sgj_opaque_p jop)
3512 {
3513     bool spf;
3514     int j, k, num, pl, pg_code, spg_code;
3515     uint32_t n;
3516     const uint8_t * bp;
3517     const char * mode_pg_name = NULL;
3518     sgj_state * jsp = &op->json_st;
3519     sgj_opaque_p jo2p, jo4p;
3520     sgj_opaque_p jo3p = NULL;
3521     sgj_opaque_p jap = NULL;
3522     sgj_opaque_p ja2p;
3523     char str[PCB_STR_LEN];
3524     char b[128];
3525     static const char * lnmpdclp = "Last n mode page data changed log page";
3526     static const char * mpdci = "Mode page data changed indicator";
3527 
3528     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
3529         sgj_pr_hr(jsp, "%s  [0xb,0x2]\n", lnmpdclp);
3530     num = len - 4;
3531     bp = &resp[0] + 4;
3532     if (jsp->pr_as_json) {
3533         jo2p = sg_log_js_hdr(jsp, jop, lnmpdclp, resp);
3534         jap = sgj_named_subarray_r(jsp, jo2p,
3535                                    "mode_page_data_changed_log_parameters");
3536     }
3537 
3538     while (num > 3) {
3539         int pc = sg_get_unaligned_be16(bp + 0);
3540 
3541         pl = bp[3] + 4;
3542         if (op->filter_given) {
3543             if (pc != op->filter)
3544                 goto skip;
3545         }
3546         if (op->do_raw) {
3547             dStrRaw(bp, pl);
3548             break;
3549         } else if (op->do_hex) {
3550             hex2stdout(bp, pl, op->dstrhex_no_ascii);
3551             break;
3552         }
3553         if (jsp->pr_as_json) {
3554             jo3p = sgj_new_unattached_object_r(jsp);
3555             if (op->do_pcb)
3556                 js_pcb(jsp, jo3p, bp[2]);
3557             sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
3558                               0 == pc ? clgc : mpdci);
3559         }
3560         if (0 == pc) {  /* Same as LAST_N_INQUIRY_DATA_CH_SUBPG [0xb,0x1] */
3561             if (pl < 8)  {
3562                 pr2serr("  <<expect parameter 0x%x to be at least 8 bytes "
3563                         "long, got %d, skip>>\n", pc, pl);
3564                 goto skip;
3565             }
3566             sgj_pr_hr(jsp, "  %s [pc=0x0]:\n", clgc);
3567             for (j = 4, k = 1; j < pl; j +=4, ++k) {
3568                 n = sg_get_unaligned_be32(bp + j);
3569                 sgj_pr_hr(jsp, "    %s [0x%x]: %u\n", cgn, k, n);
3570             }
3571             if (jsp->pr_as_json) {
3572                 ja2p = sgj_named_subarray_r(jsp, jo3p,
3573                                             "changed_generation_numbers");
3574                 for (j = 4, k = 1; j < pl; j +=4, ++k) {
3575                     jo4p = sgj_new_unattached_object_r(jsp);
3576                     n = sg_get_unaligned_be32(bp + j);
3577                     js_snakenv_ihexstr_nex(jsp, jo4p, cgn, n, true, NULL,
3578                                            NULL, NULL);
3579                     sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo4p);
3580                 }
3581             }
3582         } else {        /* pc > 0x0 */
3583             int k, val;
3584             const int nn = sg_lib_names_mode_len;
3585             struct sg_lib_simple_value_name_t * nmp = sg_lib_names_mode_arr;
3586 
3587             snprintf(b, sizeof(b), "  %s 0x%x, ", param_c, pc);
3588             spf = !! (0x40 & *(bp + 4));
3589             pg_code = 0x3f & *(bp + 4);
3590             spg_code = *(bp + 5);
3591             if (spf)       /* SPF bit set */
3592                 sgj_pr_hr(jsp, "%smode page 0x%x,0%x changed\n", b, pg_code,
3593                           spg_code);
3594             else
3595                 sgj_pr_hr(jsp, "%smode page 0x%x changed\n", b, pg_code);
3596 
3597             val = (pg_code << 8) | spg_code;
3598             for (k = 0; k < nn; ++k, ++nmp) {
3599                 if (nmp->value == val)
3600                     break;
3601             }
3602             mode_pg_name = (k < nn) ? nmp->name : NULL;
3603             if ((0 == op->do_brief) && mode_pg_name)
3604                 sgj_pr_hr(jsp, "    name: %s\n", nmp->name);
3605             if (jsp->pr_as_json) {
3606                 sgj_js_nv_i(jsp, jo3p, "spf", (int)spf);
3607                 sgj_js_nv_ihex(jsp, jo3p, "mode_page_code", pg_code);
3608                 sgj_js_nv_ihex(jsp, jo3p, "subpage_code", spg_code);
3609                 if (mode_pg_name)
3610                     sgj_js_nv_s(jsp, jo3p, "mode_page_name", mode_pg_name);
3611             }
3612         }
3613         if (op->do_pcb)
3614             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
3615                       str, sizeof(str)));
3616 skip:
3617         if (jsp->pr_as_json)
3618             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
3619         if (op->filter_given)
3620             break;
3621         num -= pl;
3622         bp += pl;
3623     }
3624     return true;
3625 }
3626 
3627 static const char * self_test_code[] = {
3628     "default", "background short", "background extended", rsv_s,
3629     "aborted background", "foreground short", "foreground extended",
3630     rsv_s};
3631 
3632 static const char * self_test_result[] = {
3633     "completed without error",
3634     "aborted by SEND DIAGNOSTIC",
3635     "aborted other than by SEND DIAGNOSTIC",
3636     "unknown error, unable to complete",
3637     "self test completed with failure in test segment (which one unknown)",
3638     "first segment in self test failed",
3639     "second segment in self test failed",
3640     "another segment in self test failed",
3641     rsv_s, rsv_s, rsv_s, rsv_s, rsv_s, rsv_s,
3642     rsv_s,
3643     "self test in progress"};
3644 
3645 /* SELF_TEST_LPAGE [0x10] <str>  introduced: SPC-3 */
3646 static bool
show_self_test_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3647 show_self_test_page(const uint8_t * resp, int len, struct opts_t * op,
3648                     sgj_opaque_p jop)
3649 {
3650     bool addr_all_ffs;
3651     int k, num, res, st_c;
3652     unsigned int v;
3653     uint32_t n;
3654     uint64_t ull;
3655     const uint8_t * bp;
3656     sgj_state * jsp = &op->json_st;
3657     sgj_opaque_p jo2p;
3658     sgj_opaque_p jo3p = NULL;
3659     sgj_opaque_p jap = NULL;
3660     char str[PCB_STR_LEN];
3661     char b[80];
3662     static const char * strlp = "Self-test results log page";
3663     static const char * stc_s = "Self-test code";
3664     static const char * str_s = "Self-test result";
3665     static const char * stn_s = "Self-test number";
3666     static const char * apoh = "Accumulated power on hours";
3667 
3668     num = len - 4;
3669     if (num < 0x190) {
3670         pr2serr("short %s [length 0x%x rather than 0x190 bytes]\n", strlp,
3671                  num);
3672         return false;
3673     }
3674     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
3675         sgj_pr_hr(jsp, "%s  [0x10]\n", strlp);
3676     if (jsp->pr_as_json) {
3677         jo2p = sg_log_js_hdr(jsp, jop, strlp, resp);
3678         jap = sgj_named_subarray_r(jsp, jo2p,
3679                                    "self_test_results_log_parameters");
3680     }
3681 
3682     for (k = 0, bp = resp + 4; k < 20; ++k, bp += 20 ) {
3683         int pc = sg_get_unaligned_be16(bp + 0);
3684         int pl = bp[3] + 4;
3685 
3686         if (op->filter_given) {
3687             if (pc != op->filter)
3688                 continue;
3689         }
3690         if (op->do_raw) {
3691             dStrRaw(bp, pl);
3692             break;
3693         } else if (op->do_hex) {
3694             hex2stdout(bp, pl, op->dstrhex_no_ascii);
3695             break;
3696         }
3697         if (jsp->pr_as_json) {
3698             jo3p = sgj_new_unattached_object_r(jsp);
3699             if (op->do_pcb)
3700                 js_pcb(jsp, jo3p, bp[2]);
3701             sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
3702                               "Self-test results");
3703         }
3704         n = sg_get_unaligned_be16(bp + 6);
3705         if ((0 == n) && (0 == bp[4])) {
3706             if (jsp->pr_as_json)
3707                 sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
3708             break;
3709         }
3710         sgj_pr_hr(jsp, "  %s = %d, accumulated power-on hours = %d\n",
3711                   param_c, pc, n);
3712         st_c = (bp[4] >> 5) & 0x7;
3713         sgj_pr_hr(jsp, "    %s: %s [%d]\n", stc_s, self_test_code[st_c],
3714                   st_c);
3715         res = bp[4] & 0xf;
3716         sgj_pr_hr(jsp, "    %s: %s [%d]\n", str_s, self_test_result[res],
3717                   res);
3718         if (bp[5])
3719             sgj_pr_hr(jsp, "    %s = %d\n", stn_s, (int)bp[5]);
3720         ull = sg_get_unaligned_be64(bp + 8);
3721 
3722         addr_all_ffs = sg_all_ffs(bp + 8, 8);
3723         if (! addr_all_ffs) {
3724             addr_all_ffs = false;
3725             if ((res > 0) && ( res < 0xf))
3726                 sgj_pr_hr(jsp, "    address of first error = 0x%" PRIx64 "\n",
3727                           ull);
3728         }
3729             addr_all_ffs = false;
3730         v = bp[16] & 0xf;
3731         if (v) {
3732             if (op->do_brief)
3733                 sgj_pr_hr(jsp, "    %s = 0x%x , asc = 0x%x, ascq = 0x%x\n",
3734                           s_key, v, bp[17], bp[18]);
3735             else {
3736                 sgj_pr_hr(jsp, "    %s = 0x%x [%s]\n", s_key, v,
3737                           sg_get_sense_key_str(v, sizeof(b), b));
3738 
3739                 sgj_pr_hr(jsp, "      asc = 0x%x, ascq = 0x%x [%s]\n",
3740                           bp[17], bp[18], sg_get_asc_ascq_str(bp[17], bp[18],
3741                           sizeof(b), b));
3742             }
3743         }
3744         if (op->do_pcb)
3745             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2],
3746                       str, sizeof(str)));
3747         if (jsp->pr_as_json) {
3748             js_snakenv_ihexstr_nex(jsp, jo3p, stc_s, st_c, true, NULL,
3749                                    self_test_code[st_c], NULL);
3750             js_snakenv_ihexstr_nex(jsp, jo3p, str_s, res, true, NULL,
3751                                     self_test_result[res], NULL);
3752             js_snakenv_ihexstr_nex(jsp, jo3p, stn_s, bp[5], false, NULL,
3753                                    NULL, "segment number that failed");
3754             js_snakenv_ihexstr_nex(jsp, jo3p, apoh, n, true, NULL,
3755                    (0xffff == n ? "65535 hours or more" : NULL), NULL);
3756             sgj_js_nv_ihexstr(jsp, jo3p, "address_of_first_failure", pc, NULL,
3757                               addr_all_ffs ? "no errors detected" : NULL);
3758             sgj_js_nv_ihexstr(jsp, jo3p, "sense_key", v, NULL,
3759                               sg_get_sense_key_str(v, sizeof(b), b));
3760             sgj_js_nv_ihexstr(jsp, jo3p, "additional_sense_code", bp[17],
3761                               NULL, NULL);
3762             sgj_js_nv_ihexstr(jsp, jo3p, "additional_sense_code_qualifier",
3763                               bp[18], NULL, sg_get_asc_ascq_str(bp[17],
3764                               bp[18], sizeof(b), b));
3765             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
3766         }
3767         if (op->filter_given)
3768             break;
3769     }
3770     return true;
3771 }
3772 
3773 /* TEMPERATURE_LPAGE [0xd] <temp>  introduced: SPC-3
3774  * N.B. The ENV_REPORTING_SUBPG [0xd,0x1] and the ENV_LIMITS_SUBPG [0xd,0x2]
3775  * (both added SPC-5) are a superset of this page. */
3776 static bool
show_temperature_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3777 show_temperature_page(const uint8_t * resp, int len, struct opts_t * op,
3778                       sgj_opaque_p jop)
3779 {
3780     int k, num, extra;
3781     const uint8_t * bp;
3782     sgj_state * jsp = &op->json_st;
3783     sgj_opaque_p jo2p;
3784     sgj_opaque_p jo3p = NULL;
3785     sgj_opaque_p jap = NULL;
3786     char str[PCB_STR_LEN];
3787     static const char * tlp = "Temperature log page";
3788     static const char * ctemp = "Current temperature";
3789     static const char * rtemp = "Reference temperature";
3790 
3791     num = len - 4;
3792     bp = &resp[0] + 4;
3793     if (num < 4) {
3794         pr2serr("badly formed Temperature page\n");
3795         return false;
3796     }
3797     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
3798         if (! op->do_temperature)
3799             sgj_pr_hr(jsp, "%s  [0xd]\n", tlp);
3800     }
3801     if (jsp->pr_as_json) {
3802         jo2p = sg_log_js_hdr(jsp, jop, tlp, resp);
3803         jap = sgj_named_subarray_r(jsp, jo2p, "temperature_log_parameters");
3804     }
3805 
3806     for (k = num; k > 0; k -= extra, bp += extra) {
3807         int pc;
3808 
3809         if (k < 3) {
3810             pr2serr("short Temperature page\n");
3811             return true;
3812         }
3813         extra = bp[3] + 4;
3814         pc = sg_get_unaligned_be16(bp + 0);
3815         if (op->filter_given) {
3816             if (pc != op->filter)
3817                 continue;
3818         }
3819         if (op->do_raw) {
3820             dStrRaw(bp, extra);
3821             goto skip;
3822         } else if (op->do_hex) {
3823             hex2stdout(bp, extra, op->dstrhex_no_ascii);
3824             goto skip;
3825         }
3826         if (jsp->pr_as_json) {
3827             jo3p = sgj_new_unattached_object_r(jsp);
3828             if (op->do_pcb)
3829                 js_pcb(jsp, jo3p, bp[2]);
3830             sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc);
3831         }
3832 
3833         switch (pc) {
3834         case 0:
3835             if ((extra > 5) && (k > 5)) {
3836                 if (0 == bp[5])
3837                     sgj_pr_hr(jsp, "  %s = 0 C (or less)\n", ctemp);
3838                 else if (bp[5] < 0xff)
3839                     sgj_pr_hr(jsp, "  %s = %d C\n", ctemp, bp[5]);
3840                 else
3841                     sgj_pr_hr(jsp, "  %s = <%s>\n", ctemp, not_avail);
3842                 if (jsp->pr_as_json) {
3843                     const char * cp = NULL;
3844 
3845                     if (0 == bp[5])
3846                         cp = "0 or less Celsius";
3847                     else if (0xff == bp[5])
3848                         cp = "temperature not available";
3849                     js_snakenv_ihexstr_nex(jsp, jo3p, "temperature", bp[5],
3850                                            false, NULL, cp,
3851                                            "current [unit: celsius]");
3852                 }
3853             }
3854             break;
3855         case 1:
3856             if ((extra > 5) && (k > 5)) {
3857                 if (bp[5] < 0xff)
3858                     sgj_pr_hr(jsp, "  %s = %d C\n", rtemp, bp[5]);
3859                 else
3860                     sgj_pr_hr(jsp, "  %s = <%s>\n", rtemp, not_avail);
3861                 if (jsp->pr_as_json) {
3862                     const char * cp;
3863 
3864                     if (0 == bp[5])
3865                         cp = "in C (or less)";
3866                     else if (0xff == bp[5])
3867                         cp = not_avail;
3868                     else
3869                         cp = "in C";
3870                     sgj_js_nv_ihex_nex(jsp, jo3p, "reference_temperature",
3871                                        bp[5], true, cp);
3872                 }
3873             }
3874             break;
3875         default:
3876             if (! op->do_temperature) {
3877                 sgj_pr_hr(jsp, "  unknown %s = 0x%x, contents in hex:\n",
3878                           param_c, pc);
3879                 hex2stdout(bp, extra, op->dstrhex_no_ascii);
3880             } else {
3881                 if (jsp->pr_as_json)
3882                     sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
3883                 continue;
3884             }
3885             break;
3886         }
3887         if (jsp->pr_as_json)
3888             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
3889         if (op->do_pcb)
3890             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
3891                       sizeof(str)));
3892 skip:
3893         if (op->filter_given)
3894             break;
3895     }
3896     return true;
3897 }
3898 
3899 /* START_STOP_LPAGE [0xe] <sscc>  introduced: SPC-3 */
3900 static bool
show_start_stop_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)3901 show_start_stop_page(const uint8_t * resp, int len, struct opts_t * op,
3902                      sgj_opaque_p jop)
3903 {
3904     int k, num, extra;
3905     uint32_t val;
3906     const uint8_t * bp;
3907     sgj_state * jsp = &op->json_st;
3908     sgj_opaque_p jo2p;
3909     sgj_opaque_p jo3p = NULL;
3910     sgj_opaque_p jap = NULL;
3911     char str[PCB_STR_LEN];
3912     char b[256];
3913     static const char * sscclp = "Start-stop cycle counter log page";
3914     static const char * dom = "Date of manufacture";
3915     static const char * ad = "Accounting date";
3916     static const char * sccodl = "Specified cycle count over device lifetime";
3917     static const char * assc = "Accumulated start-stop cycles";
3918     static const char * slucodl =
3919                         "Specified load-unload count over device lifetime";
3920     static const char * aluc = "Accumulated load-unload cycles";
3921 
3922     num = len - 4;
3923     bp = &resp[0] + 4;
3924     if (num < 4) {
3925         pr2serr("badly formed Start-stop cycle counter page\n");
3926         return false;
3927     }
3928     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
3929         sgj_pr_hr(jsp, "%s  [0xe]\n", sscclp);
3930     if (jsp->pr_as_json) {
3931         jo2p = sg_log_js_hdr(jsp, jop, sscclp, resp);
3932         jap = sgj_named_subarray_r(jsp, jo2p,
3933                                    "start_stop_cycle_log_parameters");
3934     }
3935 
3936     for (k = num; k > 0; k -= extra, bp += extra) {
3937         int pc;
3938 
3939         if (k < 3) {
3940             pr2serr("short %s\n", sscclp);
3941             return false;
3942         }
3943         extra = bp[3] + 4;
3944         pc = sg_get_unaligned_be16(bp + 0);
3945         if (op->filter_given) {
3946             if (pc != op->filter)
3947                 continue;
3948         }
3949         if (op->do_raw) {
3950             dStrRaw(bp, extra);
3951             goto skip;
3952         } else if (op->do_hex) {
3953             hex2stdout(bp, extra, op->dstrhex_no_ascii);
3954             goto skip;
3955         }
3956         if (jsp->pr_as_json) {
3957             jo3p = sgj_new_unattached_object_r(jsp);
3958             if (op->do_pcb)
3959                 js_pcb(jsp, jo3p, bp[2]);
3960         }
3961 
3962         switch (pc) {
3963         case 1:
3964             if (10 == extra) {
3965                  sgj_pr_hr(jsp, "  %s, year: %.4s, week: %.2s\n", dom,
3966                        bp + 4, bp + 8);
3967                 if (jsp->pr_as_json) {
3968                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
3969                                       "Date of manufacture");
3970                     sgj_js_nv_s_len(jsp, jo3p, "year_of_manufacture",
3971                                     (const char *)(bp + 4), 4);
3972                     sgj_js_nv_s_len(jsp, jo3p, "week_of_manufacture",
3973                                     (const char *)(bp + 8), 2);
3974                 }
3975             } else if (op->verbose) {
3976                 pr2serr("%s parameter length strange: %d\n", dom, extra - 4);
3977                 hex2stderr(bp, extra, 1);
3978             }
3979             break;
3980         case 2:
3981             if (10 == extra) {
3982                 sgj_pr_hr(jsp, "  %s, year: %.4s, week: %.2s\n", ad, bp + 4,
3983                           bp + 8);
3984                 if (jsp->pr_as_json) {
3985                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
3986                                       "Accounting date");
3987                     sgj_js_nv_s_len(jsp, jo3p, "year_of_manufacture",
3988                                     (const char *)(bp + 4), 4);
3989                     sgj_js_nv_s_len(jsp, jo3p, "week_of_manufacture",
3990                                     (const char *)(bp + 8), 2);
3991                 }
3992             } else if (op->verbose) {
3993                 pr2serr("%s parameter length strange: %d\n", ad, extra - 4);
3994                 hex2stderr(bp, extra, 1);
3995             }
3996             break;
3997         case 3:
3998             if (extra > 7) {
3999                 val = sg_get_unaligned_be32(bp + 4);
4000                 sgj_pr_hr(jsp, "  %s = %u\n", sccodl, val);
4001                 if (jsp->pr_as_json) {
4002                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
4003                                       sccodl);
4004                     js_snakenv_ihexstr_nex(jsp, jo3p, sccodl, val, false,
4005                                            NULL, NULL, NULL);
4006                 }
4007             }
4008             break;
4009         case 4:
4010             if (extra > 7) {
4011                 val = sg_get_unaligned_be32(bp + 4);
4012                 sgj_pr_hr(jsp, "  %s = %u\n", assc, val);
4013                 if (jsp->pr_as_json) {
4014                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
4015                                       assc);
4016                     js_snakenv_ihexstr_nex(jsp, jo3p, assc, val, false,
4017                                            NULL, NULL, NULL);
4018                 }
4019             }
4020             break;
4021         case 5:
4022             if (extra > 7) {
4023                 val = sg_get_unaligned_be32(bp + 4);
4024                 sgj_pr_hr(jsp, "  %s = %u\n", slucodl, val);
4025                 if (jsp->pr_as_json) {
4026                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
4027                                       slucodl);
4028                     js_snakenv_ihexstr_nex(jsp, jo3p, slucodl, val, false,
4029                                            NULL, NULL, NULL);
4030                 }
4031             }
4032             break;
4033         case 6:
4034             if (extra > 7) {
4035                 val = sg_get_unaligned_be32(bp + 4);
4036                 sgj_pr_hr(jsp, "  %s = %u\n", aluc, val);
4037                 if (jsp->pr_as_json) {
4038                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, aluc);
4039                     js_snakenv_ihexstr_nex(jsp, jo3p, aluc, val, false,
4040                                            NULL, NULL, NULL);
4041                 }
4042             }
4043             break;
4044         default:
4045             sgj_pr_hr(jsp, "  unknown %s = 0x%x, contents in hex:\n",
4046                       param_c, pc);
4047             hex2str(bp, extra, "    ", op->hex2str_oformat, sizeof(b), b);
4048             sgj_pr_hr(jsp, "%s\n", b);
4049             if (jsp->pr_as_json) {
4050                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, unknown_s);
4051                 sgj_js_nv_hex_bytes(jsp, jo3p, in_hex, bp, extra);
4052             }
4053             break;
4054         }
4055         if (jsp->pr_as_json)
4056             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
4057         if (op->do_pcb)
4058             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
4059                       sizeof(str)));
4060 skip:
4061         if (op->filter_given)
4062             break;
4063     }
4064     return true;
4065 }
4066 
4067 /* APP_CLIENT_LPAGE [0xf] <ac>  introduced: SPC-3 */
4068 static bool
show_app_client_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)4069 show_app_client_page(const uint8_t * resp, int len, struct opts_t * op,
4070                      sgj_opaque_p jop)
4071 {
4072     int k, n, num, extra;
4073     char * mp;
4074     const uint8_t * bp;
4075     sgj_state * jsp = &op->json_st;
4076     sgj_opaque_p jo2p = NULL;
4077     sgj_opaque_p jo3p = NULL;
4078     sgj_opaque_p jap = NULL;
4079     char str[PCB_STR_LEN];
4080     static const char * aclp = "Application Client log page";
4081     static const char * guac = "General Usage Application Client";
4082 
4083     num = len - 4;
4084     bp = &resp[0] + 4;
4085     if (num < 4) {
4086         pr2serr("badly formed %s\n", aclp);
4087         return false;
4088     }
4089     if (op->verbose || ((! op->do_raw) && (op->do_hex == 0)))
4090         sgj_pr_hr(jsp, "%s  [0xf]\n", aclp);
4091     if (jsp->pr_as_json)
4092         jo2p = sg_log_js_hdr(jsp, jop, aclp, resp);
4093     if ((0 == op->filter_given) && (! op->do_full)) {
4094         if ((len > 128) && (0 == op->do_hex) && (0 == op->undefined_hex)) {
4095             char d[256];
4096 
4097             hex2str(resp, 64, "  ", op->hex2str_oformat, sizeof(d), d);
4098             sgj_pr_hr(jsp, "%s", d);
4099             sgj_pr_hr(jsp, "  .....  [truncated after 64 of %d bytes (use "
4100                       "'-H' to see the rest)]\n", len);
4101             if (jsp->pr_as_json) {
4102                 sgj_js_nv_ihex(jsp, jo2p, "actual_length", len);
4103                 sgj_js_nv_ihex(jsp, jo2p, "truncated_length", 64);
4104                 sgj_js_nv_hex_bytes(jsp, jo2p, in_hex, resp, 64);
4105             }
4106         } else {
4107             n = len * 4 + 32;
4108             mp = malloc(n);
4109             if (mp) {
4110                 hex2str(resp, len, "  ", op->hex2str_oformat, n, mp);
4111                 sgj_pr_hr(jsp, "%s", mp);
4112                 if (jsp->pr_as_json) {
4113                     sgj_js_nv_ihex(jsp, jo2p, "length", len);
4114                     sgj_js_nv_hex_bytes(jsp, jo2p, in_hex, resp, len);
4115                 }
4116                 free(mp);
4117             }
4118         }
4119         return true;
4120     }
4121     if (jsp->pr_as_json)
4122         jap = sgj_named_subarray_r(jsp, jo2p,
4123                                    "application_client_log_parameters");
4124 
4125     /* here if filter_given set or --full given */
4126     for (k = num; k > 0; k -= extra, bp += extra) {
4127         int pc;
4128         char d[1024];
4129 
4130         if (k < 3) {
4131             pr2serr("short %s\n", aclp);
4132             return true;
4133         }
4134         extra = bp[3] + 4;
4135         pc = sg_get_unaligned_be16(bp + 0);
4136         if (op->filter_given) {
4137             if (pc != op->filter)
4138                 continue;
4139         }
4140         if (op->do_raw) {
4141             dStrRaw(bp, extra);
4142             break;
4143         }
4144         if (jsp->pr_as_json) {
4145             jo3p = sgj_new_unattached_object_r(jsp);
4146             if (op->do_pcb)
4147                 js_pcb(jsp, jo3p, bp[2]);
4148         }
4149         sgj_pr_hr(jsp, "  %s = %d [0x%x] %s\n", param_c, pc, pc,
4150                   (pc <= 0xfff) ? guac : "");
4151         hex2str(bp, extra, "    ", op->hex2str_oformat, sizeof(d), d);
4152         sgj_pr_hr(jsp, "%s", d);
4153         if (jsp->pr_as_json) {
4154             sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
4155                               (pc <= 0xfff) ? guac : NULL);
4156             sgj_js_nv_hex_bytes(jsp, jo3p, in_hex, bp, extra);
4157             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
4158         }
4159         if (op->do_pcb)
4160             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
4161                       sizeof(str)));
4162         if (op->filter_given)
4163             break;
4164     }
4165     return true;
4166 }
4167 
4168 /* IE_LPAGE [0x2f] <ie> "Informational Exceptions"  introduced: SPC-3
4169  * Previously known as "SMART Status and Temperature Reading" lpage.  */
4170 static bool
show_ie_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)4171 show_ie_page(const uint8_t * resp, int len, struct opts_t * op,
4172              sgj_opaque_p jop)
4173 {
4174     bool skip = false;
4175     int k, num, param_len;
4176     const uint8_t * bp;
4177     const char * cp;
4178     sgj_state * jsp = &op->json_st;
4179     sgj_opaque_p jo2p;
4180     sgj_opaque_p jo3p = NULL;
4181     sgj_opaque_p jap = NULL;
4182     char str[PCB_STR_LEN];
4183     char b[512];
4184     char bb[64];
4185     bool full, decoded;
4186     static const char * ielp = "Informational exceptions log page";
4187     static const char * ieasc =
4188                          "informational_exceptions_additional_sense_code";
4189     static const char * ct = "Current temperature";
4190     static const char * tt = "Threshold temperature";
4191     static const char * mt = "Maximum temperature";
4192     static const char * ce = "common extension";
4193 
4194     full = ! op->do_temperature;
4195     num = len - 4;
4196     bp = &resp[0] + 4;
4197     if (num < 4) {
4198         pr2serr("badly formed %s\n", ielp);
4199         return false;
4200     }
4201     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
4202         if (full)
4203             sgj_pr_hr(jsp, "%s  [0x2f]\n", ielp);
4204     }
4205     if (jsp->pr_as_json) {
4206         jo2p = sg_log_js_hdr(jsp, jop, ielp, resp);
4207         jap = sgj_named_subarray_r(jsp, jo2p,
4208                            "informational_exceptions_log_parameters");
4209     }
4210 
4211     for (k = num; k > 0; k -= param_len, bp += param_len) {
4212         int pc;
4213 
4214         if (k < 3) {
4215             pr2serr("short %s\n", ielp);
4216             return false;
4217         }
4218         param_len = bp[3] + 4;
4219         pc = sg_get_unaligned_be16(bp + 0);
4220         if (op->filter_given) {
4221             if (pc != op->filter)
4222                 continue;
4223         }
4224         if (op->do_raw) {
4225             dStrRaw(bp, param_len);
4226             goto skip;
4227         } else if (op->do_hex) {
4228             hex2stdout(bp, param_len, op->dstrhex_no_ascii);
4229             goto skip;
4230         }
4231         if (jsp->pr_as_json) {
4232             jo3p = sgj_new_unattached_object_r(jsp);
4233             if (op->do_pcb)
4234                 js_pcb(jsp, jo3p, bp[2]);
4235         }
4236         decoded = true;
4237         cp = NULL;
4238 
4239         switch (pc) {
4240         case 0x0:
4241             if (param_len > 5) {
4242                 bool na;
4243                 uint8_t t;
4244 
4245                 if (full) {
4246                      sgj_pr_hr(jsp, "  IE asc = 0x%x, ascq = 0x%x\n", bp[4],
4247                                bp[5]);
4248                     if (bp[4] || bp[5])
4249                         if(sg_get_asc_ascq_str(bp[4], bp[5], sizeof(b), b))
4250                              sgj_pr_hr(jsp, "    [%s]\n", b);
4251                     if (jsp->pr_as_json) {
4252                         sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
4253                                           "Informational exceptions general");
4254                         sgj_js_nv_ihexstr(jsp, jo3p, ieasc, bp[4], NULL,
4255                                           NULL);
4256                         snprintf(b, sizeof(b), "%s_qualifier", ieasc);
4257                         sgj_js_nv_ihexstr(jsp, jo3p, b, bp[5], NULL,
4258                                           sg_get_asc_ascq_str(bp[4], bp[5],
4259                                           sizeof(bb), bb));
4260                     }
4261                 }
4262                 if (param_len <= 6)
4263                     break;
4264                 t = bp[6];
4265                 na = (0xff == t);
4266                 if (na)
4267                     snprintf(b, sizeof(b), "%u C", t);
4268                 else
4269                     snprintf(b, sizeof(b), "<%s>", unknown_s);
4270                 sgj_pr_hr(jsp, "    %s = %s\n", ct, b);
4271                 if (jsp->pr_as_json)
4272                     js_snakenv_ihexstr_nex(jsp, jo3p, ct, t, true,
4273                                            NULL, na ? unknown_s : NULL,
4274                                            "[unit: celsius]");
4275                 if (param_len > 7) {
4276                     t = bp[7];
4277                     na = (0xff == t);
4278                     if (na)
4279                         snprintf(b, sizeof(b), "%u C", t);
4280                     else
4281                         snprintf(b, sizeof(b), "<%s>", unknown_s);
4282                     sgj_pr_hr(jsp, "    %s = %s  [%s]\n", tt, b, ce);
4283                     if (jsp->pr_as_json)
4284                         js_snakenv_ihexstr_nex(jsp, jo3p, tt, t, true, NULL,
4285                                                na ? unknown_s : NULL, ce);
4286                     t = bp[8];
4287                     if ((param_len > 8) && (t >= bp[6])) {
4288                         na = (0xff == t);
4289                         if (na)
4290                             snprintf(b, sizeof(b), "%u C", t);
4291                         else
4292                             snprintf(b, sizeof(b), "<%s>", unknown_s);
4293                         sgj_pr_hr(jsp, "    %s = %s  [%s]\n", mt, b, ce);
4294                         if (jsp->pr_as_json)
4295                             js_snakenv_ihexstr_nex(jsp, jo3p, mt, t, true,
4296                                                    NULL,
4297                                                    na ? unknown_s : NULL, ce);
4298                     }
4299                 }
4300             }
4301             decoded = true;
4302             break;
4303         default:
4304             if (op->do_brief > 0) {
4305                 cp = NULL;
4306                 skip = true;
4307                 break;
4308             }
4309             if (VP_HITA == op->vend_prod_num) {
4310                 switch (pc) {
4311                 case 0x1:
4312                     cp = "Remaining reserve 1";
4313                     break;
4314                 case 0x2:
4315                     cp = "Remaining reserve XOR";
4316                     break;
4317                 case 0x3:
4318                     cp = "XOR depletion";
4319                     break;
4320                 case 0x4:
4321                     cp = "Volatile memory backup failure";
4322                     break;
4323                 case 0x5:
4324                     cp = "Wear indicator";
4325                     break;
4326                 case 0x6:
4327                     cp = "System area wear indicator";
4328                     break;
4329                 case 0x7:
4330                     cp = "Channel hangs";
4331                     break;
4332                 case 0x8:
4333                     cp = "Flash scan failure";
4334                     break;
4335                 default:
4336                     decoded = false;
4337                     break;
4338                 }
4339                 if (cp) {
4340                     sgj_pr_hr(jsp, "  %s:\n", cp);
4341                     sgj_pr_hr(jsp, "    SMART sense_code=0x%x sense_qualifier"
4342                               "=0x%x threshold=%d%% trip=%d\n", bp[4], bp[5],
4343                               bp[6], bp[7]);
4344                     if (jsp->pr_as_json) {
4345                         sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
4346                                           cp);
4347                         sgj_js_nv_ihex(jsp, jo3p, "smart_sense_code", bp[4]);
4348                         sgj_js_nv_ihex(jsp, jo3p, "smart_sense_qualifier",
4349                                        bp[5]);
4350                         sgj_js_nv_ihex(jsp, jo3p, "smart_threshold", bp[6]);
4351                         sgj_js_nv_ihex(jsp, jo3p, "smart_trip", bp[7]);
4352                     }
4353                 }
4354             } else {
4355                 decoded = false;
4356                 if (jsp->pr_as_json)
4357                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
4358                                       unknown_s);
4359             }
4360             break;
4361         }               /* end of switch statement */
4362         if (skip)
4363             skip = false;
4364         else if ((! decoded) && full) {
4365             hex2str(bp, param_len, "    ", op->hex2str_oformat, sizeof(b), b);
4366             sgj_pr_hr(jsp, "  %s = 0x%x, contents in hex:\n%s", param_c, pc,
4367                       b);
4368         }
4369 
4370         if (jsp->pr_as_json)
4371             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
4372         if (op->do_pcb)
4373             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
4374                       sizeof(str)));
4375 skip:
4376         if (op->filter_given)
4377             break;
4378     }           /* end of for loop */
4379     return true;
4380 }
4381 
4382 /* called for SAS port of PROTO_SPECIFIC_LPAGE [0x18] */
4383 static const char *
show_sas_phy_event_info(int pes,unsigned int val,unsigned int thresh_val,char * b,int blen)4384 show_sas_phy_event_info(int pes, unsigned int val, unsigned int thresh_val,
4385                         char * b, int blen)
4386 {
4387     int n = 0;
4388     unsigned int u;
4389     const char * cp = "";
4390     static const char * pvdt = "Peak value detector threshold";
4391 
4392     switch (pes) {
4393     case 0:
4394         cp = "No event";
4395         snprintf(b, blen, "%s", cp);
4396         break;
4397     case 0x1:
4398         cp = "Invalid word count";
4399         snprintf(b, blen, "%s: %u", cp, val);
4400         break;
4401     case 0x2:
4402         cp = "Running disparity error count";
4403         snprintf(b, blen, "%s: %u", cp, val);
4404         break;
4405     case 0x3:
4406         cp = "Loss of dword synchronization count";
4407         snprintf(b, blen, "%s: %u", cp, val);
4408         break;
4409     case 0x4:
4410         cp = "Phy reset problem count";
4411         snprintf(b, blen, "%s: %u", cp, val);
4412         break;
4413     case 0x5:
4414         cp = "Elasticity buffer overflow count";
4415         snprintf(b, blen, "%s: %u", cp, val);
4416         break;
4417     case 0x6:
4418         cp = "Received ERROR count";
4419         snprintf(b, blen, "%s: %u", cp, val);
4420         break;
4421     case 0x7:
4422         cp = "Invalid SPL packet count";
4423         snprintf(b, blen, "%s: %u", cp, val);
4424         break;
4425     case 0x8:
4426         cp = "Loss of SPL packet synchronization count";
4427         snprintf(b, blen, "%s: %u", cp, val);
4428         break;
4429     case 0x20:
4430         cp = "Received address frame error count";
4431         snprintf(b, blen, "%s: %u", cp, val);
4432         break;
4433     case 0x21:
4434         cp = "Transmitted abandon-class OPEN_REJECT count";
4435         snprintf(b, blen, "%s: %u", cp, val);
4436         break;
4437     case 0x22:
4438         cp =  "Received abandon-class OPEN_REJECT count";
4439         snprintf(b, blen, "%s: %u", cp, val);
4440         break;
4441     case 0x23:
4442         cp = "Transmitted retry-class OPEN_REJECT count";
4443         snprintf(b, blen, "%s: %u", cp, val);
4444         break;
4445     case 0x24:
4446         cp = "Received retry-class OPEN_REJECT count";
4447         snprintf(b, blen, "%s: %u", cp, val);
4448         break;
4449     case 0x25:
4450         cp = "Received AIP (WAITING ON PARTIAL) count";
4451         snprintf(b, blen, "%s: %u", cp, val);
4452         break;
4453     case 0x26:
4454         cp = "Received AIP (WAITING ON CONNECTION) count";
4455         snprintf(b, blen, "%s: %u", cp, val);
4456         break;
4457     case 0x27:
4458         cp = "Transmitted BREAK count";
4459         snprintf(b, blen, "%s: %u", cp, val);
4460         break;
4461     case 0x28:
4462         cp = "Received BREAK count";
4463         snprintf(b, blen, "%s: %u", cp, val);
4464         break;
4465     case 0x29:
4466         cp = "Break timeout count";
4467         snprintf(b, blen, "%s: %u", cp, val);
4468         break;
4469     case 0x2a:
4470         cp =  "Connection count";
4471         snprintf(b, blen, "%s: %u", cp, val);
4472         break;
4473     case 0x2b:
4474         cp = "Peak transmitted pathway blocked count";
4475         n = sg_scnpr(b, blen, "%s: %u", cp, val & 0xff);
4476         sg_scnpr(b + n, blen - n, "\t%s: %u", pvdt, thresh_val & 0xff);
4477         break;
4478     case 0x2c:
4479         cp = "Peak transmitted arbitration wait time";
4480         u = val & 0xffff;
4481         if (u < 0x8000)
4482             n = sg_scnpr(b, blen, "%s (us): %u", cp, u);
4483         else
4484             n = sg_scnpr(b, blen, "%s (ms): %u", cp, 33 + (u - 0x8000));
4485         u = thresh_val & 0xffff;
4486         if (u < 0x8000)
4487             sg_scnpr(b + n, blen - n, "\t%s (us): %u", pvdt, u);
4488         else
4489             sg_scnpr(b + n, blen - n, "\t%s (ms): %u", pvdt,
4490                      33 + (u - 0x8000));
4491         break;
4492     case 0x2d:
4493         cp = "Peak arbitration time";
4494         n = sg_scnpr(b, blen, "%s (us): %u", cp, val);
4495         sg_scnpr(b + n, blen - n, "\t%s: %u", pvdt, thresh_val);
4496         break;
4497     case 0x2e:
4498         cp = "Peak connection time";
4499         n = sg_scnpr(b, blen, "%s (us): %u", cp, val);
4500         sg_scnpr(b + n, blen - n, "\t%s: %u", pvdt, thresh_val);
4501         break;
4502     case 0x2f:
4503         cp = "Persistent connection count";
4504         snprintf(b, blen, "%s: %u", cp, val);
4505         break;
4506     case 0x40:
4507         cp = "Transmitted SSP frame count";
4508         snprintf(b, blen, "%s: %u", cp, val);
4509         break;
4510     case 0x41:
4511         cp = "Received SSP frame count";
4512         snprintf(b, blen, "%s: %u", cp, val);
4513         break;
4514     case 0x42:
4515         cp = "Transmitted SSP frame error count";
4516         snprintf(b, blen, "%s: %u", cp, val);
4517         break;
4518     case 0x43:
4519         cp = "Received SSP frame error count";
4520         snprintf(b, blen, "%s: %u", cp, val);
4521         break;
4522     case 0x44:
4523         cp = "Transmitted CREDIT_BLOCKED count";
4524         snprintf(b, blen, "%s: %u", cp, val);
4525         break;
4526     case 0x45:
4527         cp = "Received CREDIT_BLOCKED count";
4528         snprintf(b, blen, "%s: %u", cp, val);
4529         break;
4530     case 0x50:
4531         cp = "Transmitted SATA frame count";
4532         snprintf(b, blen, "%s: %u", cp, val);
4533         break;
4534     case 0x51:
4535         cp = "Received SATA frame count";
4536         snprintf(b, blen, "%s: %u", cp, val);
4537         break;
4538     case 0x52:
4539         cp = "SATA flow control buffer overflow count";
4540         snprintf(b, blen, "%s: %u", cp, val);
4541         break;
4542     case 0x60:
4543         cp = "Transmitted SMP frame count";
4544         snprintf(b, blen, "%s: %u", cp, val);
4545         break;
4546     case 0x61:
4547         cp = "Received SMP frame count";
4548         snprintf(b, blen, "%s: %u", cp, val);
4549         break;
4550     case 0x63:
4551         cp = "Received SMP frame error count";
4552         snprintf(b, blen, "%s: %u", cp, val);
4553         break;
4554     default:
4555         cp = "";
4556         snprintf(b, blen, "Unknown phy event source: %d, val=%u, "
4557                  "thresh_val=%u", pes, val, thresh_val);
4558         break;
4559     }
4560     return cp;
4561 }
4562 
4563 static const char * sas_link_rate_arr[16] = {
4564     "phy enabled; unknown rate",
4565     "phy disabled",
4566     "phy enabled; speed negotiation failed",
4567     "phy enabled; SATA spinup hold state",
4568     "phy enabled; port selector",
4569     "phy enabled; reset in progress",
4570     "phy enabled; unsupported phy attached",
4571     "reserved [0x7]",
4572     "1.5 Gbps",                 /* 0x8 */
4573     "3 Gbps",
4574     "6 Gbps",
4575     "12 Gbps",
4576     "22.5 Gbps",
4577     "reserved [0xd]",
4578     "reserved [0xe]",
4579     "reserved [0xf]",
4580 };
4581 
4582 static char *
sas_negot_link_rate(int lrate,char * b,int blen)4583 sas_negot_link_rate(int lrate, char * b, int blen)
4584 {
4585     int mask = 0xf;
4586 
4587     if (~mask & lrate)
4588         snprintf(b, blen, "bad link_rate value=0x%x\n", lrate);
4589     else
4590         snprintf(b, blen, "%s", sas_link_rate_arr[lrate]);
4591     return b;
4592 }
4593 
4594 /* helper for SAS port of PROTO_SPECIFIC_LPAGE [0x18] */
4595 static void
show_sas_port_param(const uint8_t * bp,int param_len,struct opts_t * op,sgj_opaque_p jop)4596 show_sas_port_param(const uint8_t * bp, int param_len, struct opts_t * op,
4597                     sgj_opaque_p jop)
4598 {
4599     int j, m, nphys, t, spld_len, pi;
4600     uint64_t ull;
4601     unsigned int ui, ui2, ui3, ui4;
4602     char * cp;
4603     const char * ccp;
4604     const char * cc2p;
4605     const char * cc3p;
4606     const char * cc4p;
4607     const uint8_t * vcp;
4608     sgj_state * jsp = &op->json_st;
4609     sgj_opaque_p jo2p = NULL;
4610     sgj_opaque_p jo3p = NULL;
4611     sgj_opaque_p jap = NULL;
4612     sgj_opaque_p ja2p = NULL;
4613     char b[160];
4614     char s[80];
4615     static char * rtpi = "Relative target port identifier";
4616     static char * psplpfstp =
4617                 "Protocol Specific Port log parameter for SAS target port";
4618     static char * at = "attached";
4619     static char * ip = "initiator_port";
4620     static char * tp = "target_port";
4621     static char * pvdt = "peak_value_detector_threshold";
4622     static const int sz = sizeof(s);
4623     static const int blen = sizeof(b);
4624 
4625     t = sg_get_unaligned_be16(bp + 0);
4626     if (op->do_name)
4627         sgj_pr_hr(jsp, " rel_target_port=%d\n", t);
4628     else
4629         sgj_pr_hr(jsp, " %s = %d\n", rtpi, t);
4630     if (op->do_name)
4631         sgj_pr_hr(jsp, "  gen_code=%d\n", bp[6]);
4632     else
4633         sgj_pr_hr(jsp, "  generation code = %d\n", bp[6]);
4634     nphys = bp[7];
4635     if (op->do_name)
4636         sgj_pr_hr(jsp, "  num_phys=%d\n", nphys);
4637     else
4638         sgj_pr_hr(jsp, "  number of phys = %d\n", nphys);
4639     if (jsp->pr_as_json) {
4640         js_snakenv_ihexstr_nex(jsp, jop, param_c , t, true,
4641                                NULL, psplpfstp, rtpi);
4642         pi = 0xf & bp[4];
4643         sgj_js_nv_ihexstr(jsp, jop, "protocol_identifier", pi, NULL,
4644                           sg_get_trans_proto_str(pi, blen, b));
4645         sgj_js_nv_ihex(jsp, jop, "generation_code", bp[6]);
4646         sgj_js_nv_ihex(jsp, jop, "number_of_phys", bp[7]);
4647         jap = sgj_named_subarray_r(jsp, jop, "sas_phy_log_descriptor_list");
4648     }
4649 
4650     for (j = 0, vcp = bp + 8; j < (param_len - 8);
4651          vcp += spld_len, j += spld_len) {
4652         if (jsp->pr_as_json) {
4653             jo2p = sgj_new_unattached_object_r(jsp);
4654             if (op->do_pcb)
4655                 js_pcb(jsp, jo2p, vcp[2]);
4656         }
4657         if (op->do_name)
4658             sgj_pr_hr(jsp, "    phy_id=%d\n", vcp[1]);
4659         else
4660             sgj_haj_vi(jsp, jo2p, 2, "phy identifier", SGJ_SEP_EQUAL_1_SPACE,
4661                        vcp[1], true);
4662         spld_len = vcp[3];
4663         if (spld_len < 44)
4664             spld_len = 48;      /* in SAS-1 and SAS-1.1 vcp[3]==0 */
4665         else
4666             spld_len += 4;
4667         if (op->do_name) {
4668             t = ((0x70 & vcp[4]) >> 4);
4669             sgj_pr_hr(jsp, "      att_dev_type=%d\n", t);
4670             sgj_pr_hr(jsp, "      att_iport_mask=0x%x\n", vcp[6]);
4671             sgj_pr_hr(jsp, "      att_phy_id=%d\n", vcp[24]);
4672             sgj_pr_hr(jsp, "      att_reason=0x%x\n", (vcp[4] & 0xf));
4673             ull = sg_get_unaligned_be64(vcp + 16);
4674             sgj_pr_hr(jsp, "      att_sas_addr=0x%" PRIx64 "\n", ull);
4675             sgj_pr_hr(jsp, "      att_tport_mask=0x%x\n", vcp[7]);
4676             ui = sg_get_unaligned_be32(vcp + 32);
4677             sgj_pr_hr(jsp, "      inv_dwords=%u\n", ui);
4678             ui = sg_get_unaligned_be32(vcp + 40);
4679             sgj_pr_hr(jsp, "      loss_dword_sync=%u\n", ui);
4680             sgj_pr_hr(jsp, "      neg_log_lrate=%d\n", 0xf & vcp[5]);
4681             ui = sg_get_unaligned_be32(vcp + 44);
4682             sgj_pr_hr(jsp, "      phy_reset_probs=%u\n", ui);
4683             ui = sg_get_unaligned_be32(vcp + 36);
4684             sgj_pr_hr(jsp, "      running_disparity=%u\n", ui);
4685             sgj_pr_hr(jsp, "      reason=0x%x\n", (vcp[5] & 0xf0) >> 4);
4686             ull = sg_get_unaligned_be64(vcp + 8);
4687             sgj_pr_hr(jsp, "      sas_addr=0x%" PRIx64 "\n", ull);
4688         } else {
4689             t = ((0x70 & vcp[4]) >> 4);
4690             /* attached SAS device type. In SAS-1.1 case 2 was an edge
4691              * expander; in SAS-2 case 3 is marked as obsolete. */
4692             switch (t) {
4693             case 0: snprintf(s, sz, "no device %s", at); break;
4694             case 1: snprintf(s, sz, "SAS or SATA device"); break;
4695             case 2: snprintf(s, sz, "expander device"); break;
4696             case 3: snprintf(s, sz, "expander device (fanout)"); break;
4697             default: snprintf(s, sz, "%s [%d]", rsv_s, t); break;
4698             }
4699             /* the word 'SAS' in following added in spl4r01 */
4700             sgj_pr_hr(jsp, "    %s SAS device type: %s\n", at, s);
4701             if (jsp->pr_as_json)
4702                 sgj_js_nv_ihexstr(jsp, jo2p, "attached_sas_device_type", t,
4703                                   NULL, s);
4704             t = 0xf & vcp[4];
4705             switch (t) {
4706             case 0: snprintf(s, sz, "%s", unknown_s); break;
4707             case 1: snprintf(s, sz, "power on"); break;
4708             case 2: snprintf(s, sz, "hard reset"); break;
4709             case 3: snprintf(s, sz, "SMP phy control function"); break;
4710             case 4: snprintf(s, sz, "loss of dword synchronization"); break;
4711             case 5: snprintf(s, sz, "mux mix up"); break;
4712             case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
4713                 break;
4714             case 7: snprintf(s, sz, "break timeout timer expired"); break;
4715             case 8: snprintf(s, sz, "phy test function stopped"); break;
4716             case 9: snprintf(s, sz, "expander device reduced functionality");
4717                  break;
4718             default: snprintf(s, sz, "%s [0x%x]", rsv_s, t); break;
4719             }
4720             sgj_pr_hr(jsp, "    %s reason: %s\n", at, s);
4721             if (jsp->pr_as_json)
4722                 sgj_js_nv_ihexstr(jsp, jo2p, "attached_reason", t, NULL, s);
4723             t = (vcp[5] & 0xf0) >> 4;
4724             switch (t) {
4725             case 0: snprintf(s, sz, "%s", unknown_s); break;
4726             case 1: snprintf(s, sz, "power on"); break;
4727             case 2: snprintf(s, sz, "hard reset"); break;
4728             case 3: snprintf(s, sz, "SMP phy control function"); break;
4729             case 4: snprintf(s, sz, "loss of dword synchronization"); break;
4730             case 5: snprintf(s, sz, "mux mix up"); break;
4731             case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
4732                 break;
4733             case 7: snprintf(s, sz, "break timeout timer expired"); break;
4734             case 8: snprintf(s, sz, "phy test function stopped"); break;
4735             case 9: snprintf(s, sz, "expander device reduced functionality");
4736                  break;
4737             default: snprintf(s, sz, "%s [0x%x]", rsv_s, t); break;
4738             }
4739             sgj_pr_hr(jsp, "    reason: %s\n", s);
4740             if (jsp->pr_as_json)
4741                 sgj_js_nv_ihexstr(jsp, jo2p, "reason", t, NULL, s);
4742             t = (0xf & vcp[5]);
4743             ccp = "negotiated logical link rate";
4744             cc2p = sas_negot_link_rate(t, s, sz);
4745             sgj_pr_hr(jsp, "    %s: %s\n", ccp, cc2p);
4746             if (jsp->pr_as_json) {
4747                 sgj_convert_to_snake_name(ccp, b, blen);
4748                 sgj_js_nv_ihexstr(jsp, jo2p, b, t, NULL, cc2p);
4749             }
4750 
4751             sgj_pr_hr(jsp, "    %s initiator port: ssp=%d stp=%d smp=%d\n",
4752                       at, !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
4753             if (jsp->pr_as_json) {
4754                 snprintf(b, blen, "%s_ssp_%s", at, ip);
4755                 sgj_js_nv_i(jsp, jo2p, b, !! (vcp[6] & 8));
4756                 snprintf(b, blen, "%s_stp_%s", at, ip);
4757                 sgj_js_nv_i(jsp, jo2p, b, !! (vcp[6] & 4));
4758                 snprintf(b, blen, "%s_smp_%s", at, ip);
4759                 sgj_js_nv_i(jsp, jo2p, b, !! (vcp[6] & 2));
4760             }
4761             sgj_pr_hr(jsp, "    %s target port: ssp=%d stp=%d smp=%d\n", at,
4762                       !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
4763             if (jsp->pr_as_json) {
4764                 snprintf(b, blen, "%s_ssp_%s", at, tp);
4765                 sgj_js_nv_i(jsp, jo2p, b, !! (vcp[7] & 8));
4766                 snprintf(b, blen, "%s_stp_%s", at, tp);
4767                 sgj_js_nv_i(jsp, jo2p, b, !! (vcp[7] & 4));
4768                 snprintf(b, blen, "%s_smp_%s", at, tp);
4769                 sgj_js_nv_i(jsp, jo2p, b, !! (vcp[7] & 2));
4770             }
4771             ull = sg_get_unaligned_be64(vcp + 8);
4772             sgj_pr_hr(jsp, "    SAS address = 0x%" PRIx64 "\n", ull);
4773             if (jsp->pr_as_json)
4774                 sgj_js_nv_ihex(jsp, jo2p, "sas_address", ull);
4775             ull = sg_get_unaligned_be64(vcp + 16);
4776             sgj_pr_hr(jsp, "    %s SAS address = 0x%" PRIx64 "\n", at, ull);
4777             if (jsp->pr_as_json)
4778                 sgj_js_nv_ihex(jsp, jo2p, "attached_sas_address", ull);
4779             ccp = "attached phy identifier";
4780             sgj_haj_vi(jsp, jo2p, 4, ccp, SGJ_SEP_EQUAL_1_SPACE, vcp[24],
4781                        true);
4782             ccp = "Invalid DWORD count";
4783             ui = sg_get_unaligned_be32(vcp + 32);
4784             cc2p = "Running disparity error count";
4785             ui2 = sg_get_unaligned_be32(vcp + 36);
4786             cc3p = "Loss of DWORD synchronization count";
4787             ui3 = sg_get_unaligned_be32(vcp + 40);
4788             cc4p = "Phy reset problem count";
4789             ui4 = sg_get_unaligned_be32(vcp + 44);
4790             if (jsp->pr_as_json) {
4791                 sgj_convert_to_snake_name(ccp, b, blen);
4792                 sgj_js_nv_ihex(jsp, jo2p, b, ui);
4793                 sgj_convert_to_snake_name(cc2p, b, blen);
4794                 sgj_js_nv_ihex(jsp, jo2p, b, ui2);
4795                 sgj_convert_to_snake_name(cc3p, b, blen);
4796                 sgj_js_nv_ihex(jsp, jo2p, b, ui3);
4797                 sgj_convert_to_snake_name(cc4p, b, blen);
4798                 sgj_js_nv_ihex(jsp, jo2p, b, ui4);
4799             } else {
4800                 if (0 == op->do_brief) {
4801                     sgj_pr_hr(jsp, "    %s = %u\n", ccp, ui);
4802                     sgj_pr_hr(jsp, "    %s = %u\n", cc2p, ui2);
4803                     sgj_pr_hr(jsp, "    %s = %u\n", cc3p, ui3);
4804                     sgj_pr_hr(jsp, "    %s = %u\n", cc4p, ui4);
4805                 }
4806             }
4807         }
4808         if (op->do_brief > 0)
4809             goto skip;
4810         if (spld_len > 51) {
4811             int num_ped;
4812             const uint8_t * xcp;
4813 
4814             num_ped = vcp[51];
4815             if (op->verbose > 1)
4816                 sgj_pr_hr(jsp, "    <<Phy event descriptors: %d, spld_len: "
4817                           "%d, calc_ped: %d>>\n", num_ped, spld_len,
4818                           (spld_len - 52) / 12);
4819             if (num_ped > 0) {
4820                 if (op->do_name) {
4821                     sgj_pr_hr(jsp, "      phy_event_desc_num=%d\n", num_ped);
4822                     return;      /* don't decode at this stage */
4823                 } else
4824                     sgj_pr_hr(jsp, "    Phy event descriptors:\n");
4825             }
4826             if (jsp->pr_as_json) {
4827                 sgj_js_nv_i(jsp, jo2p, "number_of_phy_event_descriptors",
4828                             num_ped);
4829                 if (num_ped > 0)
4830                     ja2p = sgj_named_subarray_r(jsp, jo2p,
4831                                                 "phy_event_descriptor_list");
4832             }
4833             xcp = vcp + 52;
4834             for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
4835                 int pes = xcp[3];
4836                 unsigned int pvdt_v;
4837 
4838                 if (jsp->pr_as_json)
4839                     jo3p = sgj_new_unattached_object_r(jsp);
4840                 ui = sg_get_unaligned_be32(xcp + 4);
4841                 pvdt_v = sg_get_unaligned_be32(xcp + 8);
4842                 ccp = show_sas_phy_event_info(pes, ui, pvdt_v, b, blen);
4843                 if (0 == strlen(ccp)) {
4844                     sgj_pr_hr(jsp, "      %s\n", b);    /* unknown pvdt_v */
4845                     if (jsp->pr_as_json) {
4846                         int n;
4847 
4848                         snprintf(s, sz, "%s_pes_0x%x", unknown_s, pes);
4849                         sgj_js_nv_ihex(jsp, jo3p, s, ui);
4850                         n = strlen(s);
4851                         sg_scnpr(s + n, sz - n, "_%s", "threshold");
4852                         sgj_js_nv_ihex(jsp, jo3p, s, pvdt_v);
4853                     }
4854                 } else {
4855                     if (jsp->pr_as_json) {
4856                         sgj_convert_to_snake_name(ccp, s, sz);
4857                         sgj_js_nv_ihex(jsp, jo3p, s, ui);
4858                         if (0x2b == pes)
4859                             sgj_js_nv_ihex(jsp, jo3p, pvdt, pvdt_v);
4860                         else if (0x2c == pes)
4861                             sgj_js_nv_ihex(jsp, jo3p, pvdt, pvdt_v);
4862                         else if (0x2d == pes)
4863                             sgj_js_nv_ihex(jsp, jo3p, pvdt, pvdt_v);
4864                         else if (0x2e == pes)
4865                             sgj_js_nv_ihex(jsp, jo3p, pvdt, pvdt_v);
4866                     } else {
4867                         cp = strchr(b, '\t');
4868                         if (cp) {
4869                             *cp = '\0';
4870                             sgj_pr_hr(jsp, "      %s\n", b);
4871                             sgj_pr_hr(jsp, "      %s\n", cp + 1);
4872                         } else
4873                             sgj_pr_hr(jsp, "      %s\n", b);
4874                     }
4875                 }
4876                 if (jsp->pr_as_json)
4877                     sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
4878             }
4879         } else if (op->verbose)
4880            printf("    <<No phy event descriptors>>\n");
4881 skip:
4882         if (jsp->pr_as_json)
4883             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
4884     }   /* end of for loop over phys with this relative port */
4885 }
4886 
4887 /* PROTO_SPECIFIC_LPAGE [0x18] <psp> */
4888 static bool
show_protocol_specific_port_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)4889 show_protocol_specific_port_page(const uint8_t * resp, int len,
4890                                  struct opts_t * op, sgj_opaque_p jop)
4891 {
4892     int k, num, pl, pid;
4893     const uint8_t * bp;
4894     sgj_state * jsp = &op->json_st;
4895     sgj_opaque_p jo2p;
4896     sgj_opaque_p jo3p = NULL;
4897     sgj_opaque_p jap = NULL;
4898     char b[128];
4899     static const char * psplp = "Protocol specific port log page";
4900     static const char * fss = "for SAS SSP";
4901 
4902     num = len - 4;
4903     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
4904         if (op->do_name)
4905             sgj_pr_hr(jsp, "log_page=0x%x\n", PROTO_SPECIFIC_LPAGE);
4906         else
4907             sgj_pr_hr(jsp, "%s  [0x18]\n", psplp);
4908     }
4909     if (jsp->pr_as_json) {
4910         jo2p = sg_log_js_hdr(jsp, jop, psplp, resp);
4911         jap = sgj_named_subarray_r(jsp, jo2p,
4912                            "protocol_specific_port_log_parameter_list");
4913     }
4914 
4915     for (k = 0, bp = resp + 4; k < num; ) {
4916         int pc = sg_get_unaligned_be16(bp + 0);
4917 
4918         pl = bp[3] + 4;
4919         if (op->filter_given) {
4920             if (pc != op->filter)
4921                 goto skip;
4922         }
4923         if (op->do_raw) {
4924             dStrRaw(bp, pl);
4925             goto skip;
4926         } else if (op->do_hex) {
4927             hex2stdout(bp, pl, op->dstrhex_no_ascii);
4928             goto skip;
4929         }
4930         pid = 0xf & bp[4];
4931         if (6 != pid) {
4932             pr2serr("Protocol identifier: %d, only support SAS (SPL) which "
4933                     "is 6\n", pid);
4934             return false;   /* only decode SAS log page */
4935         }
4936         if (jsp->pr_as_json) {
4937             jo3p = sgj_new_unattached_object_r(jsp);
4938             if (op->do_pcb)
4939                 js_pcb(jsp, jo3p, bp[2]);
4940         }
4941         if ((0 == k) && (! op->do_name))
4942              sgj_pr_hr(jsp, "%s %s  [0x18]\n", psplp, fss);
4943         /* call helper */
4944         show_sas_port_param(bp, pl, op, jo3p);
4945         if (jsp->pr_as_json)
4946             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
4947         if ((op->do_pcb) && (! op->do_name))
4948             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], b,
4949                       sizeof(b)));
4950         if (op->filter_given)
4951             break;
4952 skip:
4953         k += pl;
4954         bp += pl;
4955     }
4956     return true;
4957 }
4958 
4959 /* Returns true if processed page, false otherwise */
4960 /* STATS_LPAGE [0x19], subpages: 0x0 to 0x1f <gsp,grsp>  introduced: SPC-4 */
4961 static bool
show_stats_perform_pages(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)4962 show_stats_perform_pages(const uint8_t * resp, int len,
4963                          struct opts_t * op, sgj_opaque_p jop)
4964 {
4965     bool nam, spf;
4966     int k, num, param_len, param_code, subpg_code, extra;
4967     uint64_t ull;
4968     const uint8_t * bp;
4969     const char * ccp;
4970     const char * pg_name;
4971     sgj_state * jsp = &op->json_st;
4972     sgj_opaque_p jo2p;
4973     sgj_opaque_p jo3p = NULL;
4974     sgj_opaque_p jap = NULL;
4975     char str[PCB_STR_LEN];
4976     static const char * gsaplp =
4977                 "General statistics and performance log page";
4978     static const char * gr_saplp =
4979                 "Group statistics and performance log page";
4980 
4981 // yyyyyyyyyyyyyyyyyy
4982     nam = op->do_name;
4983     num = len - 4;
4984     bp = resp + 4;
4985     spf = !!(resp[0] & 0x40);
4986     subpg_code = spf ? resp[1] : NOT_SPG_SUBPG;
4987     if (0 == subpg_code)
4988         pg_name = gsaplp;
4989     else if (subpg_code < 0x20)
4990         pg_name = gr_saplp;
4991     else
4992         pg_name = "Unknown subpage";
4993     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
4994         if (nam) {
4995              sgj_pr_hr(jsp, "log_page=0x%x\n", STATS_LPAGE);
4996             if (subpg_code > 0)
4997                 sgj_pr_hr(jsp, "log_subpage=0x%x\n", subpg_code);
4998         } else {
4999             if (0 == subpg_code)
5000                 sgj_pr_hr(jsp, "%s  [0x19]\n", gsaplp);
5001             else if (subpg_code < 0x20)
5002                 sgj_pr_hr(jsp, "%s (%d)  [0x19,0x%x]\n", gr_saplp, subpg_code,
5003                           subpg_code);
5004             else
5005                 sgj_pr_hr(jsp, "%s: %d  [0x19,0x%x]\n", pg_name, subpg_code,
5006                           subpg_code);
5007         }
5008     }
5009     if (jsp->pr_as_json)
5010         jo2p = sg_log_js_hdr(jsp, jop, pg_name, resp);
5011     if (subpg_code > 31)
5012         return false;
5013     if (jsp->pr_as_json)
5014         jap = sgj_named_subarray_r(jsp, jo2p, 0 == subpg_code ?
5015                         "general_statistics_and_performance_log_parameters" :
5016                         "group_statistics_and_performance_log_parameters");
5017     if (0 == subpg_code) { /* General statistics and performance log page */
5018         if (num < 0x5c)
5019             return false;
5020         for (k = num; k > 0; k -= extra, bp += extra) {
5021             unsigned int ui;
5022 
5023             if (k < 3)
5024                 return false;
5025             param_len = bp[3];
5026             extra = param_len + 4;
5027             param_code = sg_get_unaligned_be16(bp + 0);
5028             if (op->filter_given) {
5029                 if (param_code != op->filter)
5030                     continue;
5031             }
5032             if (op->do_raw) {
5033                 dStrRaw(bp, extra);
5034                 goto skip;
5035             } else if (op->do_hex) {
5036                 hex2stdout(bp, extra, op->dstrhex_no_ascii);
5037                 goto skip;
5038             }
5039             if (jsp->pr_as_json) {
5040                 jo3p = sgj_new_unattached_object_r(jsp);
5041                 if (op->do_pcb)
5042                     js_pcb(jsp, jo3p, bp[2]);
5043             }
5044 
5045             switch (param_code) {
5046             case 1:     /* Statistics and performance log parameter */
5047                 ccp = nam ? "parameter_code=1" : "Statistics and performance "
5048                         "log parameter";
5049                 sgj_pr_hr(jsp, "%s\n", ccp);
5050                 ull = sg_get_unaligned_be64(bp + 4);
5051                 ccp = nam ? "read_commands=" : "number of read commands = ";
5052                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5053                 ull = sg_get_unaligned_be64(bp + 12);
5054                 ccp = nam ? "write_commands=" : "number of write commands = ";
5055                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5056                 ull = sg_get_unaligned_be64(bp + 20);
5057                 ccp = nam ? "lb_received="
5058                           : "number of logical blocks received = ";
5059                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5060                 ull = sg_get_unaligned_be64(bp + 28);
5061                 ccp = nam ? "lb_transmitted="
5062                           : "number of logical blocks transmitted = ";
5063                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5064                 ull = sg_get_unaligned_be64(bp + 36);
5065                 ccp = nam ? "read_proc_intervals="
5066                           : "read command processing intervals = ";
5067                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5068                 ull = sg_get_unaligned_be64(bp + 44);
5069                 ccp = nam ? "write_proc_intervals="
5070                           : "write command processing intervals = ";
5071                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5072                 ull = sg_get_unaligned_be64(bp + 52);
5073                 ccp = nam ? "weight_rw_commands=" : "weighted number of "
5074                                 "read commands plus write commands = ";
5075                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5076                 ull = sg_get_unaligned_be64(bp + 60);
5077                 ccp = nam ? "weight_rw_processing=" : "weighted read command "
5078                                 "processing plus write command processing = ";
5079                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5080                 break;
5081             case 2:     /* Idle time log parameter */
5082                 ccp = nam ? "parameter_code=2" : "Idle time log parameter";
5083                 sgj_pr_hr(jsp, "%s\n", ccp);
5084                 ull = sg_get_unaligned_be64(bp + 4);
5085                 ccp = nam ? "idle_time_intervals=" : "idle time "
5086                                 "intervals = ";
5087                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5088                 break;
5089             case 3:     /* Time interval log parameter for general stats */
5090                 ccp = nam ? "parameter_code=3" : "Time interval log "
5091                         "parameter for general stats";
5092                 sgj_pr_hr(jsp, "%s\n", ccp);
5093                 ui = sg_get_unaligned_be32(bp + 4);
5094                 ccp = nam ? "time_interval_neg_exp=" : "time interval "
5095                                 "negative exponent = ";
5096                 sgj_pr_hr(jsp, "  %s%u\n", ccp, ui);
5097                 ui = sg_get_unaligned_be32(bp + 8);
5098                 ccp = nam ? "time_interval_int=" : "time interval "
5099                                 "integer = ";
5100                 sgj_pr_hr(jsp, "  %s%u\n", ccp, ui);
5101                 break;
5102             case 4:     /* FUA statistics and performance log parameter */
5103                 ccp = nam ? "parameter_code=4" : "Force unit access "
5104                         "statistics and performance log parameter ";
5105                 sgj_pr_hr(jsp, "%s\n", ccp);
5106                 ull = sg_get_unaligned_be64(bp + 4);
5107                 ccp = nam ? "read_fua_commands=" : "number of read FUA "
5108                                 "commands = ";
5109                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5110                 ull = sg_get_unaligned_be64(bp + 12);
5111                 ccp = nam ? "write_fua_commands=" : "number of write FUA "
5112                                 "commands = ";
5113                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5114                 ull = sg_get_unaligned_be64(bp + 20);
5115                 ccp = nam ? "read_fua_nv_commands="
5116                           : "number of read FUA_NV commands = ";
5117                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5118                 ull = sg_get_unaligned_be64(bp + 28);
5119                 ccp = nam ? "write_fua_nv_commands="
5120                           : "number of write FUA_NV commands = ";
5121                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5122                 ull = sg_get_unaligned_be64(bp + 36);
5123                 ccp = nam ? "read_fua_proc_intervals="
5124                           : "read FUA command processing intervals = ";
5125                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5126                 ull = sg_get_unaligned_be64(bp + 44);
5127                 ccp = nam ? "write_fua_proc_intervals="
5128                           : "write FUA command processing intervals = ";
5129                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5130                 ull = sg_get_unaligned_be64(bp + 52);
5131                 ccp = nam ? "read_fua_nv_proc_intervals="
5132                           : "read FUA_NV command processing intervals = ";
5133                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5134                 ull = sg_get_unaligned_be64(bp + 60);
5135                 ccp = nam ? "write_fua_nv_proc_intervals="
5136                           : "write FUA_NV command processing intervals = ";
5137                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5138                 break;
5139             case 6:     /* Time interval log parameter for cache stats */
5140                 ccp = nam ? "parameter_code=6" : "Time interval log "
5141                         "parameter for cache stats";
5142                 sgj_pr_hr(jsp, "%s\n", ccp);
5143                 ull = sg_get_unaligned_be64(bp + 4);
5144                 ccp = nam ? "time_interval_neg_exp=" : "time interval "
5145                                 "negative exponent = ";
5146                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5147                 ull = sg_get_unaligned_be64(bp + 8);
5148                 ccp = nam ? "time_interval_int=" : "time interval "
5149                                 "integer = ";
5150                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5151                 break;
5152             default:
5153                 if (nam) {
5154                     sgj_pr_hr(jsp, "parameter_code=%d\n", param_code);
5155                     sgj_pr_hr(jsp, "  unknown=1\n");
5156                 } else
5157                     pr2serr("show_performance...  unknown %s %d\n", param_c,
5158                             param_code);
5159                 if (op->verbose)
5160                     hex2stderr(bp, extra, 1);
5161                 break;
5162             }
5163             if (jsp->pr_as_json)
5164                 sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
5165             if ((op->do_pcb) && (! op->do_name))
5166                 sgj_pr_hr(jsp, "    <%s>\n", get_pcb_str(bp[2], str,
5167                           sizeof(str)));
5168 skip:
5169             if (op->filter_given)
5170                 break;
5171         }
5172     } else {    /* Group statistics and performance (n) log page */
5173         if (num < 0x34)
5174             return false;
5175         for (k = num; k > 0; k -= extra, bp += extra) {
5176             if (k < 3)
5177                 return false;
5178             param_len = bp[3];
5179             extra = param_len + 4;
5180             param_code = sg_get_unaligned_be16(bp + 0);
5181             if (op->filter_given) {
5182                 if (param_code != op->filter)
5183                     continue;
5184             }
5185             if (op->do_raw) {
5186                 dStrRaw(bp, extra);
5187                 goto skip2;
5188             } else if (op->do_hex) {
5189                 hex2stdout(bp, extra, op->dstrhex_no_ascii);
5190                 goto skip2;
5191             }
5192             if (jsp->pr_as_json) {
5193                 jo3p = sgj_new_unattached_object_r(jsp);
5194                 if (op->do_pcb)
5195                     js_pcb(jsp, jo3p, bp[2]);
5196             }
5197 
5198             switch (param_code) {
5199             case 1:     /* Group n Statistics and performance log parameter */
5200                 if (nam)
5201                     sgj_pr_hr(jsp, "parameter_code=1\n");
5202                 else
5203                     sgj_pr_hr(jsp, "Group %d Statistics and performance log "
5204                            "parameter\n", subpg_code);
5205                 ull = sg_get_unaligned_be64(bp + 4);
5206                 ccp = nam ? "gn_read_commands=" : "group n number of read "
5207                                 "commands = ";
5208                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5209                 ull = sg_get_unaligned_be64(bp + 12);
5210                 ccp = nam ? "gn_write_commands=" : "group n number of write "
5211                                 "commands = ";
5212                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5213                 ull = sg_get_unaligned_be64(bp + 20);
5214                 ccp = nam ? "gn_lb_received="
5215                           : "group n number of logical blocks received = ";
5216                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5217                 ull = sg_get_unaligned_be64(bp + 28);
5218                 ccp = nam ? "gn_lb_transmitted="
5219                           : "group n number of logical blocks transmitted = ";
5220                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5221                 ull = sg_get_unaligned_be64(bp + 36);
5222                 ccp = nam ? "gn_read_proc_intervals="
5223                           : "group n read command processing intervals = ";
5224                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5225                 ull = sg_get_unaligned_be64(bp + 44);
5226                 ccp = nam ? "gn_write_proc_intervals="
5227                           : "group n write command processing intervals = ";
5228                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5229                 break;
5230             case 4: /* Group n FUA statistics and performance log parameter */
5231                 ccp = nam ? "parameter_code=4" : "Group n force unit access "
5232                         "statistics and performance log parameter";
5233                 sgj_pr_hr(jsp, "%s\n", ccp);
5234                 ull = sg_get_unaligned_be64(bp + 4);
5235                 ccp = nam ? "gn_read_fua_commands="
5236                           : "group n number of read FUA commands = ";
5237                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5238                 ull = sg_get_unaligned_be64(bp + 12);
5239                 ccp = nam ? "gn_write_fua_commands="
5240                           : "group n number of write FUA commands = ";
5241                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5242                 ull = sg_get_unaligned_be64(bp + 20);
5243                 ccp = nam ? "gn_read_fua_nv_commands="
5244                           : "group n number of read FUA_NV commands = ";
5245                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5246                 ull = sg_get_unaligned_be64(bp + 28);
5247                 ccp = nam ? "gn_write_fua_nv_commands="
5248                           : "group n number of write FUA_NV commands = ";
5249                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5250                 ull = sg_get_unaligned_be64(bp + 36);
5251                 ccp = nam ? "gn_read_fua_proc_intervals="
5252                           : "group n read FUA command processing intervals = ";
5253                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5254                 ull = sg_get_unaligned_be64(bp + 44);
5255                 ccp = nam ? "gn_write_fua_proc_intervals=" : "group n write "
5256                             "FUA command processing intervals = ";
5257                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5258                 ull = sg_get_unaligned_be64(bp + 52);
5259                 ccp = nam ? "gn_read_fua_nv_proc_intervals=" : "group n "
5260                             "read FUA_NV command processing intervals = ";
5261                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5262                 ull = sg_get_unaligned_be64(bp + 60);
5263                 ccp = nam ? "gn_write_fua_nv_proc_intervals=" : "group n "
5264                             "write FUA_NV command processing intervals = ";
5265                 sgj_pr_hr(jsp, "  %s%" PRIu64 "\n", ccp, ull);
5266                 break;
5267             default:
5268                 if (nam) {
5269                     sgj_pr_hr(jsp, "parameter_code=%d\n", param_code);
5270                     sgj_pr_hr(jsp, "  unknown=1\n");
5271                 } else
5272                     pr2serr("show_performance...  unknown %s %d\n", param_c,
5273                             param_code);
5274                 if (op->verbose)
5275                     hex2stderr(bp, extra, 1);
5276                 break;
5277             }
5278             if (jsp->pr_as_json)
5279                 sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
5280             if ((op->do_pcb) && (! op->do_name))
5281                 sgj_pr_hr(jsp, "    <%s>\n", get_pcb_str(bp[2], str,
5282                           sizeof(str)));
5283 skip2:
5284             if (op->filter_given)
5285                 break;
5286         }
5287     }
5288     return true;
5289 }
5290 
5291 /* Returns true if processed page, false otherwise */
5292 /* STATS_LPAGE [0x19], CACHE_STATS_SUBPG [0x20] <cms>  introduced: SPC-4 */
5293 static bool
show_cache_stats_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)5294 show_cache_stats_page(const uint8_t * resp, int len, struct opts_t * op,
5295                       sgj_opaque_p jop)
5296 {
5297     int k, num, subpg_code, extra;
5298     bool nam, spf;
5299     unsigned int ui;
5300     const uint8_t * bp;
5301     const char * ccp;
5302     uint64_t ull;
5303     char str[PCB_STR_LEN];
5304 
5305 if (jop) { };
5306     nam = op->do_name;
5307     num = len - 4;
5308     bp = resp + 4;
5309     if (num < 4) {
5310         pr2serr("badly formed Cache memory statistics page\n");
5311         return false;
5312     }
5313     spf = !!(resp[0] & 0x40);
5314     subpg_code = spf ? resp[1] : NOT_SPG_SUBPG;
5315     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
5316         if (nam) {
5317             printf("log_page=0x%x\n", STATS_LPAGE);
5318             if (subpg_code > 0)
5319                 printf("log_subpage=0x%x\n", subpg_code);
5320         } else
5321             printf("Cache memory statistics page  [0x19,0x20]\n");
5322     }
5323 
5324     for (k = num; k > 0; k -= extra, bp += extra) {
5325         int pc;
5326 
5327         if (k < 3) {
5328             pr2serr("short Cache memory statistics page\n");
5329             return false;
5330         }
5331         if (8 != bp[3]) {
5332             printf("Cache memory statistics page parameter length not "
5333                    "8\n");
5334             return false;
5335         }
5336         extra = bp[3] + 4;
5337         pc = sg_get_unaligned_be16(bp + 0);
5338         if (op->filter_given) {
5339             if (pc != op->filter)
5340                 continue;
5341         }
5342         if (op->do_raw) {
5343             dStrRaw(bp, extra);
5344             goto skip;
5345         } else if (op->do_hex) {
5346             hex2stdout(bp, extra, op->dstrhex_no_ascii);
5347             goto skip;
5348         }
5349         switch (pc) {
5350         case 1:     /* Read cache memory hits log parameter */
5351             ccp = nam ? "parameter_code=1" :
5352                         "Read cache memory hits log parameter";
5353             printf("%s\n", ccp);
5354             ull = sg_get_unaligned_be64(bp + 4);
5355             ccp = nam ? "read_cache_memory_hits=" :
5356                         "read cache memory hits = ";
5357             printf("  %s%" PRIu64 "\n", ccp, ull);
5358             break;
5359         case 2:     /* Reads to cache memory log parameter */
5360             ccp = nam ? "parameter_code=2" :
5361                         "Reads to cache memory log parameter";
5362             printf("%s\n", ccp);
5363             ull = sg_get_unaligned_be64(bp + 4);
5364             ccp = nam ? "reads_to_cache_memory=" :
5365                         "reads to cache memory = ";
5366             printf("  %s%" PRIu64 "\n", ccp, ull);
5367             break;
5368         case 3:     /* Write cache memory hits log parameter */
5369             ccp = nam ? "parameter_code=3" :
5370                         "Write cache memory hits log parameter";
5371             printf("%s\n", ccp);
5372             ull = sg_get_unaligned_be64(bp + 4);
5373             ccp = nam ? "write_cache_memory_hits=" :
5374                         "write cache memory hits = ";
5375             printf("  %s%" PRIu64 "\n", ccp, ull);
5376             break;
5377         case 4:     /* Writes from cache memory log parameter */
5378             ccp = nam ? "parameter_code=4" :
5379                         "Writes from cache memory log parameter";
5380             printf("%s\n", ccp);
5381             ull = sg_get_unaligned_be64(bp + 4);
5382             ccp = nam ? "writes_from_cache_memory=" :
5383                         "writes from cache memory = ";
5384             printf("  %s%" PRIu64 "\n", ccp, ull);
5385             break;
5386         case 5:     /* Time from last hard reset log parameter */
5387             ccp = nam ? "parameter_code=5" :
5388                         "Time from last hard reset log parameter";
5389             printf("%s\n", ccp);
5390             ull = sg_get_unaligned_be64(bp + 4);
5391             ccp = nam ? "time_from_last_hard_reset=" :
5392                         "time from last hard reset = ";
5393             printf("  %s%" PRIu64 "\n", ccp, ull);
5394             break;
5395         case 6:     /* Time interval log parameter for cache stats */
5396             ccp = nam ? "parameter_code=6" :
5397                         "Time interval log parameter";
5398             printf("%s\n", ccp);
5399             ui = sg_get_unaligned_be32(bp + 4);
5400             ccp = nam ? "time_interval_neg_exp=" : "time interval "
5401                             "negative exponent = ";
5402             printf("  %s%u\n", ccp, ui);
5403             ui = sg_get_unaligned_be32(bp + 8);
5404             ccp = nam ? "time_interval_int=" : "time interval "
5405                             "integer = ";
5406             printf("  %s%u\n", ccp, ui);
5407             break;
5408         default:
5409             if (nam) {
5410                 printf("parameter_code=%d\n", pc);
5411                 printf("  unknown=1\n");
5412             } else
5413                 pr2serr("show_performance...  unknown %s %d\n", param_c,
5414                         pc);
5415             if (op->verbose)
5416                 hex2stderr(bp, extra, 1);
5417             break;
5418         }
5419         if ((op->do_pcb) && (! op->do_name))
5420             printf("    <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
5421 skip:
5422         if (op->filter_given)
5423             break;
5424     }
5425     return true;
5426 }
5427 
5428 /* FORMAT_STATUS_LPAGE [0x8] <fs>  introduced: SBC-2 */
5429 static bool
show_format_status_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)5430 show_format_status_page(const uint8_t * resp, int len,
5431                         struct opts_t * op, sgj_opaque_p jop)
5432 {
5433     bool is_count, is_not_avail;
5434     int k, num, pl, pc;
5435     uint64_t ull;
5436     const char * cp = "";
5437     const uint8_t * bp;
5438     const uint8_t * xp;
5439     sgj_state * jsp = &op->json_st;
5440     sgj_opaque_p jo2p;
5441     sgj_opaque_p jo3p = NULL;
5442     sgj_opaque_p jap = NULL;
5443     char str[PCB_STR_LEN];
5444     char b[512];
5445     static const char * fslp = "Format status log page";
5446     static const char * fso = "Format status out";
5447     static const char * fso_sn = "format_status_out";
5448 
5449     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
5450         sgj_pr_hr(jsp, "%s  [0x8]\n", fslp);
5451     num = len - 4;
5452     bp = &resp[0] + 4;
5453     if (jsp->pr_as_json) {
5454         jo2p = sg_log_js_hdr(jsp, jop, fslp, resp);
5455         jap = sgj_named_subarray_r(jsp, jo2p, "format_status_log_parameters");
5456     }
5457 
5458 
5459     while (num > 3) {
5460         pc = sg_get_unaligned_be16(bp + 0);
5461         pl = bp[3] + 4;
5462         if (op->filter_given) {
5463             if (pc != op->filter)
5464                 goto skip;
5465         }
5466         if (op->do_raw) {
5467             dStrRaw(bp, pl);
5468             goto filter_chk;
5469         } else if (op->do_hex) {
5470             hex2stdout(bp, pl, op->dstrhex_no_ascii);
5471             goto filter_chk;
5472         }
5473         if (jsp->pr_as_json) {
5474             jo3p = sgj_new_unattached_object_r(jsp);
5475             if (op->do_pcb)
5476                 js_pcb(jsp, jo3p, bp[2]);
5477         }
5478         is_count = true;
5479 
5480         switch (pc) {
5481         case 0:
5482             is_not_avail = false;
5483             if (pl < 5)
5484                 sgj_pr_hr(jsp, "  %s: <empty>\n", fso);
5485             else {
5486                 if (sg_all_ffs(bp + 4, pl - 4)) {
5487                     sgj_pr_hr(jsp, "  %s: <%s>\n", fso, not_avail);
5488                     is_not_avail = true;
5489                 } else {
5490                     hex2str(bp + 4, pl - 4, "    ", op->hex2str_oformat,
5491                             sizeof(b), b);
5492                     sgj_pr_hr(jsp, "  %s:\n%s", fso, b);
5493 
5494 
5495                 }
5496             }
5497             if (jsp->pr_as_json) {
5498                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, fso);
5499                 if (is_not_avail)
5500                     sgj_js_nv_ihexstr(jsp, jo3p, fso_sn, 0, NULL, not_avail);
5501                 else
5502                     sgj_js_nv_hex_bytes(jsp, jo3p,  fso_sn, bp + 4, pl - 4);
5503             }
5504             is_count = false;
5505             break;
5506         case 1:
5507             cp = "Grown defects during certification";
5508             break;
5509         case 2:
5510             cp = "Total blocks reassigned during format";
5511             break;
5512         case 3:
5513             cp = "Total new blocks reassigned";
5514             break;
5515         case 4:
5516             cp = "Power on minutes since format";
5517             break;
5518         default:
5519             sgj_pr_hr(jsp, "  Unknown Format %s = 0x%x\n", param_c, pc);
5520             is_count = false;
5521             hex2fp(bp, pl, "    ", op->hex2str_oformat, stdout);
5522             if (jsp->pr_as_json) {
5523                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, unknown_s);
5524                 sgj_js_nv_hex_bytes(jsp, jo3p,  in_hex, bp, pl);
5525             }
5526             break;
5527         }
5528         if (is_count) {
5529             k = pl - 4;
5530             xp = bp + 4;
5531             is_not_avail = false;
5532             ull = 0;
5533             if (sg_all_ffs(xp, k)) {
5534                 sgj_pr_hr(jsp, "  %s: <%s>\n", cp, not_avail);
5535                 is_not_avail = true;
5536             } else {
5537                 if (k > (int)sizeof(ull)) {
5538                     xp += (k - sizeof(ull));
5539                     k = sizeof(ull);
5540                 }
5541                 ull = sg_get_unaligned_be(k, xp);
5542                 sgj_pr_hr(jsp, "  %s = %" PRIu64 "\n", cp, ull);
5543             }
5544             if (jsp->pr_as_json) {
5545                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, cp);
5546                 sgj_convert_to_snake_name(cp, b, sizeof(b));
5547                 if (is_not_avail)
5548                     sgj_js_nv_ihexstr(jsp, jo3p, b, 0, NULL, not_avail);
5549                 else
5550                     sgj_js_nv_ihex(jsp, jo3p, b, ull);
5551             }
5552         }
5553         if (jsp->pr_as_json)
5554             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
5555         if ((op->do_pcb) && (! op->do_name))
5556             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
5557                       sizeof(str)));
5558 filter_chk:
5559         if (op->filter_given)
5560             break;
5561 skip:
5562         num -= pl;
5563         bp += pl;
5564     }
5565     return true;
5566 }
5567 
5568 /* Non-volatile cache page [0x17] <nvc>  introduced: SBC-2
5569  * Standard vacillates between "non-volatile" and "nonvolatile" */
5570 static bool
show_non_volatile_cache_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)5571 show_non_volatile_cache_page(const uint8_t * resp, int len,
5572                              struct opts_t * op, sgj_opaque_p jop)
5573 {
5574     int j, num, pl, pc;
5575     const char * cp;
5576     const char * c2p;
5577     const uint8_t * bp;
5578     sgj_state * jsp = &op->json_st;
5579     sgj_opaque_p jo2p;
5580     sgj_opaque_p jo3p = NULL;
5581     sgj_opaque_p jap = NULL;
5582     char str[PCB_STR_LEN];
5583     char b[96];
5584     static const char * nvclp = "Non-volatile cache log page";
5585     static const char * ziinv = "0 (i.e. it is now volatile)";
5586     static const char * indef = "indefinite";
5587 
5588     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
5589          sgj_pr_hr(jsp, "%s  [0x17]\n", nvclp);
5590     num = len - 4;
5591     bp = &resp[0] + 4;
5592     if (jsp->pr_as_json) {
5593         jo2p = sg_log_js_hdr(jsp, jop, nvclp, resp);
5594         jap = sgj_named_subarray_r(jsp, jo2p,
5595                                    "nonvolatile_cache_log_parameters");
5596     }
5597 
5598     while (num > 3) {
5599         pc = sg_get_unaligned_be16(bp + 0);
5600         pl = bp[3] + 4;
5601         if (op->filter_given) {
5602             if (pc != op->filter)
5603                 goto skip;
5604         }
5605         if (op->do_raw) {
5606             dStrRaw(bp, pl);
5607             goto filter_chk;
5608         } else if (op->do_hex) {
5609             hex2stdout(bp, pl, op->dstrhex_no_ascii);
5610             goto filter_chk;
5611         }
5612         if (jsp->pr_as_json) {
5613             jo3p = sgj_new_unattached_object_r(jsp);
5614             if (op->do_pcb)
5615                 js_pcb(jsp, jo3p, bp[2]);
5616         }
5617 
5618         cp = NULL;
5619         switch (pc) {
5620         case 0:
5621             cp = "Remaining nonvolatile time";
5622             c2p = NULL;
5623             j = sg_get_unaligned_be24(bp + 5);
5624             switch (j) {
5625             case 0:
5626                 c2p = ziinv;
5627                 sgj_pr_hr(jsp, "  %s: %s\n", cp, c2p);
5628                 break;
5629             case 1:
5630                 c2p = unknown_s;
5631                 sgj_pr_hr(jsp, "  %s: <%s>\n", cp, c2p);
5632                 break;
5633             case 0xffffff:
5634                 c2p = indef;
5635                 sgj_pr_hr(jsp, "  %s: <%s>\n", cp, c2p);
5636                 break;
5637             default:
5638                 snprintf(b, sizeof(b), "%d minutes [%d:%d]", j, (j / 60),
5639                          (j % 60));
5640                 c2p = b;
5641                 sgj_pr_hr(jsp, "  %s: %s\n", cp, c2p);
5642                 break;
5643             }
5644             break;
5645         case 1:
5646             cp = "Maximum non-volatile time";
5647             c2p = NULL;
5648             j = sg_get_unaligned_be24(bp + 5);
5649             switch (j) {
5650             case 0:
5651                 c2p = ziinv;
5652                 sgj_pr_hr(jsp, "  %s: %s\n", cp, c2p);
5653                 break;
5654             case 1:
5655                 c2p = rsv_s;
5656                 sgj_pr_hr(jsp, "  %s: <%s>\n", cp, c2p);
5657                 break;
5658             case 0xffffff:
5659                 c2p = indef;
5660                 sgj_pr_hr(jsp, "  %s: <%s>\n", cp, c2p);
5661                 break;
5662             default:
5663                 snprintf(b, sizeof(b), "%d minutes [%d:%d]", j, (j / 60),
5664                          (j % 60));
5665                 c2p = b;
5666                 sgj_pr_hr(jsp, "  %s: %s\n", cp, c2p);
5667                 break;
5668             }
5669             break;
5670         default:
5671             sgj_pr_hr(jsp, "  Unknown %s = 0x%x\n", param_c, pc);
5672             hex2stdout(bp, pl, op->dstrhex_no_ascii);
5673             break;
5674         }
5675         if (jsp->pr_as_json) {
5676             sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
5677                               cp ? cp : unknown_s);
5678             if (cp)
5679                 js_snakenv_ihexstr_nex(jsp, jo3p, cp , j, true,
5680                                        NULL, c2p, NULL);
5681             else if (pl > 4)
5682                 sgj_js_nv_hex_bytes(jsp, jo3p, in_hex, bp + 4, pl - 4);
5683 
5684             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
5685         }
5686         if ((op->do_pcb) && (! op->do_name))
5687             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
5688                       sizeof(str)));
5689 filter_chk:
5690         if (op->filter_given)
5691             break;
5692 skip:
5693         num -= pl;
5694         bp += pl;
5695     }
5696     return true;
5697 }
5698 
5699 /* LB_PROV_LPAGE [0xc] <lbp> introduced: SBC-3 */
5700 static bool
show_lb_provisioning_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)5701 show_lb_provisioning_page(const uint8_t * resp, int len,
5702                           struct opts_t * op, sgj_opaque_p jop)
5703 {
5704     bool evsm_output = false;
5705     int num, pl, pc;
5706     const uint8_t * bp;
5707     const char * cp;
5708     char str[PCB_STR_LEN];
5709 
5710 if (jop) { };
5711     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
5712         printf("Logical block provisioning page  [0xc]\n");
5713     num = len - 4;
5714     bp = &resp[0] + 4;
5715     while (num > 3) {
5716         pc = sg_get_unaligned_be16(bp + 0);
5717         pl = bp[3] + 4;
5718         if (op->filter_given) {
5719             if (pc != op->filter)
5720                 goto skip;
5721         }
5722         if (op->do_raw) {
5723             dStrRaw(bp, pl);
5724             goto filter_chk;
5725         } else if (op->do_hex) {
5726             hex2stdout(bp, pl, op->dstrhex_no_ascii);
5727             goto filter_chk;
5728         }
5729         switch (pc) {
5730         case 0x1:
5731             cp = "  Available LBA mapping threshold";
5732             break;
5733         case 0x2:
5734             cp = "  Used LBA mapping threshold";
5735             break;
5736         case 0x3:
5737             cp = "  Available provisioning resource percentage";
5738             break;
5739         case 0x100:
5740             cp = "  De-duplicated LBA";
5741             break;
5742         case 0x101:
5743             cp = "  Compressed LBA";
5744             break;
5745         case 0x102:
5746             cp = "  Total efficiency LBA";
5747             break;
5748         default:
5749             cp = NULL;
5750             break;
5751         }
5752         if (cp) {
5753             if ((pl < 8) || (num < 8)) {
5754                 if (num < 8)
5755                     pr2serr("\n    truncated by response length, expected at "
5756                             "least 8 bytes\n");
5757                 else
5758                     pr2serr("\n    parameter length >= 8 expected, got %d\n",
5759                             pl);
5760                 break;
5761             }
5762             if (0x3 == pc)      /* resource percentage log parameter */
5763                 printf("  %s: %u %%\n", cp, sg_get_unaligned_be16(bp + 4));
5764             else                /* resource count log parameters */
5765                 printf("  %s resource count: %u\n", cp,
5766                        sg_get_unaligned_be32(bp + 4));
5767             if (pl > 8) {
5768                 switch (bp[8] & 0x3) {      /* SCOPE field */
5769                 case 0: cp = not_rep; break;
5770                 case 1: cp = "dedicated to lu"; break;
5771                 case 2: cp = "not dedicated to lu"; break;
5772                 case 3: cp = rsv_s; break;
5773                 }
5774                 printf("    Scope: %s\n", cp);
5775             }
5776         } else if ((pc >= 0xfff0) && (pc <= 0xffff)) {
5777             if (op->exclude_vendor) {
5778                 if ((op->verbose > 0) && (0 == op->do_brief) &&
5779                     (! evsm_output)) {
5780                     evsm_output = true;
5781                     printf("  %s parameter(s) being ignored\n", vend_spec);
5782                 }
5783             } else {
5784                 printf("  %s [0x%x]:", vend_spec, pc);
5785                 hex2stdout(bp, ((pl < num) ? pl : num), op->dstrhex_no_ascii);
5786             }
5787         } else {
5788             printf("  Reserved [%s=0x%x]:\n", param_c_sn, pc);
5789             hex2stdout(bp, ((pl < num) ? pl : num), op->dstrhex_no_ascii);
5790         }
5791         if (op->do_pcb)
5792             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
5793 filter_chk:
5794         if (op->filter_given)
5795             break;
5796 skip:
5797         num -= pl;
5798         bp += pl;
5799     }
5800     return true;
5801 }
5802 
5803 /* UTILIZATION_SUBPG [0xe,0x1] <util>  introduced: SBC-4 */
5804 static bool
show_utilization_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)5805 show_utilization_page(const uint8_t * resp, int len, struct opts_t * op,
5806                       sgj_opaque_p jop)
5807 {
5808     int num, pl, pc, k;
5809     const uint8_t * bp;
5810     char str[PCB_STR_LEN];
5811 
5812 if (jop) { };
5813     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
5814         printf("Utilization page  [0xe,0x1]\n");
5815     num = len - 4;
5816     bp = &resp[0] + 4;
5817     while (num > 3) {
5818         pc = sg_get_unaligned_be16(bp + 0);
5819         pl = bp[3] + 4;
5820         if (op->filter_given) {
5821             if (pc != op->filter)
5822                 goto skip;
5823         }
5824         if (op->do_raw) {
5825             dStrRaw(bp, pl);
5826             goto filter_chk;
5827         } else if (op->do_hex) {
5828             hex2stdout(bp, pl, op->dstrhex_no_ascii);
5829             goto filter_chk;
5830         }
5831         switch (pc) {
5832         case 0x0:
5833             printf("  Workload utilization:");
5834             if ((pl < 6) || (num < 6)) {
5835                 if (num < 6)
5836                     pr2serr("\n    truncated by response length, expected "
5837                             "at least 6 bytes\n");
5838                 else
5839                     pr2serr("\n    parameter length >= 6 expected, got %d\n",
5840                             pl);
5841                 break;
5842             }
5843             k = sg_get_unaligned_be16(bp + 4);
5844             printf(" %d.%02d %%\n", k / 100, k % 100);
5845             break;
5846         case 0x1:
5847             printf("  Utilization usage rate based on date and time:");
5848             if ((pl < 6) || (num < 6)) {
5849                 if (num < 6)
5850                     pr2serr("\n    truncated by response length, expected "
5851                             "at least 6 bytes\n");
5852                 else
5853                     pr2serr("\n    parameter length >= 6 expected, got %d\n",
5854                             pl);
5855                 break;
5856             }
5857             printf(" %d %%\n", bp[4]);
5858             break;
5859         default:
5860             printf("  Reserved [parameter_code=0x%x]:\n", pc);
5861             hex2stdout(bp, ((pl < num) ? pl : num), op->dstrhex_no_ascii);
5862             break;
5863         }
5864         if (op->do_pcb)
5865             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
5866 filter_chk:
5867         if (op->filter_given)
5868             break;
5869 skip:
5870         num -= pl;
5871         bp += pl;
5872     }
5873     return true;
5874 }
5875 
5876 /* SOLID_STATE_MEDIA_LPAGE [0x11] <ssm>  introduced: SBC-3 */
5877 static bool
show_solid_state_media_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)5878 show_solid_state_media_page(const uint8_t * resp, int len,
5879                             struct opts_t * op, sgj_opaque_p jop)
5880 {
5881     int num, pl, pc;
5882     const uint8_t * bp;
5883     sgj_state * jsp = &op->json_st;
5884     sgj_opaque_p jo2p;
5885     sgj_opaque_p jo3p = NULL;
5886     sgj_opaque_p jap = NULL;
5887     char str[PCB_STR_LEN];
5888     static const char * ssmlp = "Solid state media log page";
5889     static const char * puei = "Percentage used endurance indicator";
5890 
5891     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
5892         sgj_pr_hr(jsp, "%s  [0x11]\n", ssmlp);
5893     if (jsp->pr_as_json) {
5894         jo2p = sg_log_js_hdr(jsp, jop, ssmlp, resp);
5895         jap = sgj_named_subarray_r(jsp, jo2p,
5896                            "solid_state_media_log_parameters");
5897     }
5898     num = len - 4;
5899     bp = &resp[0] + 4;
5900     while (num > 3) {
5901         pc = sg_get_unaligned_be16(bp + 0);
5902         pl = bp[3] + 4;
5903         if (op->filter_given) {
5904             if (pc != op->filter)
5905                 goto skip;
5906         }
5907         if (op->do_raw) {
5908             dStrRaw(bp, pl);
5909             goto filter_chk;
5910         } else if (op->do_hex) {
5911             hex2stdout(bp, pl, op->dstrhex_no_ascii);
5912             goto filter_chk;
5913         }
5914         if (jsp->pr_as_json) {
5915             jo3p = sgj_new_unattached_object_r(jsp);
5916             if (op->do_pcb)
5917                 js_pcb(jsp, jo3p, bp[2]);
5918         }
5919 
5920         switch (pc) {
5921         case 0x1:
5922             if ((pl < 8) || (num < 8)) {
5923                 if (num < 8)
5924                     pr2serr("\n    truncated by response length, expected "
5925                             "at least 8 bytes\n");
5926                 else
5927                     pr2serr("\n    parameter length >= 8 expected, got %d\n",
5928                             pl);
5929                 break;
5930             }
5931             sgj_pr_hr(jsp, "  %s: %u %%\n", puei, bp[7]);
5932             if (jsp->pr_as_json) {
5933                 js_snakenv_ihexstr_nex(jsp, jo3p, param_c, pc, true,
5934                                        NULL, puei, NULL);
5935                 js_snakenv_ihexstr_nex(jsp, jo3p, puei, bp[7], false,
5936                                        NULL, NULL, NULL);
5937             }
5938             break;
5939         default:
5940             printf("  Reserved [parameter_code=0x%x]:\n", pc);
5941             hex2stdout(bp, ((pl < num) ? pl : num), op->dstrhex_no_ascii);
5942             break;
5943         }
5944         if (jsp->pr_as_json)
5945             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
5946         if (op->do_pcb)
5947             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
5948                       sizeof(str)));
5949 filter_chk:
5950         if (op->filter_given)
5951             break;
5952 skip:
5953         num -= pl;
5954         bp += pl;
5955     }
5956     return true;
5957 }
5958 
5959 static const char * dt_dev_activity[] = {
5960     "No DT device activity",
5961     "Cleaning operation in progress",
5962     "Volume is being loaded",
5963     "Volume is being unloaded",
5964     "Other medium activity",
5965     "Reading from medium",
5966     "Writing to medium",
5967     "Locating medium",
5968     "Rewinding medium", /* 8 */
5969     "Erasing volume",
5970     "Formatting volume",
5971     "Calibrating",
5972     "Other DT device activity",
5973     "Microcode update in progress",
5974     "Reading encrypted from medium",
5975     "Writing encrypted to medium",
5976     "Diagnostic operation in progress", /* 10 */
5977 };
5978 
5979 /* DT device status [0x11] <dtds> (ssc, adc) */
5980 static bool
show_dt_device_status_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)5981 show_dt_device_status_page(const uint8_t * resp, int len,
5982                            struct opts_t * op, sgj_opaque_p jop)
5983 {
5984     bool evsm_output = false;
5985     int num, pl, pc, j;
5986     const uint8_t * bp;
5987     char str[PCB_STR_LEN];
5988     char b[512];
5989 
5990 if (jop) { };
5991     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
5992         printf("DT device status page (ssc-3, adc-3) [0x11]\n");
5993     num = len - 4;
5994     bp = &resp[0] + 4;
5995     while (num > 3) {
5996         pc = sg_get_unaligned_be16(bp + 0);
5997         pl = bp[3] + 4;
5998         if (op->filter_given) {
5999             if (pc != op->filter)
6000                 goto skip;
6001         }
6002         if (op->do_raw) {
6003             dStrRaw(bp, pl);
6004             goto filter_chk;
6005         } else if (op->do_hex) {
6006             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6007             goto filter_chk;
6008         }
6009         switch (pc) {
6010         case 0x0:
6011             printf("  Very high frequency data:\n");
6012             if ((pl < 8) || (num < 8)) {
6013                 if (num < 8)
6014                     pr2serr("    truncated by response length, expected at "
6015                             "least 8 bytes\n");
6016                 else
6017                     pr2serr("    parameter length >= 8 expected, got %d\n",
6018                             pl);
6019                 break;
6020             }
6021             printf("  PAMR=%d HUI=%d MACC=%d CMPR=%d ", !!(0x80 & bp[4]),
6022                    !!(0x40 & bp[4]), !!(0x20 & bp[4]), !!(0x10 & bp[4]));
6023             printf("WRTP=%d CRQST=%d CRQRD=%d DINIT=%d\n", !!(0x8 & bp[4]),
6024                    !!(0x4 & bp[4]), !!(0x2 & bp[4]), !!(0x1 & bp[4]));
6025             printf("  INXTN=%d RAA=%d MPRSNT=%d ", !!(0x80 & bp[5]),
6026                    !!(0x20 & bp[5]), !!(0x10 & bp[5]));
6027             printf("MSTD=%d MTHRD=%d MOUNTED=%d\n",
6028                    !!(0x4 & bp[5]), !!(0x2 & bp[5]), !!(0x1 & bp[5]));
6029             printf("  DT device activity: ");
6030             j = bp[6];
6031             if (j < (int)SG_ARRAY_SIZE(dt_dev_activity))
6032                 printf("%s\n", dt_dev_activity[j]);
6033             else if (j < 0x80)
6034                 printf("Reserved [0x%x]\n", j);
6035             else
6036                 printf("%s [0x%x]\n", vend_spec, j);
6037             printf("  VS=%d TDDEC=%d EPP=%d ", !!(0x80 & bp[7]),
6038                    !!(0x20 & bp[7]), !!(0x10 & bp[7]));
6039             printf("ESR=%d RRQST=%d INTFC=%d TAFC=%d\n", !!(0x8 & bp[7]),
6040                    !!(0x4 & bp[7]), !!(0x2 & bp[7]), !!(0x1 & bp[7]));
6041             break;
6042         case 0x1:
6043             printf("  Very high frequency polling delay: ");
6044             if ((pl < 6) || (num < 6)) {
6045                 if (num < 6)
6046                     pr2serr("\n    truncated by response length, expected at "
6047                             "least 6 bytes\n");
6048                 else
6049                     pr2serr("\n    parameter length >= 6 expected, got %d\n",
6050                             pl);
6051                 break;
6052             }
6053             printf(" %d milliseconds\n", sg_get_unaligned_be16(bp + 4));
6054             break;
6055         case 0x2:
6056             printf("   DT device ADC data encryption control status (hex "
6057                    "only now):\n");
6058             if ((pl < 12) || (num < 12)) {
6059                 if (num < 12)
6060                     pr2serr("    truncated by response length, expected at "
6061                             "least 12 bytes\n");
6062                 else
6063                     pr2serr("    parameter length >= 12 expected, got %d\n",
6064                             pl);
6065                 break;
6066             }
6067             hex2fp(bp + 4, 8, "      ", op->hex2str_oformat, stdout);
6068             break;
6069         case 0x3:
6070             printf("   Key management error data (hex only now):\n");
6071             if ((pl < 16) || (num < 16)) {
6072                 if (num < 16)
6073                     pr2serr("    truncated by response length, expected at "
6074                             "least 16 bytes\n");
6075                 else
6076                     pr2serr("    parameter length >= 16 expected, got %d\n",
6077                             pl);
6078                 break;
6079             }
6080             hex2fp(bp + 4, 12, "      ", op->hex2str_oformat, stdout);
6081             break;
6082         default:
6083             if ((pc >= 0x101) && (pc <= 0x1ff)) {
6084                 printf("  Primary port %d status:\n", pc - 0x100);
6085                 if (12 == bp[3]) { /* if length of desc is 12, assume SAS */
6086                     printf("    SAS: negotiated physical link rate: %s\n",
6087                            sas_negot_link_rate((0xf & (bp[4] >> 4)), b,
6088                                                sizeof(b)));
6089                     printf("    signal=%d, pic=%d, ", !!(0x2 & bp[4]),
6090                            !!(0x1 & bp[4]));
6091                     printf("hashed SAS addr: 0x%u\n",
6092                             sg_get_unaligned_be24(bp + 5));
6093                     printf("    SAS addr: 0x%" PRIx64 "\n",
6094                             sg_get_unaligned_be64(bp + 8));
6095                 } else {
6096                     printf("    non-SAS transport, in hex:\n");
6097                     hex2fp(bp + 4, ((pl < num) ? pl : num) - 4, "      ",
6098                            op->hex2str_oformat, stdout);
6099                 }
6100             } else if (pc >= 0x8000) {
6101                 if (op->exclude_vendor) {
6102                     if ((op->verbose > 0) && (0 == op->do_brief) &&
6103                         (! evsm_output)) {
6104                         evsm_output = true;
6105                         printf("  %s parameter(s) being ignored\n",
6106                                vend_spec);
6107                     }
6108                 } else {
6109                     printf("  %s [%s=0x%x]:\n", vend_spec, param_c_sn, pc);
6110                     hex2fp(bp, ((pl < num) ? pl : num), "    ",
6111                            op->hex2str_oformat, stdout);
6112                 }
6113             } else {
6114                 printf("  Reserved [%s=0x%x]:\n", param_c_sn, pc);
6115                 hex2fp(bp, ((pl < num) ? pl : num), "    ",
6116                         op->hex2str_oformat, stdout);
6117             }
6118             break;
6119         }
6120         if (op->do_pcb)
6121             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
6122 filter_chk:
6123         if (op->filter_given)
6124             break;
6125 skip:
6126         num -= pl;
6127         bp += pl;
6128     }
6129     return true;
6130 }
6131 
6132 /* TapeAlert response [0x12] <tar> (adc,ssc) */
6133 static bool
show_tapealert_response_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)6134 show_tapealert_response_page(const uint8_t * resp, int len,
6135                              struct opts_t * op, sgj_opaque_p jop)
6136 {
6137     bool evsm_output = false;
6138     int num, pl, pc, k, mod, div;
6139     const uint8_t * bp;
6140     char str[PCB_STR_LEN];
6141 
6142 if (jop) { };
6143     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
6144         printf("TapeAlert response page (ssc-3, adc-3) [0x12]\n");
6145     num = len - 4;
6146     bp = &resp[0] + 4;
6147     while (num > 3) {
6148         pc = sg_get_unaligned_be16(bp + 0);
6149         pl = bp[3] + 4;
6150         if (op->filter_given) {
6151             if (pc != op->filter)
6152                 goto skip;
6153         }
6154         if (op->do_raw) {
6155             dStrRaw(bp, pl);
6156             goto filter_chk;
6157         } else if (op->do_hex) {
6158             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6159             goto filter_chk;
6160         }
6161         switch (pc) {
6162         case 0x0:
6163             if (pl < 12) {
6164 
6165             }
6166             for (k = 1; k < 0x41; ++k) {
6167                 mod = ((k - 1) % 8);
6168                 div = (k - 1) / 8;
6169                 if (0 == mod) {
6170                     if (div > 0)
6171                         printf("\n");
6172                     printf("  Flag%02Xh: %d", k, !! (bp[4 + div] & 0x80));
6173                 } else
6174                     printf("  %02Xh: %d", k,
6175                            !! (bp[4 + div] & (1 << (7 - mod))));
6176             }
6177             printf("\n");
6178             break;
6179         default:
6180             if (pc <= 0x8000) {
6181                 printf("  Reserved [parameter_code=0x%x]:\n", pc);
6182                 hex2fp(bp, ((pl < num) ? pl : num), "    ",
6183                         op->hex2str_oformat, stdout);
6184             } else {
6185                 if (op->exclude_vendor) {
6186                     if ((op->verbose > 0) && (0 == op->do_brief) &&
6187                         (! evsm_output)) {
6188                         evsm_output = true;
6189                         printf("  %s parameter(s) being ignored\n",
6190                                vend_spec);
6191                     }
6192                 } else {
6193                     printf("  %s [%s=0x%x]:\n", vend_spec, param_c_sn, pc);
6194                     hex2fp(bp, ((pl < num) ? pl : num), "    ",
6195                            op->hex2str_oformat, stdout);
6196                 }
6197             }
6198             break;
6199         }
6200         if (op->do_pcb)
6201             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
6202 filter_chk:
6203         if (op->filter_given)
6204             break;
6205 skip:
6206         num -= pl;
6207         bp += pl;
6208     }
6209     return true;
6210 }
6211 
6212 #define NUM_REQ_REC_ARR_ELEMS 16
6213 static const char * req_rec_arr[NUM_REQ_REC_ARR_ELEMS] = {
6214     "Recovery not requested",
6215     "Recovery requested, no recovery procedure defined",
6216     "Instruct operator to push volume",
6217     "Instruct operator to remove and re-insert volume",
6218     "Issue UNLOAD command. Instruct operator to remove and re-insert volume",
6219     "Instruct operator to power cycle target device",
6220     "Issue LOAD command",
6221     "Issue UNLOAD command",
6222     "Issue LOGICAL UNIT RESET task management function",        /* 0x8 */
6223     "No recovery procedure defined. Contact service organization",
6224     "Issue UNLOAD command. Instruct operator to remove and quarantine "
6225         "volume",
6226     "Instruct operator to not insert a volume. Contact service organization",
6227     "Issue UNLOAD command. Instruct operator to remove volume. Contact "
6228         "service organization",
6229     "Request creation of target device error log",
6230     "Retrieve a target device error log",
6231     "Modify configuration to all microcode update and instruct operator to "
6232         "re-insert volume",     /* 0xf */
6233 };
6234 
6235 /* REQ_RECOVERY_LPAGE Requested recovery [0x13] <rr> (ssc) */
6236 static bool
show_requested_recovery_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)6237 show_requested_recovery_page(const uint8_t * resp, int len,
6238                              struct opts_t * op, sgj_opaque_p jop)
6239 {
6240     bool evsm_output = false;
6241     int num, pl, pc, j, k;
6242     const uint8_t * bp;
6243     char str[PCB_STR_LEN];
6244 
6245 if (jop) { };
6246     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
6247         printf("Requested recovery page (ssc-3) [0x13]\n");
6248     num = len - 4;
6249     bp = &resp[0] + 4;
6250     while (num > 3) {
6251         pc = sg_get_unaligned_be16(bp + 0);
6252         pl = bp[3] + 4;
6253         if (op->filter_given) {
6254             if (pc != op->filter)
6255                 goto skip;
6256         }
6257         if (op->do_raw) {
6258             dStrRaw(bp, pl);
6259             goto filter_chk;
6260         } else if (op->do_hex) {
6261             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6262             goto filter_chk;
6263         }
6264         switch (pc) {
6265         case 0x0:
6266             printf("  Recovery procedures:\n");
6267             for (k = 4; k < pl; ++ k) {
6268                 j = bp[k];
6269                 if (j < NUM_REQ_REC_ARR_ELEMS)
6270                     printf("    %s\n", req_rec_arr[j]);
6271                 else if (j < 0x80)
6272                     printf("    Reserved [0x%x]\n", j);
6273                 else
6274                     printf("    Vendor specific [0x%x]\n", j);
6275             }
6276             break;
6277         default:
6278             if (pc <= 0x8000) {
6279                 printf("  Reserved [parameter_code=0x%x]:\n", pc);
6280                 hex2fp(bp, ((pl < num) ? pl : num), "    ",
6281                         op->hex2str_oformat, stdout);
6282             } else {
6283                 if (op->exclude_vendor) {
6284                     if ((op->verbose > 0) && (0 == op->do_brief) &&
6285                         (! evsm_output)) {
6286                         evsm_output = true;
6287                         printf("  Vendor specific parameter(s) being "
6288                                "ignored\n");
6289                     }
6290                 } else {
6291                     printf("  Vendor specific [parameter_code=0x%x]:\n", pc);
6292                     hex2fp(bp, ((pl < num) ? pl : num), "    ",
6293                             op->hex2str_oformat, stdout);
6294                 }
6295             }
6296             break;
6297         }
6298         if (op->do_pcb)
6299             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
6300 filter_chk:
6301         if (op->filter_given)
6302             break;
6303 skip:
6304         num -= pl;
6305         bp += pl;
6306     }
6307     return true;
6308 }
6309 
6310 /* SAT_ATA_RESULTS_LPAGE (SAT-2) [0x16] <aptr> */
6311 static bool
show_ata_pt_results_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)6312 show_ata_pt_results_page(const uint8_t * resp, int len,
6313                          struct opts_t * op, sgj_opaque_p jop)
6314 {
6315     int num, pl, pc;
6316     const uint8_t * bp;
6317     const uint8_t * dp;
6318     char str[PCB_STR_LEN];
6319 
6320 if (jop) { };
6321     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
6322         printf("ATA pass-through results page (sat-2) [0x16]\n");
6323     num = len - 4;
6324     bp = &resp[0] + 4;
6325     while (num > 3) {
6326         pc = sg_get_unaligned_be16(bp + 0);
6327         pl = bp[3] + 4;
6328         if (op->filter_given) {
6329             if (pc != op->filter)
6330                 goto skip;
6331         }
6332         if (op->do_raw) {
6333             dStrRaw(bp, pl);
6334             goto filter_chk;
6335         } else if (op->do_hex) {
6336             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6337             goto filter_chk;
6338         }
6339         if ((pc < 0xf) && (pl > 17)) {
6340             int extend, count;
6341 
6342             printf("  Log_index=0x%x (parameter_code=0x%x)\n", pc + 1, pc);
6343             dp = bp + 4;       /* dp is start of ATA Return descriptor
6344                                  * which is 14 bytes long */
6345             extend = dp[2] & 1;
6346             count = dp[5] + (extend ? (dp[4] << 8) : 0);
6347             printf("    extend=%d  error=0x%x count=0x%x\n", extend,
6348                    dp[3], count);
6349             if (extend)
6350                 printf("    lba=0x%02x%02x%02x%02x%02x%02x\n", dp[10], dp[8],
6351                        dp[6], dp[11], dp[9], dp[7]);
6352             else
6353                 printf("    lba=0x%02x%02x%02x\n", dp[11], dp[9], dp[7]);
6354             printf("    device=0x%x  status=0x%x\n", dp[12], dp[13]);
6355         } else if (pl > 17) {
6356             printf("  Reserved [parameter_code=0x%x]:\n", pc);
6357             hex2fp(bp, ((pl < num) ? pl : num), "    ",
6358                    op->hex2str_oformat, stdout);
6359         } else {
6360             printf("  short parameter length: %d [parameter_code=0x%x]:\n",
6361                    pl, pc);
6362             hex2fp(bp, ((pl < num) ? pl : num), "    ",
6363                    op->hex2str_oformat, stdout);
6364         }
6365         if (op->do_pcb)
6366             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
6367 filter_chk:
6368         if (op->filter_given)
6369             break;
6370 skip:
6371         num -= pl;
6372         bp += pl;
6373     }
6374     return true;
6375 }
6376 
6377 static const char * bms_status[] = {
6378     "no background scans active",
6379     "background medium scan is active",
6380     "background pre-scan is active",
6381     "background scan halted due to fatal error",
6382     "background scan halted due to a vendor specific pattern of error",
6383     "background scan halted due to medium formatted without P-List",
6384     "background scan halted - vendor specific cause",
6385     "background scan halted due to temperature out of range",
6386     ("background scan enabled, none active (waiting for BMS interval timer "
6387         "to expire)"),  /* clang warns about this, add parens to quieten */
6388     "background scan halted - scan results list full",
6389     "background scan halted - pre-scan time limit timer expired" /* 10 */,
6390 };
6391 
6392 static const char * reassign_status[] = {
6393     "Reserved [0x0]",
6394     "Reassignment pending receipt of Reassign or Write command",
6395     "Logical block successfully reassigned by device server",
6396     "Reserved [0x3]",
6397     "Reassignment by device server failed",
6398     "Logical block recovered by device server via rewrite",
6399     "Logical block reassigned by application client, has valid data",
6400     "Logical block reassigned by application client, contains no valid data",
6401     "Logical block unsuccessfully reassigned by application client", /* 8 */
6402 };
6403 
6404 /* Background scan results [0x15,0] <bsr> for disk  introduced: SBC-3 */
6405 static bool
show_background_scan_results_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)6406 show_background_scan_results_page(const uint8_t * resp, int len,
6407                                   struct opts_t * op, sgj_opaque_p jop)
6408 {
6409     bool skip_out = false;
6410     bool evsm_output = false;
6411     bool ok;
6412     int j, m, n, num, pl, pc;
6413     const uint8_t * bp;
6414     double d;
6415     sgj_state * jsp = &op->json_st;
6416     sgj_opaque_p jo2p;
6417     sgj_opaque_p jo3p = NULL;
6418     sgj_opaque_p jap = NULL;
6419     char str[PCB_STR_LEN];
6420     char b[144];
6421     char e[80];
6422     static const int blen = sizeof(b);
6423     static const int elen = sizeof(e);
6424     static const char * bsrlp = "Background scan results log page";
6425     static const char * bss = "Background scan status";
6426     static const char * bms = "Background medium scan";
6427     static const char * bsr = "Background scan results";
6428     static const char * bs = "background scan";
6429     static const char * ms = "Medium scan";
6430     static const char * apom = "Accumulated power on minutes";
6431     static const char * rs = "Reassign status";
6432 
6433     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
6434         sgj_pr_hr(jsp, "%s  [0x15]\n", bsrlp);
6435     num = len - 4;
6436     bp = &resp[0] + 4;
6437     if (jsp->pr_as_json) {
6438         jo2p = sg_log_js_hdr(jsp, jop, bsrlp, resp);
6439         jap = sgj_named_subarray_r(jsp, jo2p, "background_scan_parameters");
6440     }
6441 
6442     while (num > 3) {
6443         pc = sg_get_unaligned_be16(bp + 0);
6444         pl = bp[3] + 4;
6445         if (op->filter_given) {
6446             if (pc != op->filter)
6447                 goto skip;
6448         }
6449         if (op->do_raw) {
6450             dStrRaw(bp, pl);
6451             goto filter_chk;
6452         } else if (op->do_hex) {
6453             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6454             goto filter_chk;
6455         }
6456         if (jsp->pr_as_json) {
6457             jo3p = sgj_new_unattached_object_r(jsp);
6458             if (op->do_pcb)
6459                 js_pcb(jsp, jo3p, bp[2]);
6460         }
6461 
6462         switch (pc) {
6463         case 0:
6464             sgj_pr_hr(jsp, "  Status parameters:\n");
6465             if (jsp->pr_as_json)
6466                 sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, bss);
6467             if ((pl < 16) || (num < 16)) {
6468                 if (num < 16)
6469                     pr2serr("    truncated by response length, expected at "
6470                             "least 16 bytes\n");
6471                 else
6472                     pr2serr("    parameter length >= 16 expected, got %d\n",
6473                             pl);
6474                 break;
6475             }
6476             sg_scnpr(b, blen, "    %s: ", apom);
6477             j = sg_get_unaligned_be32(bp + 4);
6478             sgj_pr_hr(jsp, "%s%d [h:m  %d:%d]\n", b, j, (j / 60), (j % 60));
6479             if (jsp->pr_as_json)
6480                 js_snakenv_ihexstr_nex(jsp, jo3p, apom, j, false, NULL, NULL,
6481                                        NULL);
6482             sg_scnpr(b, blen, "    Status: ");
6483             j = bp[9];
6484             ok = (j < (int)SG_ARRAY_SIZE(bms_status));
6485             if (ok)
6486                 sgj_pr_hr(jsp, "%s%s\n", b, bms_status[j]);
6487             else
6488                 sgj_pr_hr(jsp, "%sunknown [0x%x] %s value\n", b, j, bss);
6489             if (jsp->pr_as_json)
6490                 js_snakenv_ihexstr_nex(jsp, jo3p, bss, j, true, NULL,
6491                                        ok ? bms_status[j] : unknown_s, NULL);
6492             j = sg_get_unaligned_be16(bp + 10);
6493             snprintf(b, blen, "Number of %ss performed", bs);
6494             sgj_pr_hr(jsp, "    %s: %d\n", b, j);
6495             if (jsp->pr_as_json)
6496                 js_snakenv_ihexstr_nex(jsp, jo3p, b, j, true, NULL, NULL,
6497                                        NULL);
6498             j = sg_get_unaligned_be16(bp + 12);
6499             snprintf(b, blen, "%s progress", bms);
6500             d = (100.0 * j / 65536.0);
6501 #ifdef SG_LIB_MINGW
6502             snprintf(e, elen, "%g %%", d);
6503 #else
6504             snprintf(e, elen, "%.2f %%", d);
6505 #endif
6506             sgj_pr_hr(jsp, "    %s: %s\n", b, e);
6507             if (jsp->pr_as_json)
6508                 js_snakenv_ihexstr_nex(jsp, jo3p, b, j, true, NULL, e,
6509                                        NULL);
6510             j = sg_get_unaligned_be16(bp + 14);
6511             snprintf(b, blen, "Number of %ss performed", bms);
6512 
6513             ok = (j > 0);
6514             if (ok)
6515                 sgj_pr_hr(jsp, "    %s: %d\n", b, j);
6516             else
6517                 sgj_pr_hr(jsp, "    %s: 0 [%s]\n", b, not_rep);
6518             if (jsp->pr_as_json)
6519                 js_snakenv_ihexstr_nex(jsp, jo3p, b, j, true, NULL,
6520                                        ok ? NULL : not_rep, NULL);
6521             break;
6522         default:
6523             if (pc > 0x800) {
6524                 if (jsp->pr_as_json)
6525                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL,
6526                                       (pc >= 0x8000) ? vend_spec : NULL);
6527                 if ((pc >= 0x8000) && (pc <= 0xafff)) {
6528                     if (op->exclude_vendor) {
6529                         skip_out = true;
6530                         if ((op->verbose > 0) && (0 == op->do_brief) &&
6531                             (! evsm_output)) {
6532                             evsm_output = true;
6533                             sgj_pr_hr(jsp, "  %s parameter(s) being "
6534                                       "ignored\n", vend_spec);
6535                         }
6536                     } else
6537                         sgj_pr_hr(jsp, "  %s parameter # %d [0x%x], %s\n",
6538                                   ms, pc, pc, vend_spec);
6539                 } else
6540                     sgj_pr_hr(jsp, "  %s parameter # %d [0x%x], %s\n", ms, pc,
6541                               pc, rsv_s);
6542                 if (skip_out)
6543                     skip_out = false;
6544                 else
6545                     hex2fp(bp, ((pl < num) ? pl : num), "    ",
6546                            op->hex2str_oformat, stdout);
6547                 break;
6548             } else {
6549                 sgj_pr_hr(jsp, "  %s parameter # %d [0x%x]\n", ms, pc, pc);
6550                 if (jsp->pr_as_json)
6551                     sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, bsr);
6552             }
6553             if ((pl < 24) || (num < 24)) {
6554                 if (num < 24)
6555                     pr2serr("    truncated by response length, expected at "
6556                             "least 24 bytes\n");
6557                 else
6558                     pr2serr("    parameter length >= 24 expected, got %d\n",
6559                             pl);
6560                 break;
6561             }
6562             j = sg_get_unaligned_be32(bp + 4);
6563             n = (j % 60);
6564             sgj_pr_hr(jsp, "    %s when error detected: %d [%d:%d]\n", apom,
6565                       j, (j / 60), n);
6566             if (jsp->pr_as_json) {
6567                 snprintf(b, blen, "%d hours, %d minute%s", (j / 60), n,
6568                          n != 1 ? "s" : "");
6569                 js_snakenv_ihexstr_nex(jsp, jo3p, apom, j, true, NULL, b,
6570                                        "when error detected [unit: minute]");
6571             }
6572             j = (bp[8] >> 4) & 0xf;
6573             ok = (j < (int)SG_ARRAY_SIZE(reassign_status));
6574             if (ok)
6575                 sgj_pr_hr(jsp, "    %s: %s\n", rs, reassign_status[j]);
6576             else
6577                 sgj_pr_hr(jsp, "    %s: %s [0x%x]\n", rs, rsv_s, j);
6578             if (jsp->pr_as_json)
6579                 js_snakenv_ihexstr_nex(jsp, jo3p, rs, j, true, NULL,
6580                                        ok ? reassign_status[j] : NULL, NULL);
6581             n = 0xf & b[8];
6582             sgj_pr_hr(jsp, "    %s: %s  [sk,asc,ascq: 0x%x,0x%x,0x%x]\n",
6583                       s_key, sg_get_sense_key_str(n, blen, b), n, bp[9],
6584                                                   bp[10]);
6585             if (bp[9] || bp[10])
6586                 sgj_pr_hr(jsp, "      %s\n",
6587                           sg_get_asc_ascq_str(bp[9], bp[10], blen, b));
6588             if (jsp->pr_as_json) {
6589                 sgj_js_nv_ihexstr(jsp, jo3p, "sense_key", n, NULL,
6590                                   sg_get_sense_key_str(n, blen, b));
6591                 sgj_js_nv_ihexstr(jsp, jo3p, "additional_sense_code", bp[9],
6592                                   NULL, NULL);
6593                 sgj_js_nv_ihexstr(jsp, jo3p, "additional_sense_code_qualifier",
6594                                   bp[10], NULL, sg_get_asc_ascq_str(bp[9],
6595                                   bp[10], blen, b));
6596             }
6597             if (op->verbose) {
6598                 n = sg_scnpr(b, blen, "    vendor bytes [11 -> 15]: ");
6599                 for (m = 0; m < 5; ++m)
6600                     n += sg_scnpr(b + n, blen - n, "0x%02x ", bp[11 + m]);
6601                  sgj_pr_hr(jsp, "%s\n", b);
6602             }
6603             n = sg_scnpr(b, blen, "    LBA (associated with medium error): "
6604                          "0x");
6605             if (sg_all_zeros(bp + 16, 8))
6606                 sgj_pr_hr(jsp, "%s0\n", b);
6607             else {
6608                 for (m = 0; m < 8; ++m)
6609                     n += sg_scnpr(b + n, blen - n, "%02x", bp[16 + m]);
6610                 sgj_pr_hr(jsp, "%s\n", b);
6611             }
6612             if (jsp->pr_as_json)
6613                 js_snakenv_ihexstr_nex(jsp, jo3p, "logical_block_address",
6614                                        sg_get_unaligned_be64(bp + 16), true,
6615                                        NULL, NULL, "of medium error");
6616             break;
6617         }               /* end of switch statement block */
6618         if (jsp->pr_as_json)
6619             sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
6620         if (op->do_pcb)
6621             sgj_pr_hr(jsp, "        <%s>\n", get_pcb_str(bp[2], str,
6622                       sizeof(str)));
6623 filter_chk:
6624         if (op->filter_given)
6625             break;
6626 skip:
6627         num -= pl;
6628         bp += pl;
6629     }
6630     return true;
6631 }
6632 
6633 /* ZONED_BLOCK_DEV_STATS_SUBPG [0x14,0x1] <zbds>  introduced: zbc2r01 */
6634 static bool
show_zoned_block_dev_stats(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)6635 show_zoned_block_dev_stats(const uint8_t * resp, int len,
6636                            struct opts_t * op, sgj_opaque_p jop)
6637 {
6638     bool trunc, bad_pl;
6639     int num, pl, pc;
6640     const uint8_t * bp;
6641     char str[PCB_STR_LEN];
6642 
6643 if (jop) { };
6644     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
6645         printf("Zoned block device statistics page  [0x14,0x1]\n");
6646     num = len - 4;
6647     bp = &resp[0] + 4;
6648     while (num > 3) {
6649         trunc = false;
6650         bad_pl = false;
6651         pc = sg_get_unaligned_be16(bp + 0);
6652         pl = bp[3] + 4;
6653         if (4 == pl)    /* DC HC560 has empty descriptors */
6654             goto skip;
6655         if (op->filter_given) {
6656             if (pc != op->filter)
6657                 goto skip;
6658         }
6659         if (op->do_raw) {
6660             dStrRaw(bp, pl);
6661             goto filter_chk;
6662         } else if (op->do_hex) {
6663             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6664             goto filter_chk;
6665         }
6666         switch (pc) {
6667         case 0x0:
6668             if ((pl < 8) || (num < 8)) {
6669                 if (num < 8)
6670                     trunc = true;
6671                 else
6672                     bad_pl = true;
6673             } else
6674                 printf("  Maximum open zones: %" PRIu32 "\n",
6675                        sg_get_unaligned_be32(bp + 8));
6676             break;
6677         case 0x1:
6678             if ((pl < 8) || (num < 8)) {
6679                 if (num < 8)
6680                     trunc = true;
6681                 else
6682                     bad_pl = true;
6683             } else
6684                 printf("  Maximum explicitly open zones: %" PRIu32 "\n",
6685                        sg_get_unaligned_be32(bp + 8));
6686             break;
6687         case 0x2:
6688             if ((pl < 8) || (num < 8)) {
6689                 if (num < 8)
6690                     trunc = true;
6691                 else
6692                     bad_pl = true;
6693             } else
6694                 printf("  Maximum implicitly open zones: %" PRIu32 "\n",
6695                        sg_get_unaligned_be32(bp + 8));
6696             break;
6697         case 0x3:
6698             if ((pl < 8) || (num < 8)) {
6699                 if (num < 8)
6700                     trunc = true;
6701                 else
6702                     bad_pl = true;
6703             } else
6704                 printf("  Minimum empty zones: %" PRIu32 "\n",
6705                        sg_get_unaligned_be32(bp + 8));
6706             break;
6707         case 0x4:
6708             if ((pl < 8) || (num < 8)) {
6709                 if (num < 8)
6710                     trunc = true;
6711                 else
6712                     bad_pl = true;
6713             } else
6714                 printf("  Maximum non-sequential zones: %" PRIu32 "\n",
6715                        sg_get_unaligned_be32(bp + 8));
6716             break;
6717         case 0x5:
6718             if ((pl < 8) || (num < 8)) {
6719                 if (num < 8)
6720                     trunc = true;
6721                 else
6722                     bad_pl = true;
6723             } else
6724                 printf("  Zones emptied: %" PRIu32 "\n",
6725                        sg_get_unaligned_be32(bp + 8));
6726             break;
6727         case 0x6:
6728             if ((pl < 8) || (num < 8)) {
6729                 if (num < 8)
6730                     trunc = true;
6731                 else
6732                     bad_pl = true;
6733             } else
6734                 printf("  Suboptimal write commands: %" PRIu32 "\n",
6735                        sg_get_unaligned_be32(bp + 8));
6736             break;
6737         case 0x7:
6738             if ((pl < 8) || (num < 8)) {
6739                 if (num < 8)
6740                     trunc = true;
6741                 else
6742                     bad_pl = true;
6743             } else
6744                 printf("  Commands exceeding optimal limit: %" PRIu32 "\n",
6745                        sg_get_unaligned_be32(bp + 8));
6746             break;
6747         case 0x8:
6748             if ((pl < 8) || (num < 8)) {
6749                 if (num < 8)
6750                     trunc = true;
6751                 else
6752                     bad_pl = true;
6753             } else
6754                 printf("  Failed explicit opens: %" PRIu32 "\n",
6755                        sg_get_unaligned_be32(bp + 8));
6756             break;
6757         case 0x9:
6758             if ((pl < 8) || (num < 8)) {
6759                 if (num < 8)
6760                     trunc = true;
6761                 else
6762                     bad_pl = true;
6763             } else
6764                 printf("  Read rule violations: %" PRIu32 "\n",
6765                        sg_get_unaligned_be32(bp + 8));
6766             break;
6767         case 0xa:
6768             if ((pl < 8) || (num < 8)) {
6769                 if (num < 8)
6770                     trunc = true;
6771                 else
6772                     bad_pl = true;
6773             } else
6774                 printf("  Write rule violations: %" PRIu32 "\n",
6775                        sg_get_unaligned_be32(bp + 8));
6776             break;
6777         case 0xb:       /* added zbc2r04 */
6778             if ((pl < 8) || (num < 8)) {
6779                 if (num < 8)
6780                     trunc = true;
6781                 else
6782                     bad_pl = true;
6783             } else
6784                 printf("  Maximum implicitly open or before required zones: "
6785                        "%" PRIu32 "\n", sg_get_unaligned_be32(bp + 8));
6786             break;
6787         default:
6788             printf("  Reserved [parameter_code=0x%x]:\n", pc);
6789             hex2fp(bp, ((pl < num) ? pl : num), "    ",
6790                    op->hex2str_oformat, stdout);
6791             break;
6792         }
6793         if (trunc)
6794             pr2serr("    truncated by response length, expected at least "
6795                     "8 bytes\n");
6796         if (bad_pl)
6797             pr2serr("    parameter length >= 8 expected, got %d\n", pl);
6798         if (op->do_pcb)
6799             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
6800 filter_chk:
6801         if (op->filter_given)
6802             break;
6803 skip:
6804         num -= pl;
6805         bp += pl;
6806     }
6807     return true;
6808 }
6809 
6810 /* PENDING_DEFECTS_SUBPG [0x15,0x1] <pd>  introduced: SBC-4 */
6811 static bool
show_pending_defects_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)6812 show_pending_defects_page(const uint8_t * resp, int len,
6813                           struct opts_t * op, sgj_opaque_p jop)
6814 {
6815     int num, pl, pc;
6816     uint32_t count;
6817     const uint8_t * bp;
6818     char str[PCB_STR_LEN];
6819 
6820 if (jop) { };
6821     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
6822         printf("Pending defects page  [0x15,0x1]\n");
6823     num = len - 4;
6824     bp = &resp[0] + 4;
6825     while (num > 3) {
6826         pc = sg_get_unaligned_be16(bp + 0);
6827         pl = bp[3] + 4;
6828         if (op->filter_given) {
6829             if (pc != op->filter)
6830                 goto skip;
6831         }
6832         if (op->do_raw) {
6833             dStrRaw(bp, pl);
6834             goto filter_chk;
6835         } else if (op->do_hex) {
6836             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6837             goto filter_chk;
6838         }
6839         switch (pc) {
6840         case 0x0:
6841             printf("  Pending defect count: ");
6842             if ((pl < 8) || (num < 8)) {
6843                 if (num < 8)
6844                     pr2serr("\n    truncated by response length, expected "
6845                             "at least 8 bytes\n");
6846                 else
6847                     pr2serr("\n    parameter length >= 8 expected, got %d\n",
6848                             pl);
6849                 break;
6850             }
6851             count = sg_get_unaligned_be32(bp + 4);
6852             if (0 == count) {
6853                 printf("0\n");
6854                 break;
6855             }
6856             printf("%3u  |     LBA            Accumulated power_on\n", count);
6857             printf("-----------------------------|---------------");
6858             printf("-----------hours---------\n");
6859             break;
6860         default:
6861             printf("  Pending defect %4d:  ", pc);
6862             if ((pl < 16) || (num < 16)) {
6863                 if (num < 16)
6864                     pr2serr("\n    truncated by response length, expected "
6865                             "at least 16 bytes\n");
6866                 else
6867                     pr2serr("\n    parameter length >= 16 expected, got %d\n",
6868                             pl);
6869                 break;
6870             }
6871             printf("        0x%-16" PRIx64 "      %5u\n",
6872                    sg_get_unaligned_be64(bp + 8),
6873                    sg_get_unaligned_be32(bp + 4));
6874             break;
6875         }
6876         if (op->do_pcb)
6877             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
6878 filter_chk:
6879         if (op->filter_given)
6880             break;
6881 skip:
6882         num -= pl;
6883         bp += pl;
6884     }
6885     return true;
6886 }
6887 
6888 /* BACKGROUND_OP_SUBPG [0x15,0x2] <bop>  introduced: SBC-4 rev 7 */
6889 static bool
show_background_op_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)6890 show_background_op_page(const uint8_t * resp, int len,
6891                         struct opts_t * op, sgj_opaque_p jop)
6892 {
6893     int num, pl, pc;
6894     const uint8_t * bp;
6895     char str[PCB_STR_LEN];
6896 
6897 if (jop) { };
6898     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
6899         printf("Background operation page  [0x15,0x2]\n");
6900     num = len - 4;
6901     bp = &resp[0] + 4;
6902     while (num > 3) {
6903         pc = sg_get_unaligned_be16(bp + 0);
6904         pl = bp[3] + 4;
6905         if (op->filter_given) {
6906             if (pc != op->filter)
6907                 goto skip;
6908         }
6909         if (op->do_raw) {
6910             dStrRaw(bp, pl);
6911             goto filter_chk;
6912         } else if (op->do_hex) {
6913             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6914             goto filter_chk;
6915         }
6916         switch (pc) {
6917         case 0x0:
6918             printf("  Background operation:");
6919             if ((pl < 8) || (num < 8)) {
6920                 if (num < 8)
6921                     pr2serr("\n    truncated by response length, expected "
6922                             "at least 8 bytes\n");
6923                 else
6924                     pr2serr("\n    parameter length >= 8 expected, got %d\n",
6925                             pl);
6926                 break;
6927             }
6928             printf(" BO_STATUS=%d\n", bp[4]);
6929             break;
6930         default:
6931             printf("  Reserved [parameter_code=0x%x]:\n", pc);
6932             hex2fp(bp, ((pl < num) ? pl : num), "    ",
6933                    op->hex2str_oformat, stdout);
6934             break;
6935         }
6936         if (op->do_pcb)
6937             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
6938 filter_chk:
6939         if (op->filter_given)
6940             break;
6941 skip:
6942         num -= pl;
6943         bp += pl;
6944     }
6945     return true;
6946 }
6947 
6948 /* LPS misalignment page [0x15,0x3] <lps>  introduced: SBC-4 rev 10
6949    LPS: "Long Physical Sector" a term from an ATA feature set */
6950 static bool
show_lps_misalignment_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)6951 show_lps_misalignment_page(const uint8_t * resp, int len,
6952                            struct opts_t * op, sgj_opaque_p jop)
6953 {
6954     int num, pl, pc;
6955     const uint8_t * bp;
6956     char str[PCB_STR_LEN];
6957 
6958 if (jop) { };
6959     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
6960         printf("LPS misalignment page  [0x15,0x3]\n");
6961     num = len - 4;
6962     bp = &resp[0] + 4;
6963     while (num > 3) {
6964         pc = sg_get_unaligned_be16(bp + 0);
6965         pl = bp[3] + 4;
6966         if (op->filter_given) {
6967             if (pc != op->filter)
6968                 goto skip;
6969         }
6970         if (op->do_raw) {
6971             dStrRaw(bp, pl);
6972             goto filter_chk;
6973         } else if (op->do_hex) {
6974             hex2stdout(bp, pl, op->dstrhex_no_ascii);
6975             goto filter_chk;
6976         }
6977         switch (pc) {
6978         case 0x0:
6979             printf("  LPS misalignment count: ");
6980             if (4 == bp[3])
6981                 printf("max lpsm: %" PRIu16 ", count=%" PRIu16 "\n",
6982                        sg_get_unaligned_be16(bp + 4),
6983                        sg_get_unaligned_be16(bp + 6));
6984             else
6985                 printf("<unexpected pc=0 parameter length=%d>\n", bp[4]);
6986             break;
6987         default:
6988             if (pc <= 0xf000) {         /* parameter codes 0x1 to 0xf000 */
6989                 if (8 == bp[3])
6990                     printf("  LBA of misaligned block: 0x%" PRIx64 "\n",
6991                            sg_get_unaligned_be64(bp + 4));
6992                 else
6993                     printf("<unexpected pc=0x%x parameter length=%d>\n",
6994                            pc, bp[4]);
6995             } else {
6996                 printf("<unexpected pc=0x%x>\n", pc);
6997                 hex2fp(bp, ((pl < num) ? pl : num), "    ",
6998                        op->hex2str_oformat, stdout);
6999             }
7000             break;
7001         }
7002         if (op->do_pcb)
7003             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
7004 filter_chk:
7005         if (op->filter_given)
7006             break;
7007 skip:
7008         num -= pl;
7009         bp += pl;
7010     }
7011     return true;
7012 }
7013 
7014 /* Service buffer information [0x15] <sbi> (adc) */
7015 static bool
show_service_buffer_info_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)7016 show_service_buffer_info_page(const uint8_t * resp, int len,
7017                               struct opts_t * op, sgj_opaque_p jop)
7018 {
7019     bool evsm_output = false;
7020     int num, pl, pc;
7021     const uint8_t * bp;
7022     char str[PCB_STR_LEN];
7023 
7024 if (jop) { };
7025     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
7026         printf("Service buffer information page (adc-3) [0x15]\n");
7027     num = len - 4;
7028     bp = &resp[0] + 4;
7029     while (num > 3) {
7030         pc = sg_get_unaligned_be16(bp + 0);
7031         pl = bp[3] + 4;
7032         if (op->filter_given) {
7033             if (pc != op->filter)
7034                 goto skip;
7035         }
7036         if (op->do_raw) {
7037             dStrRaw(bp, pl);
7038             goto filter_chk;
7039         } else if (op->do_hex) {
7040             hex2stdout(bp, pl, op->dstrhex_no_ascii);
7041             goto filter_chk;
7042         }
7043         if (pc < 0x100) {
7044             printf("  Service buffer identifier: 0x%x\n", pc);
7045             printf("    Buffer id: 0x%x, tu=%d, nmp=%d, nmm=%d, "
7046                    "offline=%d\n", bp[4], !!(0x10 & bp[5]),
7047                    !!(0x8 & bp[5]), !!(0x4 & bp[5]), !!(0x2 & bp[5]));
7048             printf("    pd=%d, code_set: %s, Service buffer title:\n",
7049                    !!(0x1 & bp[5]), sg_get_desig_code_set_str(0xf & bp[6]));
7050             printf("      %.*s\n", pl - 8, bp + 8);
7051         } else if (pc < 0x8000) {
7052             printf("  parameter_code=0x%x, Reserved, parameter in hex:\n",
7053                    pc);
7054             hex2fp(bp + 4, pl - 4, "    ", op->hex2str_oformat, stdout);
7055         } else {
7056             if (op->exclude_vendor) {
7057                 if ((op->verbose > 0) && (0 == op->do_brief) &&
7058                     (! evsm_output)) {
7059                     evsm_output = true;
7060                     printf("  Vendor specific parameter(s) being "
7061                            "ignored\n");
7062                 }
7063             } else {
7064                 printf("  parameter_code=0x%x, Vendor-specific, parameter in "
7065                        "hex:\n", pc);
7066                 hex2fp(bp + 4, pl - 4, "    ", op->hex2str_oformat, stdout);
7067             }
7068         }
7069         if (op->do_pcb)
7070             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
7071 filter_chk:
7072         if (op->filter_given)
7073             break;
7074 skip:
7075         num -= pl;
7076         bp += pl;
7077     }
7078     return true;
7079 }
7080 
7081 /* Sequential access device page [0xc] <sad> for tape */
7082 static bool
show_sequential_access_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)7083 show_sequential_access_page(const uint8_t * resp, int len,
7084                             struct opts_t * op, sgj_opaque_p jop)
7085 {
7086     bool evsm_output = false;
7087     int num, pl, pc;
7088     const uint8_t * bp;
7089     uint64_t ull, gbytes;
7090     bool all_set;
7091     char str[PCB_STR_LEN];
7092 
7093 if (jop) { };
7094     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
7095         printf("Sequential access device page (ssc-3)\n");
7096     num = len - 4;
7097     bp = &resp[0] + 4;
7098     while (num > 3) {
7099         pc = sg_get_unaligned_be16(bp + 0);
7100         pl = bp[3] + 4;
7101         if (op->filter_given) {
7102             if (pc != op->filter)
7103                 goto skip;
7104         }
7105         if (op->do_raw) {
7106             dStrRaw(bp, pl);
7107             goto filter_chk;
7108         } else if (op->do_hex) {
7109             hex2stdout(bp, pl, op->dstrhex_no_ascii);
7110             goto filter_chk;
7111         }
7112         ull = sg_get_unaligned_be(pl - 4, bp + 4);
7113         all_set = sg_all_ffs(bp + 4, pl - 4);
7114         gbytes = ull / 1000000000;
7115         switch (pc) {
7116         case 0:
7117             printf("  Data bytes received with WRITE commands: %" PRIu64
7118                    " GB", gbytes);
7119             if (op->verbose)
7120                 printf(" [%" PRIu64 " bytes]", ull);
7121             printf("\n");
7122             break;
7123         case 1:
7124             printf("  Data bytes written to media by WRITE commands: %" PRIu64
7125                    " GB", gbytes);
7126             if (op->verbose)
7127                 printf(" [%" PRIu64 " bytes]", ull);
7128             printf("\n");
7129             break;
7130         case 2:
7131             printf("  Data bytes read from media by READ commands: %" PRIu64
7132                    " GB", gbytes);
7133             if (op->verbose)
7134                 printf(" [%" PRIu64 " bytes]", ull);
7135             printf("\n");
7136             break;
7137         case 3:
7138             printf("  Data bytes transferred by READ commands: %" PRIu64
7139                    " GB", gbytes);
7140             if (op->verbose)
7141                 printf(" [%" PRIu64 " bytes]", ull);
7142             printf("\n");
7143             break;
7144         case 4:
7145             if (! all_set)
7146                 printf("  Native capacity from BOP to EOD: %" PRIu64 " MB\n",
7147                        ull);
7148             break;
7149         case 5:
7150             if (! all_set)
7151                 printf("  Native capacity from BOP to EW of current "
7152                        "partition: %" PRIu64 " MB\n", ull);
7153             break;
7154         case 6:
7155             if (! all_set)
7156                 printf("  Minimum native capacity from EW to EOP of current "
7157                        "partition: %" PRIu64 " MB\n", ull);
7158             break;
7159         case 7:
7160             if (! all_set)
7161                 printf("  Native capacity from BOP to current position: %"
7162                        PRIu64 " MB\n", ull);
7163             break;
7164         case 8:
7165             if (! all_set)
7166                 printf("  Maximum native capacity in device object buffer: %"
7167                        PRIu64 " MB\n", ull);
7168             break;
7169         case 0x100:
7170             if (ull > 0)
7171                 printf("  Cleaning action required\n");
7172             else
7173                 printf("  Cleaning action not required (or completed)\n");
7174             if (op->verbose)
7175                 printf("    cleaning value: %" PRIu64 "\n", ull);
7176             break;
7177         default:
7178             if (pc >= 0x8000) {
7179                 if (op->exclude_vendor) {
7180                     if ((op->verbose > 0) && (0 == op->do_brief) &&
7181                         (! evsm_output)) {
7182                         evsm_output = true;
7183                         printf("  Vendor specific parameter(s) being "
7184                                "ignored\n");
7185                     }
7186                 } else
7187                     printf("  Vendor specific parameter [0x%x] value: %"
7188                            PRIu64 "\n", pc, ull);
7189             } else
7190                 printf("  Reserved parameter [0x%x] value: %" PRIu64 "\n",
7191                        pc, ull);
7192             break;
7193         }
7194         if (op->do_pcb)
7195             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
7196 filter_chk:
7197         if (op->filter_given)
7198             break;
7199 skip:
7200         num -= pl;
7201         bp += pl;
7202     }
7203     return true;
7204 }
7205 
7206 /* Device statistics 0x14 <ds> for tape and ADC */
7207 static bool
show_device_stats_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)7208 show_device_stats_page(const uint8_t * resp, int len,
7209                        struct opts_t * op, sgj_opaque_p jop)
7210 {
7211     bool evsm_output = false;
7212     int num, pl, pc;
7213     const uint8_t * bp;
7214     char str[PCB_STR_LEN];
7215 
7216 if (jop) { };
7217     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
7218         printf("Device statistics page (ssc-3 and adc)\n");
7219     num = len - 4;
7220     bp = &resp[0] + 4;
7221     while (num > 3) {
7222         pc = sg_get_unaligned_be16(bp + 0);
7223         pl = bp[3] + 4;
7224         if (op->filter_given) {
7225             if (pc != op->filter)
7226                 goto skip;
7227         }
7228         if (op->do_raw) {
7229             dStrRaw(bp, pl);
7230              goto filter_chk;
7231         } else if (op->do_hex) {
7232             hex2stdout(bp, pl, op->dstrhex_no_ascii);
7233              goto filter_chk;
7234         }
7235         if (pc < 0x1000) {
7236             bool vl_num = true;
7237 
7238             switch (pc) {
7239             case 0:
7240                 printf("  Lifetime media loads:");
7241                 break;
7242             case 1:
7243                 printf("  Lifetime cleaning operations:");
7244                 break;
7245             case 2:
7246                 printf("  Lifetime power on hours:");
7247                 break;
7248             case 3:
7249                 printf("  Lifetime media motion (head) hours:");
7250                 break;
7251             case 4:
7252                 printf("  Lifetime metres of tape processed:");
7253                 break;
7254             case 5:
7255                 printf("  Lifetime media motion (head) hours when "
7256                        "incompatible media last loaded:");
7257                 break;
7258             case 6:
7259                 printf("  Lifetime power on hours when last temperature "
7260                        "condition occurred:");
7261                 break;
7262             case 7:
7263                 printf("  Lifetime power on hours when last power "
7264                        "consumption condition occurred:");
7265                 break;
7266             case 8:
7267                 printf("  Media motion (head) hours since last successful "
7268                        "cleaning operation:");
7269                 break;
7270             case 9:
7271                 printf("  Media motion (head) hours since 2nd to last "
7272                        "successful cleaning:");
7273                 break;
7274             case 0xa:
7275                 printf("  Media motion (head) hours since 3rd to last "
7276                        "successful cleaning:");
7277                 break;
7278             case 0xb:
7279                 printf("  Lifetime power on hours when last operator "
7280                        "initiated forced reset\n    and/or emergency "
7281                        "eject occurred:");
7282                 break;
7283             case 0xc:
7284                 printf("  Lifetime power cycles:");
7285                 break;
7286             case 0xd:
7287                 printf("  Volume loads since last parameter reset:");
7288                 break;
7289             case 0xe:
7290                 printf("  Hard write errors:");
7291                 break;
7292             case 0xf:
7293                 printf("  Hard read errors:");
7294                 break;
7295             case 0x10:
7296                 printf("  Duty cycle sample time (ms):");
7297                 break;
7298             case 0x11:
7299                 printf("  Read duty cycle:");
7300                 break;
7301             case 0x12:
7302                 printf("  Write duty cycle:");
7303                 break;
7304             case 0x13:
7305                 printf("  Activity duty cycle:");
7306                 break;
7307             case 0x14:
7308                 printf("  Volume not present duty cycle:");
7309                 break;
7310             case 0x15:
7311                 printf("  Ready duty cycle:");
7312                 break;
7313             case 0x16:
7314                 printf("  MBs transferred from app client in duty cycle "
7315                        "sample time:");
7316                 break;
7317             case 0x17:
7318                 printf("  MBs transferred to app client in duty cycle "
7319                        "sample time:");
7320                 break;
7321             case 0x40:
7322                 printf("  Drive manufacturer's serial number:");
7323                 break;
7324             case 0x41:
7325                 printf("  Drive serial number:");
7326                 break;
7327             case 0x42:          /* added ssc5r02b */
7328                 vl_num = false;
7329                 printf("  Manufacturing date (yyyymmdd): %.*s\n", pl - 4,
7330                        bp + 4);
7331                 break;
7332             case 0x43:          /* added ssc5r02b */
7333                 vl_num = false;
7334                 printf("  Manufacturing date (yyyyww): %.*s\n", pl - 4,
7335                        bp + 4);
7336                 break;
7337             case 0x80:
7338                 printf("  Medium removal prevented:");
7339                 break;
7340             case 0x81:
7341                 printf("  Maximum recommended mechanism temperature "
7342                        "exceeded:");
7343                 break;
7344             default:
7345                 vl_num = false;
7346                 printf("  Reserved %s [0x%x] data in hex:\n", param_c, pc);
7347                 hex2fp(bp + 4, pl - 4, "    ", op->hex2str_oformat, stdout);
7348                 break;
7349             }
7350             if (vl_num)
7351                 printf(" %" PRIu64 "\n", sg_get_unaligned_be(pl - 4, bp + 4));
7352         } else {        /* parameter_code >= 0x1000 */
7353             int k;
7354             const uint8_t * p = bp + 4;
7355 
7356             switch (pc) {
7357             case 0x1000:
7358                 printf("  Media motion (head) hours for each medium type:\n");
7359                 for (k = 0; ((pl - 4) - k) >= 8; k += 8, p += 8)
7360                     printf("    [%d] Density code: %u, Medium type: 0x%x, "
7361                            "hours: %u\n", ((k / 8) + 1), p[2], p[3],
7362                            sg_get_unaligned_be32(p + 4));
7363                 break;
7364             default:
7365                 if (pc >= 0x8000) {
7366                     if (op->exclude_vendor) {
7367                         if ((op->verbose > 0) && (0 == op->do_brief) &&
7368                             (! evsm_output)) {
7369                             evsm_output = true;
7370                             printf("  Vendor specific parameter(s) being "
7371                                    "ignored\n");
7372                         }
7373                     } else
7374                         printf("  Vendor specific parameter [0x%x], dump in "
7375                                "hex:\n", pc);
7376                 } else {
7377                     printf("  Reserved parameter [0x%x], dump in hex:\n", pc);
7378                     hex2fp(bp + 4, pl - 4, "    ", op->hex2str_oformat,
7379                            stdout);
7380                 }
7381                 break;
7382             }
7383         }
7384         if (op->do_pcb)
7385             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
7386 filter_chk:
7387         if (op->filter_given)
7388             break;
7389 skip:
7390         num -= pl;
7391         bp += pl;
7392     }
7393     return true;
7394 }
7395 
7396 /* Media changer statistics 0x14 <mcs> for media changer */
7397 static bool
show_media_stats_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)7398 show_media_stats_page(const uint8_t * resp, int len, struct opts_t * op,
7399                       sgj_opaque_p jop)
7400 {
7401     int num, pl, pc;
7402     const uint8_t * bp;
7403     uint64_t ull;
7404     char str[PCB_STR_LEN];
7405 
7406 if (jop) { };
7407     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
7408         printf("Media statistics page (smc-3)\n");
7409     num = len - 4;
7410     bp = &resp[0] + 4;
7411     while (num > 3) {
7412         pc = sg_get_unaligned_be16(bp + 0);
7413         pl = bp[3] + 4;
7414         if (op->filter_given) {
7415             if (pc != op->filter)
7416                 goto skip;
7417         }
7418         if (op->do_raw) {
7419             dStrRaw(bp, pl);
7420             goto filter_chk;
7421         } else if (op->do_hex) {
7422             hex2stdout(bp, pl, op->dstrhex_no_ascii);
7423             goto filter_chk;
7424         }
7425         ull = sg_get_unaligned_be(pl - 4, bp + 4);
7426         switch (pc) {
7427         case 0:
7428             printf("  Number of moves: %" PRIu64 "\n", ull);
7429             break;
7430         case 1:
7431             printf("  Number of picks: %" PRIu64 "\n", ull);
7432             break;
7433         case 2:
7434             printf("  Number of pick retries: %" PRIu64 "\n", ull);
7435             break;
7436         case 3:
7437             printf("  Number of places: %" PRIu64 "\n", ull);
7438             break;
7439         case 4:
7440             printf("  Number of place retries: %" PRIu64 "\n", ull);
7441             break;
7442         case 5:
7443             printf("  Number of volume tags read by volume "
7444                    "tag reader: %" PRIu64 "\n", ull);
7445             break;
7446         case 6:
7447             printf("  Number of invalid volume tags returned by "
7448                    "volume tag reader: %" PRIu64 "\n", ull);
7449             break;
7450         case 7:
7451             printf("  Number of library door opens: %" PRIu64 "\n", ull);
7452             break;
7453         case 8:
7454             printf("  Number of import/export door opens: %" PRIu64 "\n",
7455                    ull);
7456             break;
7457         case 9:
7458             printf("  Number of physical inventory scans: %" PRIu64 "\n",
7459                    ull);
7460             break;
7461         case 0xa:
7462             printf("  Number of medium transport unrecovered errors: "
7463                    "%" PRIu64 "\n", ull);
7464             break;
7465         case 0xb:
7466             printf("  Number of medium transport recovered errors: "
7467                    "%" PRIu64 "\n", ull);
7468             break;
7469         case 0xc:
7470             printf("  Number of medium transport X axis translation "
7471                    "unrecovered errors: %" PRIu64 "\n", ull);
7472             break;
7473         case 0xd:
7474             printf("  Number of medium transport X axis translation "
7475                    "recovered errors: %" PRIu64 "\n", ull);
7476             break;
7477         case 0xe:
7478             printf("  Number of medium transport Y axis translation "
7479                    "unrecovered errors: %" PRIu64 "\n", ull);
7480             break;
7481         case 0xf:
7482             printf("  Number of medium transport Y axis translation "
7483                    "recovered errors: %" PRIu64 "\n", ull);
7484             break;
7485         case 0x10:
7486             printf("  Number of medium transport Z axis translation "
7487                    "unrecovered errors: %" PRIu64 "\n", ull);
7488             break;
7489         case 0x11:
7490             printf("  Number of medium transport Z axis translation "
7491                    "recovered errors: %" PRIu64 "\n", ull);
7492             break;
7493         case 0x12:
7494             printf("  Number of medium transport rotational translation "
7495                    "unrecovered errors: %" PRIu64 "\n", ull);
7496             break;
7497         case 0x13:
7498             printf("  Number of medium transport rotational translation "
7499                    "recovered errors: %" PRIu64 "\n", ull);
7500             break;
7501         case 0x14:
7502             printf("  Number of medium transport inversion translation "
7503                    "unrecovered errors: %" PRIu64 "\n", ull);
7504             break;
7505         case 0x15:
7506             printf("  Number of medium transport inversion translation "
7507                    "recovered errors: %" PRIu64 "\n", ull);
7508             break;
7509         case 0x16:
7510             printf("  Number of medium transport auxiliary translation "
7511                    "unrecovered errors: %" PRIu64 "\n", ull);
7512             break;
7513         case 0x17:
7514             printf("  Number of medium transport auxiliary translation "
7515                    "recovered errors: %" PRIu64 "\n", ull);
7516             break;
7517         default:
7518             printf("  Reserved parameter [0x%x] value: %" PRIu64 "\n",
7519                    pc, ull);
7520             break;
7521         }
7522         if (op->do_pcb)
7523             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
7524 filter_chk:
7525         if (op->filter_given)
7526             break;
7527 skip:
7528         num -= pl;
7529         bp += pl;
7530     }
7531     return true;
7532 }
7533 
7534 /* Element statistics page, 0x15 <els> for SMC */
7535 static bool
show_element_stats_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)7536 show_element_stats_page(const uint8_t * resp, int len,
7537                         struct opts_t * op, sgj_opaque_p jop)
7538 {
7539     int num, pl, pc;
7540     unsigned int v;
7541     const uint8_t * bp;
7542     char str[PCB_STR_LEN];
7543 
7544 if (jop) { };
7545     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
7546         printf("Element statistics page (smc-3) [0x15]\n");
7547     num = len - 4;
7548     bp = &resp[0] + 4;
7549     while (num > 3) {
7550         pc = sg_get_unaligned_be16(bp + 0);
7551         pl = bp[3] + 4;
7552         if (op->filter_given) {
7553             if (pc != op->filter)
7554                 goto skip;
7555         }
7556         if (op->do_raw) {
7557             dStrRaw(bp, pl);
7558             goto filter_chk;
7559         } else if (op->do_hex) {
7560             hex2stdout(bp, pl, op->dstrhex_no_ascii);
7561             goto filter_chk;
7562         }
7563         printf("  Element address: %d\n", pc);
7564         v = sg_get_unaligned_be32(bp + 4);
7565         printf("    Number of places: %u\n", v);
7566         v = sg_get_unaligned_be32(bp + 8);
7567         printf("    Number of place retries: %u\n", v);
7568         v = sg_get_unaligned_be32(bp + 12);
7569         printf("    Number of picks: %u\n", v);
7570         v = sg_get_unaligned_be32(bp + 16);
7571         printf("    Number of pick retries: %u\n", v);
7572         v = sg_get_unaligned_be32(bp + 20);
7573         printf("    Number of determined volume identifiers: %u\n", v);
7574         v = sg_get_unaligned_be32(bp + 24);
7575         printf("    Number of unreadable volume identifiers: %u\n", v);
7576         if (op->do_pcb)
7577             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
7578 filter_chk:
7579         if (op->filter_given)
7580             break;
7581 skip:
7582         num -= pl;
7583         bp += pl;
7584     }
7585     return true;
7586 }
7587 
7588 /* Tape diagnostic data [0x16] <tdd> for tape */
7589 static bool
show_tape_diag_data_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)7590 show_tape_diag_data_page(const uint8_t * resp, int len,
7591                          struct opts_t * op, sgj_opaque_p jop)
7592 {
7593     int k, n, num, pl, pc;
7594     unsigned int v;
7595     const uint8_t * bp;
7596     char str[PCB_STR_LEN];
7597     char b[512];
7598 
7599 if (jop) { };
7600     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
7601         printf("Tape diagnostics data page (ssc-3) [0x16]\n");
7602     num = len - 4;
7603     bp = &resp[0] + 4;
7604     while (num > 3) {
7605         pc = sg_get_unaligned_be16(bp + 0);
7606         pl = bp[3] + 4;
7607         if (op->filter_given) {
7608             if (pc != op->filter)
7609                 goto skip;
7610         }
7611         if (op->do_raw) {
7612             dStrRaw(bp, pl);
7613             goto filter_chk;
7614         } else if (op->do_hex) {
7615             hex2stdout(bp, pl, op->dstrhex_no_ascii);
7616             goto filter_chk;
7617         }
7618         printf("  %s: %d\n", param_c, pc);
7619         printf("    Density code: 0x%x\n", bp[6]);
7620         printf("    Medium type: 0x%x\n", bp[7]);
7621         v = sg_get_unaligned_be32(bp + 8);
7622         printf("    Lifetime media motion hours: %u\n", v);
7623         printf("    Repeat: %d\n", !!(bp[13] & 0x80));
7624         v = bp[13] & 0xf;
7625         printf("    Sense key: 0x%x [%s]\n", v,
7626                sg_get_sense_key_str(v, sizeof(b), b));
7627         printf("    Additional sense code: 0x%x\n", bp[14]);
7628         printf("    Additional sense code qualifier: 0x%x\n", bp[15]);
7629         if (bp[14] || bp[15])
7630             printf("      [%s]\n", sg_get_asc_ascq_str(bp[14], bp[15],
7631                    sizeof(b), b));
7632         v = sg_get_unaligned_be32(bp + 16);
7633         printf("    Vendor specific code qualifier: 0x%x\n", v);
7634         v = sg_get_unaligned_be32(bp + 20);
7635         printf("    Product revision level: %u\n", v);
7636         v = sg_get_unaligned_be32(bp + 24);
7637         printf("    Hours since last clean: %u\n", v);
7638         printf("    Operation code: 0x%x\n", bp[28]);
7639         printf("    Service action: 0x%x\n", bp[29] & 0xf);
7640         // Check Medium id number for all zeros
7641         // ssc4r03.pdf does not define this field, why? xxxxxx
7642         if (sg_all_zeros(bp + 32, 32))
7643             printf("    Medium id number is 32 bytes of zero\n");
7644         else {
7645             hex2str(bp + 32, 32, "      ", 0 /* with ASCII */, sizeof(b), b);
7646             printf("    Medium id number (in hex):\n%s", b);
7647         }
7648         printf("    Timestamp origin: 0x%x\n", bp[64] & 0xf);
7649         // Check Timestamp for all zeros
7650         if (sg_all_zeros(bp + 66, 6))
7651             printf("    Timestamp is all zeros:\n");
7652         else {
7653             hex2str(bp + 66, 6, NULL, op->hex2str_oformat, sizeof(b), b);
7654             printf("    Timestamp: %s", b);
7655         }
7656         if (pl > 72) {
7657             n = pl - 72;
7658             k = hex2str(bp + 72, n, "      ", op->hex2str_oformat,
7659                         sizeof(b), b);
7660             printf("    Vendor specific:\n");
7661             printf("%s", b);
7662             if (k >= (int)sizeof(b) - 1)
7663                 printf("      <truncated>\n");
7664         }
7665         if (op->do_pcb)
7666             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
7667 filter_chk:
7668         if (op->filter_given)
7669             break;
7670 skip:
7671         num -= pl;
7672         bp += pl;
7673     }
7674     return true;
7675 }
7676 
7677 /* Media changer diagnostic data [0x16] <mcdd> for media changer */
7678 static bool
show_mchanger_diag_data_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)7679 show_mchanger_diag_data_page(const uint8_t * resp, int len,
7680                              struct opts_t * op, sgj_opaque_p jop)
7681 {
7682     int num, pl, pc;
7683     unsigned int v;
7684     const uint8_t * bp;
7685     char str[PCB_STR_LEN];
7686     char b[512];
7687 
7688 if (jop) { };
7689     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
7690         printf("Media changer diagnostics data page (smc-3) [0x16]\n");
7691     num = len - 4;
7692     bp = &resp[0] + 4;
7693     while (num > 3) {
7694         pc = sg_get_unaligned_be16(bp + 0);
7695         pl = bp[3] + 4;
7696         if (op->filter_given) {
7697             if (pc != op->filter)
7698                 goto skip;
7699         }
7700         if (op->do_raw) {
7701             dStrRaw(bp, pl);
7702             goto filter_chk;
7703         } else if (op->do_hex) {
7704             hex2stdout(bp, pl, op->dstrhex_no_ascii);
7705             goto filter_chk;
7706         }
7707         printf("  %s: %d\n", param_c, pc);
7708         printf("    Repeat: %d\n", !!(bp[5] & 0x80));
7709         v = bp[5] & 0xf;
7710         printf("    Sense key: 0x%x [%s]\n", v,
7711                sg_get_sense_key_str(v, sizeof(b), b));
7712         printf("    Additional sense code: 0x%x\n", bp[6]);
7713         printf("    Additional sense code qualifier: 0x%x\n", bp[7]);
7714         if (bp[6] || bp[7])
7715             printf("      [%s]\n", sg_get_asc_ascq_str(bp[6], bp[7],
7716                    sizeof(b), b));
7717         v = sg_get_unaligned_be32(bp + 8);
7718         printf("    Vendor specific code qualifier: 0x%x\n", v);
7719         v = sg_get_unaligned_be32(bp + 12);
7720         printf("    Product revision level: %u\n", v);
7721         v = sg_get_unaligned_be32(bp + 16);
7722         printf("    Number of moves: %u\n", v);
7723         v = sg_get_unaligned_be32(bp + 20);
7724         printf("    Number of pick: %u\n", v);
7725         v = sg_get_unaligned_be32(bp + 24);
7726         printf("    Number of pick retries: %u\n", v);
7727         v = sg_get_unaligned_be32(bp + 28);
7728         printf("    Number of places: %u\n", v);
7729         v = sg_get_unaligned_be32(bp + 32);
7730         printf("    Number of place retries: %u\n", v);
7731         v = sg_get_unaligned_be32(bp + 36);
7732         printf("    Number of determined volume identifiers: %u\n", v);
7733         v = sg_get_unaligned_be32(bp + 40);
7734         printf("    Number of unreadable volume identifiers: %u\n", v);
7735         printf("    Operation code: 0x%x\n", bp[44]);
7736         printf("    Service action: 0x%x\n", bp[45] & 0xf);
7737         printf("    Media changer error type: 0x%x\n", bp[46]);
7738         printf("    MTAV: %d\n", !!(bp[47] & 0x8));
7739         printf("    IAV: %d\n", !!(bp[47] & 0x4));
7740         printf("    LSAV: %d\n", !!(bp[47] & 0x2));
7741         printf("    DAV: %d\n", !!(bp[47] & 0x1));
7742         v = sg_get_unaligned_be16(bp + 48);
7743         printf("    Medium transport address: 0x%x\n", v);
7744         v = sg_get_unaligned_be16(bp + 50);
7745         printf("    Initial address: 0x%x\n", v);
7746         v = sg_get_unaligned_be16(bp + 52);
7747         printf("    Last successful address: 0x%x\n", v);
7748         v = sg_get_unaligned_be16(bp + 54);
7749         printf("    Destination address: 0x%x\n", v);
7750         if (pl > 91) {
7751             printf("    Volume tag information:\n");
7752             hex2fp(bp + 56, 36, "    ", op->hex2str_oformat, stdout);
7753         }
7754         if (pl > 99) {
7755             printf("    Timestamp origin: 0x%x\n", bp[92] & 0xf);
7756             printf("    Timestamp:\n");
7757             hex2fp(bp + 94, 6, "    ", op->hex2str_oformat, stdout);
7758         }
7759         if (op->do_pcb)
7760             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
7761 filter_chk:
7762         if (op->filter_given)
7763             break;
7764 skip:
7765         num -= pl;
7766         bp += pl;
7767     }
7768     return true;
7769 }
7770 
7771 /* Helper for show_volume_stats_pages() */
7772 static void
volume_stats_partition(const uint8_t * xp,int len,bool pr_in_hex)7773 volume_stats_partition(const uint8_t * xp, int len, bool pr_in_hex)
7774 {
7775     uint64_t ull;
7776 
7777     while (len > 3) {
7778         bool all_ffs, ffs_last_fe;
7779         int dl, pn;
7780 
7781         dl = xp[0] + 1;
7782         if (dl < 3)
7783             return;
7784         pn = sg_get_unaligned_be16(xp + 2);
7785         ffs_last_fe = false;
7786         all_ffs = false;
7787         if (sg_all_ffs(xp + 4, dl - 3)) {
7788             switch (xp[4 + dl - 3]) {
7789             case 0xff:
7790                 all_ffs = true;
7791                 break;
7792             case 0xfe:
7793                 ffs_last_fe = true;
7794                 break;
7795             default:
7796                 break;
7797             }
7798         }
7799         if (! (all_ffs || ffs_last_fe)) {
7800             ull = sg_get_unaligned_be(dl - 4, xp + 4);
7801             if (pr_in_hex)
7802                 printf("    partition number: %d, partition record data "
7803                        "counter: 0x%" PRIx64 "\n", pn, ull);
7804             else
7805                 printf("    partition number: %d, partition record data "
7806                        "counter: %" PRIu64 "\n", pn, ull);
7807         } else if (all_ffs)
7808             printf("    partition number: %d, partition record data "
7809                    "counter is all 0xFFs\n", pn);
7810         else    /* ffs_last_fe is true */
7811             printf("    partition number: %d, partition record data "
7812                    "counter is all 0xFFs apart\n    from a trailing "
7813                    "0xFE\n", pn);
7814         xp += dl;
7815         len -= dl;
7816     }
7817 }
7818 
7819 /* Helper for show_volume_stats_pages() */
7820 static void
volume_stats_history(const uint8_t * xp,int len)7821 volume_stats_history(const uint8_t * xp, int len)
7822 {
7823     while (len > 3) {
7824         int dl, mhi;
7825 
7826         dl = xp[0] + 1;
7827         if (dl < 4)
7828             return;
7829         mhi = sg_get_unaligned_be16(xp + 2);
7830         if (dl < 12)
7831             printf("    index: %d\n", mhi);
7832         else if (12 == dl)
7833             printf("    index: %d, vendor: %.8s\n", mhi, xp + 4);
7834         else
7835             printf("    index: %d, vendor: %.8s, unit serial number: %.*s\n",
7836                    mhi, xp + 4, dl - 12, xp + 12);
7837         xp += dl;
7838         len -= dl;
7839     }
7840 }
7841 
7842 /* Volume Statistics log page and subpages (ssc-4) [0x17, 0x0-0xf] <vs> */
7843 static bool
show_volume_stats_pages(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)7844 show_volume_stats_pages(const uint8_t * resp, int len,
7845                        struct opts_t * op, sgj_opaque_p jop)
7846 {
7847     bool skip_out = false;
7848     bool evsm_output = false;
7849     int num, pl, pc, subpg_code;
7850     bool spf;
7851     const uint8_t * bp;
7852     char str[PCB_STR_LEN];
7853     char b[512];
7854 
7855 if (jop) { };
7856     spf = !!(resp[0] & 0x40);
7857     subpg_code = spf ? resp[1] : NOT_SPG_SUBPG;
7858     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
7859         if (subpg_code < 0x10)
7860             printf("Volume statistics page (ssc-4), subpage=%d\n",
7861                    subpg_code);
7862         else {
7863             printf("Volume statistics page (ssc-4), subpage=%d; Reserved, "
7864                    "skip\n", subpg_code);
7865             return false;
7866         }
7867     }
7868     num = len - 4;
7869     bp = &resp[0] + 4;
7870     while (num > 3) {
7871         pc = sg_get_unaligned_be16(bp + 0);
7872         pl = bp[3] + 4;
7873         if (op->filter_given) {
7874             if (pc != op->filter)
7875                 goto skip;
7876         }
7877         if (op->do_raw) {
7878             dStrRaw(bp, pl);
7879             goto filter_chk;
7880         } else if (op->do_hex) {
7881             hex2stdout(bp, pl, op->dstrhex_no_ascii);
7882             goto filter_chk;
7883         }
7884 
7885         switch (pc) {
7886         case 0:
7887             printf("  Page valid: %" PRIu64 "\n",
7888                    sg_get_unaligned_be(pl - 4, bp + 4));
7889             break;
7890         case 1:
7891             printf("  Thread count: %" PRIu64 "\n",
7892                    sg_get_unaligned_be(pl - 4, bp + 4));
7893             break;
7894         case 2:
7895             printf("  Total data sets written: %" PRIu64 "\n",
7896                    sg_get_unaligned_be(pl - 4, bp + 4));
7897             break;
7898         case 3:
7899             printf("  Total write retries: %" PRIu64 "\n",
7900                    sg_get_unaligned_be(pl - 4, bp + 4));
7901             break;
7902         case 4:
7903             printf("  Total unrecovered write errors: %" PRIu64 "\n",
7904                    sg_get_unaligned_be(pl - 4, bp + 4));
7905             break;
7906         case 5:
7907             printf("  Total suspended writes: %" PRIu64 "\n",
7908                    sg_get_unaligned_be(pl - 4, bp + 4));
7909             break;
7910         case 6:
7911             printf("  Total fatal suspended writes: %" PRIu64 "\n",
7912                    sg_get_unaligned_be(pl - 4, bp + 4));
7913             break;
7914         case 7:
7915             printf("  Total data sets read: %" PRIu64 "\n",
7916                    sg_get_unaligned_be(pl - 4, bp + 4));
7917             break;
7918         case 8:
7919             printf("  Total read retries: %" PRIu64 "\n",
7920                    sg_get_unaligned_be(pl - 4, bp + 4));
7921             break;
7922         case 9:
7923             printf("  Total unrecovered read errors: %" PRIu64 "\n",
7924                    sg_get_unaligned_be(pl - 4, bp + 4));
7925             break;
7926         case 0xa:
7927             printf("  Total suspended reads: %" PRIu64 "\n",
7928                    sg_get_unaligned_be(pl - 4, bp + 4));
7929             break;
7930         case 0xb:
7931             printf("  Total fatal suspended reads: %" PRIu64 "\n",
7932                    sg_get_unaligned_be(pl - 4, bp + 4));
7933             break;
7934         case 0xc:
7935             printf("  Last mount unrecovered write errors: %" PRIu64 "\n",
7936                    sg_get_unaligned_be(pl - 4, bp + 4));
7937             break;
7938         case 0xd:
7939             printf("  Last mount unrecovered read errors: %" PRIu64 "\n",
7940                    sg_get_unaligned_be(pl - 4, bp + 4));
7941             break;
7942         case 0xe:
7943             printf("  Last mount megabytes written: %" PRIu64 "\n",
7944                    sg_get_unaligned_be(pl - 4, bp + 4));
7945             break;
7946         case 0xf:
7947             printf("  Last mount megabytes read: %" PRIu64 "\n",
7948                    sg_get_unaligned_be(pl - 4, bp + 4));
7949             break;
7950         case 0x10:
7951             printf("  Lifetime megabytes written: %" PRIu64 "\n",
7952                    sg_get_unaligned_be(pl - 4, bp + 4));
7953             break;
7954         case 0x11:
7955             printf("  Lifetime megabytes read: %" PRIu64 "\n",
7956                    sg_get_unaligned_be(pl - 4, bp + 4));
7957             break;
7958         case 0x12:
7959             printf("  Last load write compression ratio: %" PRIu64 "\n",
7960                    sg_get_unaligned_be(pl - 4, bp + 4));
7961             break;
7962         case 0x13:
7963             printf("  Last load read compression ratio: %" PRIu64 "\n",
7964                    sg_get_unaligned_be(pl - 4, bp + 4));
7965             break;
7966         case 0x14:
7967             printf("  Medium mount time: %" PRIu64 "\n",
7968                    sg_get_unaligned_be(pl - 4, bp + 4));
7969             break;
7970         case 0x15:
7971             printf("  Medium ready time: %" PRIu64 "\n",
7972                    sg_get_unaligned_be(pl - 4, bp + 4));
7973             break;
7974         case 0x16:
7975             printf("  Total native capacity [MB]: %s\n",
7976                    num_or_unknown(bp + 4, pl - 4, false, b, sizeof(b)));
7977             break;
7978         case 0x17:
7979             printf("  Total used native capacity [MB]: %s\n",
7980                    num_or_unknown(bp + 4, pl - 4, false, b, sizeof(b)));
7981             break;
7982         case 0x1a:
7983             printf("  Volume stop writes of forward wraps: %" PRIu64 "\n",
7984                    sg_get_unaligned_be(pl - 4, bp + 4));
7985             break;
7986         case 0x1b:
7987             printf("  Volume stop writes of backward wraps: %" PRIu64 "\n",
7988                    sg_get_unaligned_be(pl - 4, bp + 4));
7989             break;
7990         case 0x40:
7991             printf("  Volume serial number: %.*s\n", pl - 4, bp + 4);
7992             break;
7993         case 0x41:
7994             printf("  Tape lot identifier: %.*s\n", pl - 4, bp + 4);
7995             break;
7996         case 0x42:
7997             printf("  Volume barcode: %.*s\n", pl - 4, bp + 4);
7998             break;
7999         case 0x43:
8000             printf("  Volume manufacturer: %.*s\n", pl - 4, bp + 4);
8001             break;
8002         case 0x44:
8003             printf("  Volume license code: %.*s\n", pl - 4, bp + 4);
8004             break;
8005         case 0x45:
8006             printf("  Volume personality: %.*s\n", pl - 4, bp + 4);
8007             break;
8008         case 0x80:
8009             printf("  Write protect: %s\n",
8010                    num_or_unknown(bp + 4, pl - 4, false, b, sizeof(b)));
8011             break;
8012         case 0x81:
8013             printf("  WORM: %s\n",
8014                    num_or_unknown(bp + 4, pl - 4, false, b, sizeof(b)));
8015             break;
8016         case 0x82:
8017             printf("  Maximum recommended tape path temperature exceeded: "
8018                    "%s\n", num_or_unknown(bp + 4, pl - 4, false, b,
8019                                           sizeof(b)));
8020             break;
8021         case 0x100:
8022             printf("  Volume write mounts: %" PRIu64 "\n",
8023                    sg_get_unaligned_be(pl - 4, bp + 4));
8024             break;
8025         case 0x101:
8026             printf("  Beginning of medium passes: %" PRIu64 "\n",
8027                    sg_get_unaligned_be(pl - 4, bp + 4));
8028             break;
8029         case 0x102:
8030             printf("  Middle of medium passes: %" PRIu64 "\n",
8031                    sg_get_unaligned_be(pl - 4, bp + 4));
8032             break;
8033         case 0x200:
8034             printf("  Logical position of first encrypted logical object:\n");
8035             volume_stats_partition(bp + 4, pl - 4, true);
8036             break;
8037         case 0x201:
8038             printf("  Logical position of first unencrypted logical object "
8039                    "after first\n  encrypted logical object:\n");
8040             volume_stats_partition(bp + 4, pl - 4, true);
8041             break;
8042         case 0x202:
8043             printf("  Native capacity partition(s) [MB]:\n");
8044             volume_stats_partition(bp + 4, pl - 4, false);
8045             break;
8046         case 0x203:
8047             printf("  Used native capacity partition(s) [MB]:\n");
8048             volume_stats_partition(bp + 4, pl - 4, false);
8049             break;
8050         case 0x204:
8051             printf("  Remaining native capacity partition(s) [MB]:\n");
8052             volume_stats_partition(bp + 4, pl - 4, false);
8053             break;
8054         case 0x300:
8055             printf("  Mount history:\n");
8056             volume_stats_history(bp + 4, pl - 4);
8057             break;
8058 
8059         default:
8060             if (pc >= 0xf000) {
8061                     if (op->exclude_vendor) {
8062                     skip_out = true;
8063                     if ((op->verbose > 0) && (0 == op->do_brief) &&
8064                         (! evsm_output)) {
8065                         evsm_output = true;
8066                         printf("  Vendor specific parameter(s) being "
8067                                "ignored\n");
8068                     }
8069                 } else
8070                     printf("  Vendor specific %s (0x%x), payload in hex\n",
8071                            param_c, pc);
8072             } else
8073                 printf("  Reserved %s (0x%x), payload in hex\n", param_c, pc);
8074             if (skip_out)
8075                 skip_out = false;
8076             else
8077                 hex2fp(bp + 4, pl - 4, "    ", op->hex2str_oformat, stdout);
8078             break;
8079         }
8080         if (op->do_pcb)
8081             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
8082 filter_chk:
8083         if (op->filter_given)
8084             break;
8085 skip:
8086         num -= pl;
8087         bp += pl;
8088     }
8089     return true;
8090 }
8091 
8092 /* TAPE_ALERT_LPAGE [0x2e] <ta> */
8093 static bool
show_tape_alert_ssc_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)8094 show_tape_alert_ssc_page(const uint8_t * resp, int len,
8095                          struct opts_t * op, sgj_opaque_p jop)
8096 {
8097     int num, pl, pc, flag;
8098     const uint8_t * bp;
8099     char str[PCB_STR_LEN];
8100 
8101 if (jop) { };
8102     /* N.B. the Tape alert log page for smc-3 is different */
8103     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
8104         printf("Tape alert page (ssc-3) [0x2e]\n");
8105     num = len - 4;
8106     bp = &resp[0] + 4;
8107     while (num > 3) {
8108         pc = sg_get_unaligned_be16(bp + 0);
8109         pl = bp[3] + 4;
8110         if (op->filter_given) {
8111             if (pc != op->filter)
8112                 goto skip;
8113         }
8114         if (op->do_raw) {
8115             dStrRaw(bp, pl);
8116             goto filter_chk;
8117         } else if (op->do_hex) {
8118             hex2stdout(bp, pl, op->dstrhex_no_ascii);
8119             goto filter_chk;
8120         }
8121         flag = bp[4] & 1;
8122         if (op->verbose && (0 == op->do_brief) && flag)
8123             printf("  >>>> ");
8124         if ((0 == op->do_brief) || op->verbose || flag) {
8125             if (NULL == sg_lib_tapealert_strs[0])
8126                 printf("  No string available for code 0x%x, flag: %d\n",
8127                        pc, flag);
8128             else if (pc <= 0x40)
8129                 printf("  %s: %d\n", sg_lib_tapealert_strs[pc], flag);
8130             else
8131                 printf("  Reserved %s 0x%x, flag: %d\n", param_c, pc, flag);
8132         }
8133         if (op->do_pcb)
8134             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
8135 filter_chk:
8136         if (op->filter_given)
8137             break;
8138 skip:
8139         num -= pl;
8140         bp += pl;
8141     }
8142     return true;
8143 }
8144 
8145 /* 0x37 */
8146 static bool
show_seagate_cache_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)8147 show_seagate_cache_page(const uint8_t * resp, int len,
8148                         struct opts_t * op, sgj_opaque_p jop)
8149 {
8150     bool skip = false;
8151     int num, pl, pc;
8152     int bsti = 0;
8153     const uint8_t * bp;
8154     char str[PCB_STR_LEN];
8155 
8156 if (jop) { };
8157     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) {
8158         if (resp[1] > 0) {
8159             printf("Suspicious page 0x37, SPF=0 but subpage=0x%x\n", resp[1]);
8160             if (op->verbose)
8161                 printf("... try vendor=wdc\n");
8162             if (op->do_brief > 0)
8163                 return true;
8164         } else
8165             printf("Seagate cache page [0x37]\n");
8166     }
8167     num = len - 4;
8168     bp = &resp[0] + 4;
8169     while (num > 3) {
8170         pc = sg_get_unaligned_be16(bp + 0);
8171         pl = bp[3] + 4;
8172         if (op->filter_given) {
8173             if (pc != op->filter)
8174                 goto skip;
8175         }
8176         if (op->do_raw) {
8177             dStrRaw(bp, pl);
8178             goto filter_chk;
8179         } else if (op->do_hex) {
8180             hex2stdout(bp, pl, op->dstrhex_no_ascii);
8181             goto filter_chk;
8182         }
8183         switch (pc) {
8184         case 0:
8185             ++bsti;
8186             if (bsti < 2)
8187                 printf("  Blocks sent to initiator");
8188             else
8189                 skip = true;
8190             break;
8191         case 1:
8192             printf("  Blocks received from initiator");
8193             break;
8194         case 2:
8195             printf("  Blocks read from cache and sent to initiator");
8196             break;
8197         case 3:
8198             printf("  Number of read and write commands whose size "
8199                    "<= segment size");
8200             break;
8201         case 4:
8202             printf("  Number of read and write commands whose size "
8203                    "> segment size");
8204             break;
8205         default:
8206             printf("  Unknown Seagate %s = 0x%x", param_c, pc);
8207             break;
8208         }
8209         if (skip)
8210             skip = false;
8211         else {
8212             printf(" = %" PRIu64 "", sg_get_unaligned_be(pl - 4, bp + 4));
8213             printf("\n");
8214             if (op->do_pcb)
8215                 printf("        <%s>\n", get_pcb_str(bp[2], str,
8216                        sizeof(str)));
8217         }
8218 filter_chk:
8219         if (op->filter_given)
8220             break;
8221 skip:
8222         num -= pl;
8223         bp += pl;
8224     }
8225     return true;
8226 }
8227 
8228 /* 0x37 */
8229 static bool
show_hgst_misc_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)8230 show_hgst_misc_page(const uint8_t * resp, int len, struct opts_t * op,
8231                     sgj_opaque_p jop)
8232 {
8233     bool valid = false;
8234     int num, pl, pc;
8235     const uint8_t * bp;
8236     char str[PCB_STR_LEN];
8237 
8238 if (jop) { };
8239     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
8240         printf("HGST/WDC miscellaneous page [0x37, 0x%x]\n",
8241                op->decod_subpg_code);
8242     num = len - 4;
8243     if (num < 0x30) {
8244         printf("HGST/WDC miscellaneous page too short (%d) < 48\n", num);
8245         return valid;
8246     }
8247     bp = &resp[0] + 4;
8248     while (num > 3) {
8249         pc = sg_get_unaligned_be16(bp + 0);
8250         pl = bp[3] + 4;
8251         if (op->filter_given) {
8252             if (pc != op->filter)
8253                 goto skip;
8254         }
8255         if (op->do_raw) {
8256             dStrRaw(bp, pl);
8257             goto filter_chk;
8258         } else if (op->do_hex) {
8259             hex2stdout(bp, pl, op->dstrhex_no_ascii);
8260             goto filter_chk;
8261         }
8262         switch (pc) {
8263         case 0:
8264             valid = true;
8265             printf("  Power on hours = %u\n", sg_get_unaligned_be32(bp + 4));
8266             printf("  Total Bytes Read = %" PRIu64 "\n",
8267                    sg_get_unaligned_be64(bp + 8));
8268             printf("  Total Bytes Written = %" PRIu64 "\n",
8269                    sg_get_unaligned_be64(bp + 16));
8270             printf("  Max Drive Temp (Celsius) = %u\n", bp[24]);
8271             printf("  GList Size = %u\n", sg_get_unaligned_be16(bp + 25));
8272             printf("  Number of Information Exceptions = %u\n", bp[27]);
8273             printf("  MED EXC = %u\n", !! (0x80 & bp[28]));
8274             printf("  HDW EXC = %u\n", !! (0x40 & bp[28]));
8275             printf("  Total Read Commands = %" PRIu64 "\n",
8276                    sg_get_unaligned_be64(bp + 29));
8277             printf("  Total Write Commands = %" PRIu64 "\n",
8278                    sg_get_unaligned_be64(bp + 37));
8279             printf("  Flash Correction Count = %u\n",
8280                    sg_get_unaligned_be16(bp + 46));
8281             break;
8282         default:
8283             valid = false;
8284             printf("  Unknown HGST/WDC %s = 0x%x", param_c, pc);
8285             break;
8286         }
8287         if (op->do_pcb)
8288             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
8289 filter_chk:
8290         if (op->filter_given)
8291             break;
8292 skip:
8293         num -= pl;
8294         bp += pl;
8295     }
8296     return valid;
8297 }
8298 
8299 /* 0x3e */
8300 static bool
show_seagate_factory_page(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)8301 show_seagate_factory_page(const uint8_t * resp, int len,
8302                           struct opts_t * op, sgj_opaque_p jop)
8303 {
8304     bool valid = false;
8305     int num, pl, pc;
8306     const uint8_t * bp;
8307     uint64_t ull;
8308     char str[PCB_STR_LEN];
8309 
8310 if (jop) { };
8311     if (op->verbose || ((! op->do_raw) && (0 == op->do_hex)))
8312         printf("Seagate/Hitachi factory page [0x3e]\n");
8313     num = len - 4;
8314     bp = &resp[0] + 4;
8315     while (num > 3) {
8316         pc = sg_get_unaligned_be16(bp + 0);
8317         pl = bp[3] + 4;
8318         if (op->filter_given) {
8319             if (pc != op->filter)
8320                 goto skip;
8321         }
8322         if (op->do_raw) {
8323             dStrRaw(bp, pl);
8324             goto filter_chk;
8325         } else if (op->do_hex) {
8326             hex2stdout(bp, pl, op->dstrhex_no_ascii);
8327             goto filter_chk;
8328         }
8329         valid = true;
8330         switch (pc) {
8331         case 0:
8332             printf("  number of hours powered up");
8333             break;
8334         case 8:
8335             printf("  number of minutes until next internal SMART test");
8336             break;
8337         default:
8338             valid = false;
8339             printf("  Unknown Seagate/Hitachi %s = 0x%x", param_c, pc);
8340             break;
8341         }
8342         if (valid) {
8343             ull = sg_get_unaligned_be(pl - 4, bp + 4);
8344             if (0 == pc)
8345                 printf(" = %.2f", ((double)ull) / 60.0 );
8346             else
8347                 printf(" = %" PRIu64 "", ull);
8348         }
8349         printf("\n");
8350         if (op->do_pcb)
8351             printf("        <%s>\n", get_pcb_str(bp[2], str, sizeof(str)));
8352 filter_chk:
8353         if (op->filter_given)
8354             break;
8355 skip:
8356         num -= pl;
8357         bp += pl;
8358     }
8359     return true;
8360 }
8361 
8362 static void
decode_page_contents(const uint8_t * resp,int len,struct opts_t * op,sgj_opaque_p jop)8363 decode_page_contents(const uint8_t * resp, int len, struct opts_t * op,
8364                      sgj_opaque_p jop)
8365 {
8366     int pg_code, subpg_code, vpn;
8367     bool spf;
8368     bool done = false;
8369     const struct log_elem * lep;
8370 
8371     if (len < 3) {
8372         pr2serr("%s: response has bad length: %d\n", __func__, len);
8373         return;
8374     }
8375     spf = !!(resp[0] & 0x40);
8376     pg_code = resp[0] & 0x3f;
8377     if ((VP_HITA == op->vend_prod_num) && (pg_code >= 0x30))
8378         subpg_code = resp[1];   /* Hitachi don't set SPF on VS pages */
8379     else
8380         subpg_code = spf ? resp[1] : NOT_SPG_SUBPG;
8381     op->decod_subpg_code = subpg_code;
8382     if ((SUPP_SPGS_SUBPG == subpg_code) && (SUPP_PAGES_LPAGE != pg_code)) {
8383         done = show_supported_pgs_sub_page(resp, len, op, jop);
8384         if (done)
8385             return;
8386     }
8387     vpn = (op->vend_prod_num >= 0) ? op->vend_prod_num : op->deduced_vpn;
8388     lep = pg_subpg_pdt_search(pg_code, subpg_code, op->dev_pdt, vpn);
8389 
8390     /* Below is the indirect function call to all the show_* functions */
8391     if (lep && lep->show_pagep)
8392         done = (*lep->show_pagep)(resp, len, op, jop);
8393 
8394     if (! done) {
8395         if (0 == op->do_hex) {
8396             static const char * unable_s = "Unable to decode page = 0x";
8397 
8398             if (subpg_code > 0)
8399                 printf("%s%x, subpage = 0x%x, here is hex:\n", unable_s,
8400                        pg_code, subpg_code);
8401             else
8402                 printf("%s%x, here is hex:\n", unable_s, pg_code);
8403         }
8404         if ((len > 128) && (0 == op->do_hex)) {
8405             hex2fp(resp, 64, "  ", op->hex2str_oformat, stdout);
8406             printf("  .....  [truncated after 64 of %d bytes (use '-H' to "
8407                    "see the rest)]\n", len);
8408         } else {
8409             if (0 == op->do_hex)
8410                 hex2fp(resp, len, "  ", op->hex2str_oformat, stdout);
8411             else
8412                 hex2stdout(resp, len, op->dstrhex_no_ascii);
8413         }
8414     }
8415 }
8416 
8417 /* Tries to fetch the TEMPERATURE_LPAGE [0xd] page first. If that fails
8418  * tries to get the Informational Exceptions (IE_LPAGE) page. */
8419 static int
fetchTemperature(int sg_fd,uint8_t * resp,int max_len,struct opts_t * op,sgj_opaque_p jop)8420 fetchTemperature(int sg_fd, uint8_t * resp, int max_len, struct opts_t * op,
8421                  sgj_opaque_p jop)
8422 {
8423     int len;
8424     int res = 0;
8425 
8426     op->pg_code = TEMPERATURE_LPAGE;
8427     op->subpg_code = NOT_SPG_SUBPG;
8428     res = do_logs(sg_fd, resp, max_len, op);
8429     if (0 == res) {
8430         len = sg_get_unaligned_be16(resp + 2) + 4;
8431         if (op->do_raw)
8432             dStrRaw(resp, len);
8433         else if (op->do_hex)
8434             hex2stdout(resp, len, op->dstrhex_no_ascii);
8435         else
8436             show_temperature_page(resp, len, op, jop);
8437     } else if (SG_LIB_CAT_NOT_READY == res)
8438         pr2serr("Device not ready\n");
8439     else {
8440         op->pg_code = IE_LPAGE;
8441         res = do_logs(sg_fd, resp, max_len, op);
8442         if (0 == res) {
8443             len = sg_get_unaligned_be16(resp + 2) + 4;
8444             if (op->do_raw)
8445                 dStrRaw(resp, len);
8446             else if (op->do_hex)
8447                 hex2stdout(resp, len, op->dstrhex_no_ascii);
8448             else
8449                 show_ie_page(resp, len, op, jop);
8450         } else
8451             pr2serr("Unable to find temperature in either Temperature or "
8452                     "IE log page\n");
8453     }
8454     sg_cmds_close_device(sg_fd);
8455     return (res >= 0) ? res : SG_LIB_CAT_OTHER;
8456 }
8457 
8458 /* Returns 0 if successful else SG_LIB_SYNTAX_ERROR. */
8459 static int
decode_pg_arg(struct opts_t * op)8460 decode_pg_arg(struct opts_t * op)
8461 {
8462     int nn;
8463     const struct log_elem * lep;
8464     char * cp;
8465 
8466     if (isalpha((uint8_t)op->pg_arg[0])) {
8467         char b[80];
8468 
8469         if (strlen(op->pg_arg) >= (sizeof(b) - 1)) {
8470             pr2serr("argument to '--page=' is too long\n");
8471             return SG_LIB_SYNTAX_ERROR;
8472         }
8473         strcpy(b, op->pg_arg);
8474         cp = (char *)strchr(b, ',');
8475         if (cp)
8476             *cp = '\0';
8477         lep = acron_search(b);
8478         if (NULL == lep) {
8479             pr2serr("bad argument to '--page=' no acronyn match to "
8480                     "'%s'\n", b);
8481             pr2serr("  Try using '-e' or'-ee' to see available "
8482                     "acronyns\n");
8483             return SG_LIB_SYNTAX_ERROR;
8484         }
8485         op->lep = lep;
8486         op->pg_code = lep->pg_code;
8487         if (cp) {
8488             nn = sg_get_num_nomult(cp + 1);
8489             if ((nn < 0) || (nn > 255)) {
8490                 pr2serr("Bad second value in argument to "
8491                         "'--page='\n");
8492                 return SG_LIB_SYNTAX_ERROR;
8493             }
8494             op->subpg_code = nn;
8495         } else
8496             op->subpg_code = lep->subpg_code;
8497     } else { /* numeric arg: either 'pg_num' or 'pg_num,subpg_num' */
8498         int n;
8499 
8500         cp = (char *)strchr(op->pg_arg, ',');
8501         n = sg_get_num_nomult(op->pg_arg);
8502         if ((n < 0) || (n > 63)) {
8503             pr2serr("Bad argument to '--page='\n");
8504             usage(1);
8505             return SG_LIB_SYNTAX_ERROR;
8506         }
8507         if (cp) {
8508             nn = sg_get_num_nomult(cp + 1);
8509             if ((nn < 0) || (nn > 255)) {
8510                 pr2serr("Bad second value in argument to "
8511                         "'--page='\n");
8512                 usage(1);
8513                 return SG_LIB_SYNTAX_ERROR;
8514             }
8515         } else
8516             nn = 0;
8517         op->pg_code = n;
8518         op->subpg_code = nn;
8519     }
8520     return 0;
8521 }
8522 
8523 /* Since the Supported subpages page is sitting in the rsp_buff which is
8524  * MX_ALLOC_LEN bytes long (~ 64 KB) then move it (from rsp_buff+0 to
8525  * rsp_buff+pg_len-1) to the top end of that buffer. Then there is room
8526  * to merge supp_pgs_rsp with the supported subpages with the result back
8527  * at the bottom of rsp_buff. The new length of the merged subpages page
8528  * (excluding its 4 byte header) is returned.
8529  * Assumes both pages are in ascending order (as required by SPC-4). */
8530 static int
merge_both_supported(const uint8_t * supp_pgs_p,int su_p_pg_len,int pg_len)8531 merge_both_supported(const uint8_t * supp_pgs_p, int su_p_pg_len, int pg_len)
8532 {
8533     uint8_t pg;
8534     int k, kp, ks;
8535     int max_blen = (2 * su_p_pg_len) + pg_len;
8536     uint8_t * m_buff = rsp_buff + (rsp_buff_sz - pg_len);
8537     uint8_t * r_buff = rsp_buff + 4;
8538 
8539     if (pg_len > 0)
8540         memmove(m_buff, rsp_buff + 4, pg_len);
8541     for (k = 0, kp = 0, ks = 0; k < max_blen; k += 2) {
8542         if (kp < su_p_pg_len)
8543             pg = supp_pgs_p[kp];
8544         else
8545             pg = 0xff;
8546         if (ks < pg_len) {
8547             if (m_buff[ks] < pg) {
8548                 r_buff[k] = m_buff[ks];
8549                 r_buff[k + 1] = m_buff[ks + 1];
8550                 ks += 2;
8551             } else if ((m_buff[ks] == pg) && (m_buff[ks + 1] == 0)) {
8552                 r_buff[k] = m_buff[ks];
8553                 r_buff[k + 1] = m_buff[ks + 1];
8554                 ks += 2;
8555                 ++kp;
8556             } else {
8557                 r_buff[k] = pg;
8558                 r_buff[k + 1] = 0;
8559                 ++kp;
8560             }
8561         } else {
8562             if (0xff == pg)
8563                 break;
8564             r_buff[k] = pg;
8565             r_buff[k + 1] = 0;
8566             ++kp;
8567         }
8568     }
8569     sg_put_unaligned_be16(k, rsp_buff + 2);
8570     return k;
8571 }
8572 
8573 
8574 int
main(int argc,char * argv[])8575 main(int argc, char * argv[])
8576 {
8577     bool as_json;
8578     int k, nn, pg_len, res, vb;
8579     int resp_len = 0;
8580     int su_p_pg_len = 0;
8581     int in_len = -1;
8582     int sg_fd = -1;
8583     int ret = 0;
8584     uint8_t * parr;
8585     uint8_t * free_parr = NULL;
8586     struct opts_t * op;
8587     sgj_state * jsp;
8588     sgj_opaque_p jop = NULL;
8589     struct sg_simple_inquiry_resp inq_out;
8590     struct opts_t opts SG_C_CPP_ZERO_INIT;
8591     uint8_t supp_pgs_rsp[256];
8592     char b[128];
8593     static const int blen = sizeof(b);
8594 
8595     op = &opts;
8596     /* N.B. some disks only give data for current cumulative */
8597     op->page_control = 1;
8598     op->dev_pdt = -1;
8599     op->vend_prod_num = VP_NONE;
8600     op->deduced_vpn = VP_NONE;
8601     res = parse_cmd_line(op, argc, argv);
8602     if (res)
8603         return SG_LIB_SYNTAX_ERROR;
8604     if (op->do_help) {
8605         usage_for(op->do_help, op);
8606         return 0;
8607     }
8608     jsp = &op->json_st;
8609     as_json = jsp->pr_as_json;
8610     if (as_json) {
8611         if (op->do_name) {
8612             pr2serr(">>> The --json option is superior to the --name "
8613                     "option.\n");
8614             pr2serr(">>> Ignoring the --name option.\n");
8615             op->do_name = false;
8616         }
8617         jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
8618     }
8619 #ifdef DEBUG
8620     pr2serr("In DEBUG mode, ");
8621     if (op->verbose_given && op->version_given) {
8622         pr2serr("but override: '-vV' given, zero verbose and continue\n");
8623         op->verbose_given = false;
8624         op->version_given = false;
8625         op->verbose = 0;
8626     } else if (! op->verbose_given) {
8627         pr2serr("set '-vv'\n");
8628         op->verbose = 2;
8629     } else
8630         pr2serr("keep verbose=%d\n", op->verbose);
8631 #else
8632     if (op->verbose_given && op->version_given)
8633         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
8634 #endif
8635     if (op->version_given) {
8636         pr2serr("Version string: %s\n", version_str);
8637         return 0;
8638     }
8639     if (op->do_hex > 0) {
8640         if (op->do_hex > 2) {
8641             op->dstrhex_no_ascii = -1;
8642             op->hex2str_oformat = 1;
8643         } else {
8644             op->dstrhex_no_ascii = (1 == op->do_hex);
8645             op->hex2str_oformat = (1 == op->do_hex);
8646         }
8647     } else {
8648         if (op->undefined_hex > 0) {
8649             if (op->undefined_hex > 2) {
8650                 op->dstrhex_no_ascii = -1;
8651                 op->hex2str_oformat = 1;
8652             } else {
8653                 op->dstrhex_no_ascii = (1 == op->undefined_hex);
8654                 op->hex2str_oformat = (1 == op->undefined_hex);
8655             }
8656         } else {       /* default when no --hex nor --undefined */
8657             op->dstrhex_no_ascii = -1;
8658             op->hex2str_oformat = 1;
8659         }
8660     }
8661     vb = op->verbose;
8662     if (op->vend_prod) {
8663         if (0 == memcmp("-1", op->vend_prod,3))
8664             k = VP_NONE;
8665         else if (isdigit((uint8_t)op->vend_prod[0]))
8666             k = sg_get_num_nomult(op->vend_prod);
8667         else
8668             k = find_vpn_by_acron(op->vend_prod);
8669         op->vend_prod_num = k;
8670         if (VP_ALL == k)
8671             ;
8672         else if ((k < 0) || (k > (32 - MVP_OFFSET))) {
8673             pr2serr("Bad vendor/product acronym after '--vendor=' "
8674                     " ('-M ') option\n");
8675             enumerate_vp();
8676             return SG_LIB_SYNTAX_ERROR;
8677         }
8678     }
8679     if (op->do_enumerate > 0) {
8680         if (op->device_name && vb)
8681             pr2serr("Warning: device: %s is being ignored\n",
8682                     op->device_name);
8683         enumerate_pages(op);
8684         return 0;
8685     }
8686     if (op->in_fn) {
8687         if (op->maxlen_given) {
8688             if (op->maxlen > MX_INLEN_ALLOC_LEN) {
8689                 pr2serr("bad argument to '--maxlen=' when --in= given, from "
8690                         "2 to %d (inclusive) expected\n", MX_INLEN_ALLOC_LEN);
8691                 return SG_LIB_SYNTAX_ERROR;
8692             }
8693             rsp_buff_sz = op->maxlen;
8694         } else
8695             rsp_buff_sz = DEF_INLEN_ALLOC_LEN;
8696     } else {
8697         if (op->maxlen_given) {
8698             if (op->maxlen > MX_ALLOC_LEN) {
8699                 pr2serr("bad argument to '--maxlen=', from 2 to 65535 "
8700                         "(inclusive) expected\n");
8701                 return SG_LIB_SYNTAX_ERROR;
8702             }
8703             rsp_buff_sz = op->maxlen;
8704         }
8705     }
8706     rsp_buff = sg_memalign(rsp_buff_sz, 0 /* page aligned */, &free_rsp_buff,
8707                            false);
8708     if (NULL == rsp_buff) {
8709         pr2serr("Unable to allocate %d bytes on the heap\n", rsp_buff_sz);
8710         ret = sg_convert_errno(ENOMEM);
8711         goto err_out;
8712     }
8713     if (NULL == op->device_name) {
8714         if (op->in_fn) {
8715             bool found = false;
8716             bool r_spf = false;
8717             uint16_t u;
8718             int pg_code, subpg_code, pdt, n;
8719             const struct log_elem * lep;
8720             const uint8_t * bp;
8721 
8722             if ((ret = sg_f2hex_arr(op->in_fn, op->do_raw, false, rsp_buff,
8723                                     &in_len, rsp_buff_sz)))
8724                 goto err_out;
8725             if (vb > 2)
8726                 pr2serr("Read %d [0x%x] bytes of user supplied data\n",
8727                         in_len, in_len);
8728             if (op->do_raw)
8729                 op->do_raw = false;    /* can interfere on decode */
8730             if (in_len < 4) {
8731                 pr2serr("--in=%s only decoded %d bytes (needs 4 at least)\n",
8732                         op->in_fn, in_len);
8733                 ret = SG_LIB_SYNTAX_ERROR;
8734                 goto err_out;
8735             }
8736             if (op->pg_arg) {
8737                 char b[144];
8738                 char * cp;
8739 
8740                 strcpy(b, op->pg_arg);
8741                 cp = (char *)strchr(b, ',');
8742                 if (cp)
8743                     *cp = '\0';
8744                 lep = acron_search(b);
8745                 if (NULL == lep) {
8746                     pr2serr("bad argument to '--page=' no acronyn match to "
8747                             "'%s'\n", b);
8748                     pr2serr("  Try using '-e' or'-ee' to see available "
8749                             "acronyns\n");
8750                     return SG_LIB_SYNTAX_ERROR;
8751                 }
8752                 op->lep = lep;
8753                 op->pg_code = lep->pg_code;
8754                 op->subpg_code = lep->subpg_code;
8755                 if (op->subpg_code > 0)
8756                     r_spf = true;
8757             }
8758 
8759             for (bp = rsp_buff, k = 0; k < in_len; bp += n, k += n) {
8760                 bool spf = !! (bp[0] & 0x40);
8761 
8762                 pg_code = bp[0] & 0x3f;
8763                 subpg_code = spf ? bp[1] : NOT_SPG_SUBPG;
8764                 u = sg_get_unaligned_be16(bp + 2);
8765                 n = u + 4;
8766                 if (n > (in_len - k)) {
8767                     pr2serr("bytes decoded remaining (%d) less than lpage "
8768                             "length (%d), try decoding anyway\n", in_len - k,
8769                             n);
8770                     n = in_len - k;
8771                 }
8772                 if (op->pg_arg) {
8773                     if ((NOT_SPG_SUBPG == op->subpg_code) && spf) {
8774                         continue;
8775                     } else if ((! spf) && (! r_spf)) {
8776                         if (pg_code != op->pg_code)
8777                             continue;
8778                     } else if ((SUPP_SPGS_SUBPG == op->subpg_code) &&
8779                              (SUPP_PAGES_LPAGE != op->pg_code)) {
8780                         if (pg_code != op->pg_code)
8781                             continue;
8782                     } else if ((SUPP_SPGS_SUBPG != op->subpg_code) &&
8783                                (SUPP_PAGES_LPAGE == op->pg_code)) {
8784                         if (subpg_code != op->subpg_code)
8785                             continue;
8786                     } else if ((SUPP_SPGS_SUBPG != op->subpg_code) &&
8787                                (SUPP_PAGES_LPAGE != op->pg_code)) {
8788                         if ((pg_code != op->pg_code) ||
8789                             (subpg_code != op->subpg_code))
8790                             continue;
8791                     }
8792                 }
8793                 if (op->exclude_vendor && (pg_code >= 0x30))
8794                     continue;
8795                 found = true;
8796                 if (op->do_hex > 2) {
8797                      hex2fp(bp, n, NULL, op->hex2str_oformat, stdout);
8798                      continue;
8799                 }
8800                 pdt = op->dev_pdt;
8801                 lep = pg_subpg_pdt_search(pg_code, subpg_code, pdt,
8802                                           op->vend_prod_num);
8803                 if (lep) {
8804                     /* Below is the indirect function call to all the
8805                      * show_* functions */
8806                     if (lep->show_pagep)
8807                         (*lep->show_pagep)(bp, n, op, jop);
8808                     else
8809                         sgj_pr_hr(jsp, "Unable to decode %s [%s]\n",
8810                                   lep->name, lep->acron);
8811                 } else {
8812                     nn = sg_scnpr(b, blen, "Unable to decode page=0x%x",
8813                                   pg_code);
8814                     if (subpg_code > 0)
8815                         sg_scnpr(b + nn, blen - nn, ", subpage=0x%x",
8816                                  subpg_code);
8817                     if (pdt >= 0)
8818                         sg_scnpr(b + nn, blen - nn, ", pdt=0x%x\n", pdt);
8819                     sgj_pr_hr(jsp, "%s\n", b);
8820                 }
8821             }           /* end of page/subpage search loop */
8822             if (op->pg_arg && (! found)) {
8823                 nn = sg_scnpr(b, blen, "Unable to find page=0x%x",
8824                               op->pg_code);
8825                 if (op->subpg_code > 0)
8826                     sg_scnpr(b + nn, blen - nn, ", subpage=0x%x",
8827                              op->subpg_code);
8828                 sgj_pr_hr(jsp, "%s\n", b);
8829                 if (jsp->pr_as_json)
8830                     sgj_js_nv_i(jsp, jop, "page_not_found", 1);
8831             }
8832             ret = 0;
8833             goto err_out;
8834         }
8835         if (op->pg_arg) {         /* do this for 'sg_logs -p xxx' */
8836             ret = decode_pg_arg(op);
8837             if (ret)
8838                 goto err_out;
8839         }
8840         pr2serr("No DEVICE argument given\n\n");
8841         usage_for(1, op);
8842         ret = SG_LIB_FILE_ERROR;
8843         goto err_out;
8844     }
8845     if (op->do_select) {
8846         if (op->do_temperature) {
8847             pr2serr("--select cannot be used with --temperature\n");
8848             ret = SG_LIB_CONTRADICT;
8849             goto err_out;
8850         }
8851         if (op->do_transport) {
8852             pr2serr("--select cannot be used with --transport\n");
8853             ret = SG_LIB_CONTRADICT;
8854             goto err_out;
8855         }
8856     } else if (op->do_raw) {
8857         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
8858             perror("sg_set_binary_mode");
8859             ret = SG_LIB_FILE_ERROR;
8860             goto err_out;
8861         }
8862     }
8863     if (op->do_all) {
8864         if (op->do_select) {
8865             pr2serr("--all conflicts with --select\n");
8866             ret = SG_LIB_CONTRADICT;
8867             goto err_out;
8868         }
8869     }
8870     if (op->in_fn) {
8871         if (! op->do_select) {
8872             pr2serr("--in=FN can only be used with --select when DEVICE "
8873                     "given\n");
8874             ret = SG_LIB_CONTRADICT;
8875             goto err_out;
8876         }
8877         if ((ret = sg_f2hex_arr(op->in_fn, op->do_raw, false, rsp_buff,
8878                                 &in_len, rsp_buff_sz)))
8879             goto err_out;
8880         if (vb > 2)
8881             pr2serr("Read %d [0x%x] bytes of user supplied data\n", in_len,
8882                     in_len);
8883     }
8884     if (op->pg_arg) {
8885         if (op->do_all) {
8886             if (0 == op->do_brief)
8887                 pr2serr(">>> warning: --page=%s ignored when --all given\n",
8888                         op->pg_arg);
8889         } else {
8890             ret = decode_pg_arg(op);
8891             if (ret)
8892                 goto err_out;
8893         }
8894     }
8895 
8896 #ifdef SG_LIB_WIN32
8897 #ifdef SG_LIB_WIN32_DIRECT
8898     win32_spt_init_state = !! scsi_pt_win32_spt_state();
8899     if (vb > 4)
8900         pr2serr("Initial win32 SPT interface state: %s\n",
8901                 win32_spt_init_state ? "direct" : "indirect");
8902 #endif
8903 #endif
8904     sg_fd = sg_cmds_open_device(op->device_name, op->o_readonly, vb);
8905     if ((sg_fd < 0) && (! op->o_readonly))
8906         sg_fd = sg_cmds_open_device(op->device_name, true /* ro */, vb);
8907     if (sg_fd < 0) {
8908         pr2serr("error opening file: %s: %s \n", op->device_name,
8909                 safe_strerror(-sg_fd));
8910         ret = sg_convert_errno(-sg_fd);
8911         goto err_out;
8912     }
8913     if (op->do_list || op->do_all) {
8914         op->pg_code = SUPP_PAGES_LPAGE;
8915         if ((op->do_list > 1) || (op->do_all > 1))
8916             op->subpg_code = SUPP_SPGS_SUBPG;
8917     }
8918     if (op->do_transport) {
8919         if ((op->pg_code > 0) || (op->subpg_code > 0) ||
8920             op->do_temperature) {
8921             pr2serr("'-T' should not be mixed with options implying other "
8922                     "pages\n");
8923             ret = SG_LIB_FILE_ERROR;
8924             goto err_out;
8925         }
8926         op->pg_code = PROTO_SPECIFIC_LPAGE;
8927     }
8928 
8929     memset(&inq_out, 0, sizeof(inq_out));
8930     if (op->no_inq < 2) {
8931         if (sg_simple_inquiry(sg_fd, &inq_out, true, vb)) {
8932             pr2serr("%s doesn't respond to a SCSI INQUIRY\n",
8933                     op->device_name);
8934             ret = SG_LIB_CAT_OTHER;
8935             goto err_out;
8936         }
8937         op->dev_pdt = inq_out.peripheral_type;
8938         if ((! op->do_raw) && (0 == op->do_hex) && (! op->do_name) &&
8939             (0 == op->no_inq) && (0 == op->do_brief))
8940             sgj_pr_hr(jsp, "    %.8s  %.16s  %.4s\n", inq_out.vendor,
8941                       inq_out.product, inq_out.revision);
8942         memcpy(t10_vendor_str, inq_out.vendor, 8);
8943         memcpy(t10_product_str, inq_out.product, 16);
8944         if (VP_NONE == op->vend_prod_num)
8945             op->deduced_vpn = find_vpn_by_inquiry();
8946     }
8947 
8948     if (op->do_temperature) {
8949         ret = fetchTemperature(sg_fd, rsp_buff, SHORT_RESP_LEN, op, jop);
8950         goto err_out;
8951     }
8952     if (op->do_select) {
8953         k = sg_ll_log_select(sg_fd, op->do_pcreset, op->do_sp,
8954                              op->page_control, op->pg_code, op->subpg_code,
8955                              rsp_buff, ((in_len > 0) ? in_len : 0), true, vb);
8956         if (k) {
8957             if (SG_LIB_CAT_NOT_READY == k)
8958                 pr2serr("log_select: device not ready\n");
8959             else if (SG_LIB_CAT_ILLEGAL_REQ == k)
8960                 pr2serr("log_select: field in cdb illegal\n");
8961             else if (SG_LIB_CAT_INVALID_OP == k)
8962                 pr2serr("log_select: not supported\n");
8963             else if (SG_LIB_CAT_UNIT_ATTENTION == k)
8964                 pr2serr("log_select: unit attention\n");
8965             else if (SG_LIB_CAT_ABORTED_COMMAND == k)
8966                 pr2serr("log_select: aborted command\n");
8967             else
8968                 pr2serr("log_select: failed (%d), try '-v' for more "
8969                         "information\n", k);
8970         }
8971         ret = (k >= 0) ?  k : SG_LIB_CAT_OTHER;
8972         goto err_out;
8973     }
8974     if (op->do_list > 2) {
8975         const int supp_pgs_blen = sizeof(supp_pgs_rsp);
8976 
8977         op->subpg_code = NOT_SPG_SUBPG;
8978         res = do_logs(sg_fd, supp_pgs_rsp, supp_pgs_blen, op);
8979         if (res != 0)
8980             goto bad;
8981         su_p_pg_len = sg_get_unaligned_be16(supp_pgs_rsp + 2);
8982         if ((su_p_pg_len + 4) > supp_pgs_blen) {
8983             pr2serr("Supported log pages log page is too long [%d], exit\n",
8984                     su_p_pg_len);
8985             res = SG_LIB_CAT_OTHER;
8986             goto bad;
8987         }
8988         op->subpg_code = SUPP_SPGS_SUBPG;
8989     }
8990     resp_len = (op->maxlen > 0) ? op->maxlen : MX_ALLOC_LEN;
8991     res = do_logs(sg_fd, rsp_buff, resp_len, op);
8992     if (0 == res) {
8993         pg_len = sg_get_unaligned_be16(rsp_buff + 2);
8994         if ((pg_len + 4) > resp_len) {
8995             pr2serr("Only fetched %d bytes of response (available: %d "
8996                     "bytes)\n    truncate output\n",
8997                    resp_len, pg_len + 4);
8998             pg_len = resp_len - 4;
8999         }
9000         goto good;
9001     }
9002 bad:
9003     if (SG_LIB_CAT_INVALID_OP == res)
9004         pr2serr("%snot supported\n", ls_s);
9005     else if (SG_LIB_CAT_NOT_READY == res)
9006         pr2serr("%sdevice not ready\n", ls_s);
9007     else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
9008         if ((op->do_list > 2) && (SUPP_SPGS_SUBPG == op->subpg_code)) {
9009             rsp_buff[0] = 0x40;
9010             rsp_buff[1] = SUPP_SPGS_SUBPG;
9011             pg_len = 0;
9012             res = 0;
9013             if (op->verbose)
9014                 pr2serr("%sfield in cdb illegal in [0,0xff], "
9015                         "continue with merge\n", ls_s);
9016             goto good;
9017         } else
9018             pr2serr("%sfield in cdb illegal\n", ls_s);
9019     } else if (SG_LIB_CAT_UNIT_ATTENTION == res)
9020         pr2serr("%sunit attention\n", ls_s);
9021     else if (SG_LIB_CAT_ABORTED_COMMAND == res)
9022         pr2serr("%saborted command\n", ls_s);
9023     else if (SG_LIB_TRANSPORT_ERROR == res)
9024         pr2serr("%stransport error\n", ls_s);
9025     else
9026         pr2serr("%sother error [%d]\n", ls_s, res);
9027     ret = res;
9028     goto err_out;
9029 
9030 good:
9031     if (op->do_list > 2)
9032         pg_len = merge_both_supported(supp_pgs_rsp + 4, su_p_pg_len, pg_len);
9033 
9034     if (0 == op->do_all) {
9035         if (op->filter_given) {
9036             if (op->do_hex > 2)
9037                 hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii);
9038             else
9039                 decode_page_contents(rsp_buff, pg_len + 4, op, jop);
9040         } else if (op->do_raw)
9041             dStrRaw(rsp_buff, pg_len + 4);
9042         else if (op->do_hex > 1)
9043             hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii);
9044         else if (pg_len > 1) {
9045             if (op->do_hex) {
9046                 if (rsp_buff[0] & 0x40)
9047                     printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, "
9048                            "page_len=0x%x\n", rsp_buff[0] & 0x3f, rsp_buff[1],
9049                            !!(rsp_buff[0] & 0x80), pg_len);
9050                 else
9051                     printf("Log page code=0x%x, DS=%d, SPF=0, page_len=0x%x\n",
9052                            rsp_buff[0] & 0x3f, !!(rsp_buff[0] & 0x80), pg_len);
9053                 hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii);
9054             }
9055             else
9056                 decode_page_contents(rsp_buff, pg_len + 4, op, jop);
9057         }
9058     }
9059     ret = res;
9060 
9061     if (op->do_all && (pg_len > 1)) {
9062         int my_len = pg_len;
9063         bool spf;
9064 
9065         parr = sg_memalign(parr_sz, 0, &free_parr, false);
9066         if (NULL == parr) {
9067             pr2serr("Unable to allocate heap for parr\n");
9068             ret = sg_convert_errno(ENOMEM);
9069             goto err_out;
9070         }
9071         spf = !!(rsp_buff[0] & 0x40);
9072         if (my_len > parr_sz) {
9073             pr2serr("Unexpectedly large page_len=%d, trim to %d\n", my_len,
9074                     parr_sz);
9075             my_len = parr_sz;
9076         }
9077         memcpy(parr, rsp_buff + 4, my_len);
9078         for (k = 0; k < my_len; ++k) {
9079             op->pg_code = parr[k] & 0x3f;
9080             if (spf)
9081                 op->subpg_code = parr[++k];
9082             else
9083                 op->subpg_code = NOT_SPG_SUBPG;
9084 
9085             /* Some devices include [pg_code, 0xff] for all pg_code > 0 */
9086             if ((op->pg_code > 0) && (SUPP_SPGS_SUBPG == op->subpg_code))
9087                 continue;       /* skip since no new information */
9088             if ((op->pg_code >= 0x30) && op->exclude_vendor)
9089                 continue;
9090             if (! op->do_raw)
9091                 sgj_pr_hr(jsp, "\n");
9092             res = do_logs(sg_fd, rsp_buff, resp_len, op);
9093             if (0 == res) {
9094                 pg_len = sg_get_unaligned_be16(rsp_buff + 2);
9095                 if ((pg_len + 4) > resp_len) {
9096                     pr2serr("Only fetched %d bytes of response, truncate "
9097                             "output\n", resp_len);
9098                     pg_len = resp_len - 4;
9099                 }
9100                 if (op->do_raw && (! op->filter_given))
9101                     dStrRaw(rsp_buff, pg_len + 4);
9102                 else if (op->do_hex > 4)
9103                     decode_page_contents(rsp_buff, pg_len + 4, op, jop);
9104                 else if (op->do_hex > 1)
9105                     hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii);
9106                 else if (1 == op->do_hex) {
9107                     if (0 == op->do_brief) {
9108                         if (rsp_buff[0] & 0x40)
9109                             printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, "
9110                                    "page_len=0x%x\n", rsp_buff[0] & 0x3f,
9111                                    rsp_buff[1], !!(rsp_buff[0] & 0x80),
9112                                    pg_len);
9113                         else
9114                             printf("Log page code=0x%x, DS=%d, SPF=0, "
9115                                    "page_len=0x%x\n", rsp_buff[0] & 0x3f,
9116                                    !!(rsp_buff[0] & 0x80), pg_len);
9117                     }
9118                     hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii);
9119                 }
9120                 else
9121                     decode_page_contents(rsp_buff, pg_len + 4, op, jop);
9122             } else if (SG_LIB_CAT_INVALID_OP == res)
9123                 pr2serr("%spage=0x%x,0x%x not supported\n", ls_s,
9124                         op->pg_code, op->subpg_code);
9125             else if (SG_LIB_CAT_NOT_READY == res)
9126                 pr2serr("%sdevice not ready\n", ls_s);
9127             else if (SG_LIB_CAT_ILLEGAL_REQ == res)
9128                 pr2serr("%sfield in cdb illegal [page=0x%x,0x%x]\n", ls_s,
9129                         op->pg_code, op->subpg_code);
9130             else if (SG_LIB_CAT_UNIT_ATTENTION == res)
9131                 pr2serr("%sunit attention\n", ls_s);
9132             else if (SG_LIB_CAT_ABORTED_COMMAND == res)
9133                 pr2serr("%saborted command\n", ls_s);
9134             else
9135                 pr2serr("%sfailed, try '-v' for more information\n", ls_s);
9136         }
9137     }
9138 err_out:
9139     if (free_rsp_buff)
9140         free(free_rsp_buff);
9141     if (free_parr)
9142         free(free_parr);
9143     if (sg_fd >= 0)
9144         sg_cmds_close_device(sg_fd);
9145     if (0 == vb) {
9146         if (! sg_if_can2stderr("sg_logs failed: ", ret))
9147             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
9148                     "more information\n");
9149     }
9150     if (as_json) {
9151         if (0 == op->do_hex)
9152             sgj_js2file(jsp, NULL, ret, stdout);
9153         sgj_finish(jsp);
9154     }
9155     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
9156 }
9157