diff options
Diffstat (limited to 'src/sg_logs.c')
-rw-r--r-- | src/sg_logs.c | 9156 |
1 files changed, 9156 insertions, 0 deletions
diff --git a/src/sg_logs.c b/src/sg_logs.c new file mode 100644 index 00000000..ce6a7e98 --- /dev/null +++ b/src/sg_logs.c @@ -0,0 +1,9156 @@ +/* A utility program originally written for the Linux OS SCSI subsystem. + * Copyright (C) 2000-2022 D. Gilbert + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program outputs information provided by a SCSI LOG SENSE command + * and in some cases issues a LOG SELECT command. + * + */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <getopt.h> +#define __STDC_FORMAT_MACROS 1 +#include <inttypes.h> +#include <errno.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "sg_lib.h" +#include "sg_lib_names.h" +#include "sg_cmds_basic.h" +#ifdef SG_LIB_WIN32 +#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */ +#endif +#include "sg_unaligned.h" +#include "sg_pr2serr.h" + +static const char * version_str = "2.08 20221112"; /* spc6r06 + sbc5r03 */ + +#define MY_NAME "sg_logs" + +#define MX_ALLOC_LEN (0xfffc) +#define MX_INLEN_ALLOC_LEN (0x800000) +#define DEF_INLEN_ALLOC_LEN (0x40000) +#define SHORT_RESP_LEN 128 + +#define SUPP_PAGES_LPAGE 0x0 +#define BUFF_OVER_UNDER_LPAGE 0x1 +#define WRITE_ERR_LPAGE 0x2 +#define READ_ERR_LPAGE 0x3 +#define READ_REV_ERR_LPAGE 0x4 +#define VERIFY_ERR_LPAGE 0x5 +#define NON_MEDIUM_LPAGE 0x6 +#define LAST_N_ERR_LPAGE 0x7 +#define FORMAT_STATUS_LPAGE 0x8 +#define LAST_N_DEFERRED_LPAGE 0xb +#define LB_PROV_LPAGE 0xc +#define TEMPERATURE_LPAGE 0xd +#define START_STOP_LPAGE 0xe +#define APP_CLIENT_LPAGE 0xf +#define SELF_TEST_LPAGE 0x10 +#define SOLID_STATE_MEDIA_LPAGE 0x11 +#define REQ_RECOVERY_LPAGE 0x13 +#define DEVICE_STATS_LPAGE 0x14 +#define BACKGROUND_SCAN_LPAGE 0x15 +#define SAT_ATA_RESULTS_LPAGE 0x16 +#define PROTO_SPECIFIC_LPAGE 0x18 +#define STATS_LPAGE 0x19 +#define PCT_LPAGE 0x1a +#define TAPE_ALERT_LPAGE 0x2e +#define IE_LPAGE 0x2f +#define NOT_SPG_SUBPG 0x0 /* any page: no subpages */ +#define SUPP_SPGS_SUBPG 0xff /* all subpages of ... */ +#define PENDING_DEFECTS_SUBPG 0x1 /* page 0x15 */ +#define BACKGROUND_OP_SUBPG 0x2 /* page 0x15 */ +#define CACHE_STATS_SUBPG 0x20 /* page 0x19 */ +#define CMD_DUR_LIMITS_SUBPG 0x21 /* page 0x19 */ +#define ENV_REPORTING_SUBPG 0x1 /* page 0xd */ +#define UTILIZATION_SUBPG 0x1 /* page 0xe */ +#define ENV_LIMITS_SUBPG 0x2 /* page 0xd */ +#define LPS_MISALIGNMENT_SUBPG 0x3 /* page 0x15 */ +#define ZONED_BLOCK_DEV_STATS_SUBPG 0x1 /* page 0x14 */ +#define LAST_N_INQUIRY_DATA_CH_SUBPG 0x1 /* page 0xb */ +#define LAST_N_MODE_PG_DATA_CH_SUBPG 0x2 /* page 0xb */ + +/* Vendor product numbers/identifiers */ +#define VP_NONE (-1) +#define VP_SEAG 0 +#define VP_HITA 1 +#define VP_TOSH 2 +#define VP_LTO5 3 +#define VP_LTO6 4 +#define VP_ALL 99 + +#define MVP_OFFSET 8 + +/* Vendor product masks + * MVP_STD OR-ed with MVP_<vendor> is a T10 defined lpage with vendor + * specific parameter codes (e.g. Information Exceptions lpage [0x2f]) */ +#define MVP_STD (1 << (MVP_OFFSET - 1)) +#define MVP_SEAG (1 << (VP_SEAG + MVP_OFFSET)) +#define MVP_HITA (1 << (VP_HITA + MVP_OFFSET)) +#define MVP_TOSH (1 << (VP_TOSH + MVP_OFFSET)) +#define MVP_LTO5 (1 << (VP_LTO5 + MVP_OFFSET)) +#define MVP_LTO6 (1 << (VP_LTO6 + MVP_OFFSET)) + +#define OVP_LTO (MVP_LTO5 | MVP_LTO6) +#define OVP_ALL (~0) + + +#define PCB_STR_LEN 128 + +#define LOG_SENSE_PROBE_ALLOC_LEN 4 +#define LOG_SENSE_DEF_TIMEOUT 64 /* seconds */ + +static uint8_t * rsp_buff; +static uint8_t * free_rsp_buff; +static int rsp_buff_sz = MX_ALLOC_LEN + 4; +static const int parr_sz = 4096; + +static const char * const unknown_s = "unknown"; +static const char * const not_avail = "not available"; +static const char * const param_c = "Parameter code"; +static const char * const param_c_sn = "parameter_code"; +static const char * const as_s_s = "as_string"; +static const char * const rstrict_s = "restricted"; +static const char * const rsv_s = "reserved"; +static const char * const vend_spec = "vendor specific"; +static const char * const not_rep = "not reported"; +static const char * const in_hex = "in hex"; +static const char * const s_key = "sense key"; + +static struct option long_options[] = { + {"All", no_argument, 0, 'A'}, /* equivalent to '-aa' */ + {"ALL", no_argument, 0, 'A'}, /* equivalent to '-aa' */ + {"all", no_argument, 0, 'a'}, + {"brief", no_argument, 0, 'b'}, + {"control", required_argument, 0, 'c'}, + {"enumerate", no_argument, 0, 'e'}, + {"exclude", no_argument, 0, 'E'}, + {"filter", required_argument, 0, 'f'}, + {"full", no_argument, 0, 'F'}, + {"help", no_argument, 0, 'h'}, + {"hex", no_argument, 0, 'H'}, + {"in", required_argument, 0, 'i'}, + {"inhex", required_argument, 0, 'i'}, + {"json", optional_argument, 0, 'j'}, + {"list", no_argument, 0, 'l'}, + {"maxlen", required_argument, 0, 'm'}, + {"name", no_argument, 0, 'n'}, + {"new", no_argument, 0, 'N'}, + {"no_inq", no_argument, 0, 'x'}, + {"no-inq", no_argument, 0, 'x'}, + {"old", no_argument, 0, 'O'}, + {"page", required_argument, 0, 'p'}, + {"paramp", required_argument, 0, 'P'}, + {"pcb", no_argument, 0, 'q'}, + {"ppc", no_argument, 0, 'Q'}, + {"pdt", required_argument, 0, 'D'}, + {"raw", no_argument, 0, 'r'}, + {"readonly", no_argument, 0, 'X'}, + {"reset", no_argument, 0, 'R'}, + {"sp", no_argument, 0, 's'}, + {"select", no_argument, 0, 'S'}, + {"temperature", no_argument, 0, 't'}, + {"transport", no_argument, 0, 'T'}, + {"undefined", no_argument, 0, 'u'}, + {"vendor", required_argument, 0, 'M'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {0, 0, 0, 0}, +}; + +struct opts_t { + bool do_full; + bool do_name; + bool do_pcb; + bool do_ppc; + bool do_raw; + bool do_pcreset; + bool do_select; + bool do_sp; + bool do_temperature; + bool do_transport; + bool exclude_vendor; + bool filter_given; + bool maxlen_given; + bool o_readonly; + bool opt_new; + bool verbose_given; + bool version_given; + int do_all; + int do_brief; + int do_enumerate; + int do_help; + int do_hex; + int do_list; + int dstrhex_no_ascii; /* value for dStrHex() no_ascii argument */ + int hex2str_oformat; /* value for hex2str() oformat argument */ + int vend_prod_num; /* one of the VP_* constants or -1 (def) */ + int deduced_vpn; /* deduced vendor_prod_num; from INQUIRY, etc */ + int verbose; + int filter; + int page_control; + int maxlen; + int pg_code; + int subpg_code; + int paramp; + int no_inq; + int dev_pdt; /* from device or --pdt=DT */ + int decod_subpg_code; + int undefined_hex; /* hex format of undefined/unrecognized fields */ + const char * device_name; + const char * in_fn; + const char * pg_arg; + const char * vend_prod; + const struct log_elem * lep; + sgj_state json_st; +}; + + +struct log_elem { + int pg_code; + int subpg_code; /* only unless subpg_high>0 then this is only */ + int subpg_high; /* when >0 this is high end of subpage range */ + int pdt; /* -1 for all */ + int flags; /* bit mask; or-ed with MVP_* constants */ + const char * name; + const char * acron; + bool (*show_pagep)(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); + /* Returns true if done */ +}; + +struct vp_name_t { + int vend_prod_num; /* vendor/product identifier */ + const char * acron; + const char * name; + const char * t10_vendorp; + const char * t10_productp; +}; + +static const char * ls_s = "log_sense: "; + +static bool show_supported_pgs_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_supported_pgs_sub_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_buffer_over_under_run_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_error_counter_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_non_medium_error_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_last_n_error_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_format_status_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_last_n_deferred_error_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_last_n_inq_data_ch_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_last_n_mode_pg_data_ch_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_lb_provisioning_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_sequential_access_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_temperature_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_start_stop_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_utilization_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_app_client_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_self_test_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_solid_state_media_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_device_stats_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_media_stats_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_dt_device_status_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_tapealert_response_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_requested_recovery_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_background_scan_results_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_zoned_block_dev_stats(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_pending_defects_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_background_op_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_lps_misalignment_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_element_stats_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_service_buffer_info_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_ata_pt_results_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_tape_diag_data_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_mchanger_diag_data_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_non_volatile_cache_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_volume_stats_pages(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_protocol_specific_port_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_stats_perform_pages(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_cache_stats_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_power_condition_transitions_page(const uint8_t * resp, + int len, struct opts_t * op, + sgj_opaque_p jop); +static bool show_environmental_reporting_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_environmental_limits_page(const uint8_t * resp, int len, + struct opts_t * op, + sgj_opaque_p jop); +static bool show_cmd_dur_limits_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_data_compression_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_tape_alert_ssc_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_ie_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_tape_usage_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_tape_capacity_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_seagate_cache_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_seagate_factory_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_hgst_perf_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); +static bool show_hgst_misc_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop); + +/* elements in page_number/subpage_number order */ +static struct log_elem log_arr[] = { + {SUPP_PAGES_LPAGE, 0, 0, -1, MVP_STD, "Supported log pages", "sp", + show_supported_pgs_page}, /* 0, 0 */ + {SUPP_PAGES_LPAGE, SUPP_SPGS_SUBPG, 0, -1, MVP_STD, "Supported log pages " + "and subpages", "ssp", show_supported_pgs_sub_page}, /* 0, 0xff */ + {BUFF_OVER_UNDER_LPAGE, 0, 0, -1, MVP_STD, "Buffer over-run/under-run", + "bou", show_buffer_over_under_run_page}, /* 0x1, 0x0 */ + {WRITE_ERR_LPAGE, 0, 0, -1, MVP_STD, "Write error counters", "we", + show_error_counter_page}, /* 0x2, 0x0 */ + {READ_ERR_LPAGE, 0, 0, -1, MVP_STD, "Read error counters", "re", + show_error_counter_page}, /* 0x3, 0x0 */ + {READ_REV_ERR_LPAGE, 0, 0, -1, MVP_STD, "Read reverse error counters", + "rre", show_error_counter_page}, /* 0x4, 0x0 */ + {VERIFY_ERR_LPAGE, 0, 0, -1, MVP_STD, "Verify error counters", "ve", + show_error_counter_page}, /* 0x5, 0x0 */ + {NON_MEDIUM_LPAGE, 0, 0, -1, MVP_STD, "Non medium", "nm", + show_non_medium_error_page}, /* 0x6, 0x0 */ + {LAST_N_ERR_LPAGE, 0, 0, -1, MVP_STD, "Last n error", "lne", + show_last_n_error_page}, /* 0x7, 0x0 */ + {FORMAT_STATUS_LPAGE, 0, 0, 0, MVP_STD, "Format status", "fs", + show_format_status_page}, /* 0x8, 0x0 SBC */ + {LAST_N_DEFERRED_LPAGE, 0, 0, -1, MVP_STD, "Last n deferred error", "lnd", + show_last_n_deferred_error_page}, /* 0xb, 0x0 */ + {LAST_N_DEFERRED_LPAGE, LAST_N_INQUIRY_DATA_CH_SUBPG, 0, -1, MVP_STD, + "Last n inquiry data changed", "lnic", + show_last_n_inq_data_ch_page}, /* 0xb, 0x1 */ + {LAST_N_DEFERRED_LPAGE, LAST_N_MODE_PG_DATA_CH_SUBPG, 0, -1, MVP_STD, + "Last n mode page data changed", "lnmc", + show_last_n_mode_pg_data_ch_page}, /* 0xb, 0x2 */ + {LB_PROV_LPAGE, 0, 0, 0, MVP_STD, "Logical block provisioning", "lbp", + show_lb_provisioning_page}, /* 0xc, 0x0 SBC */ + {0xc, 0, 0, PDT_TAPE, MVP_STD, "Sequential access device", "sad", + show_sequential_access_page}, /* 0xc, 0x0 SSC */ + {TEMPERATURE_LPAGE, 0, 0, -1, MVP_STD, "Temperature", "temp", + show_temperature_page}, /* 0xd, 0x0 */ + {TEMPERATURE_LPAGE, ENV_REPORTING_SUBPG, 0, -1, MVP_STD, /* 0xd, 0x1 */ + "Environmental reporting", "enr", show_environmental_reporting_page}, + {TEMPERATURE_LPAGE, ENV_LIMITS_SUBPG, 0, -1, MVP_STD, /* 0xd, 0x2 */ + "Environmental limits", "enl", show_environmental_limits_page}, + {START_STOP_LPAGE, 0, 0, -1, MVP_STD, "Start-stop cycle counter", "sscc", + show_start_stop_page}, /* 0xe, 0x0 */ + {START_STOP_LPAGE, UTILIZATION_SUBPG, 0, 0, MVP_STD, "Utilization", + "util", show_utilization_page}, /* 0xe, 0x1 SBC */ /* sbc4r04 */ + {APP_CLIENT_LPAGE, 0, 0, -1, MVP_STD, "Application client", "ac", + show_app_client_page}, /* 0xf, 0x0 */ + {SELF_TEST_LPAGE, 0, 0, -1, MVP_STD, "Self test results", "str", + show_self_test_page}, /* 0x10, 0x0 */ + {SOLID_STATE_MEDIA_LPAGE, 0, 0, 0, MVP_STD, "Solid state media", "ssm", + show_solid_state_media_page}, /* 0x11, 0x0 SBC */ + {0x11, 0, 0, PDT_TAPE, MVP_STD, "DT Device status", "dtds", + show_dt_device_status_page}, /* 0x11, 0x0 SSC,ADC */ + {0x12, 0, 0, PDT_TAPE, MVP_STD, "Tape alert response", "tar", + show_tapealert_response_page}, /* 0x12, 0x0 SSC,ADC */ + {REQ_RECOVERY_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Requested recovery", "rr", + show_requested_recovery_page}, /* 0x13, 0x0 SSC,ADC */ + {DEVICE_STATS_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Device statistics", "ds", + show_device_stats_page}, /* 0x14, 0x0 SSC,ADC */ + {DEVICE_STATS_LPAGE, 0, 0, PDT_MCHANGER, MVP_STD, /* 0x14, 0x0 SMC */ + "Media changer statistics", "mcs", show_media_stats_page}, + {DEVICE_STATS_LPAGE, ZONED_BLOCK_DEV_STATS_SUBPG, /* 0x14,0x1 zbc2r01 */ + 0, 0, MVP_STD, "Zoned block device statistics", "zbds", + show_zoned_block_dev_stats}, + {BACKGROUND_SCAN_LPAGE, 0, 0, 0, MVP_STD, "Background scan results", + "bsr", show_background_scan_results_page}, /* 0x15, 0x0 SBC */ + {BACKGROUND_SCAN_LPAGE, BACKGROUND_OP_SUBPG, 0, 0, MVP_STD, + "Background operation", "bop", show_background_op_page}, + /* 0x15, 0x2 SBC */ + {BACKGROUND_SCAN_LPAGE, LPS_MISALIGNMENT_SUBPG, 0, 0, MVP_STD, + "LPS misalignment", "lps", show_lps_misalignment_page}, + /* 0x15, 0x3 SBC-4 */ + {0x15, 0, 0, PDT_MCHANGER, MVP_STD, "Element statistics", "els", + show_element_stats_page}, /* 0x15, 0x0 SMC */ + {0x15, 0, 0, PDT_ADC, MVP_STD, "Service buffers information", "sbi", + show_service_buffer_info_page}, /* 0x15, 0x0 ADC */ + {BACKGROUND_SCAN_LPAGE, PENDING_DEFECTS_SUBPG, 0, 0, MVP_STD, + "Pending defects", "pd", show_pending_defects_page}, /* 0x15, 0x1 SBC */ + {SAT_ATA_RESULTS_LPAGE, 0, 0, 0, MVP_STD, "ATA pass-through results", + "aptr", show_ata_pt_results_page}, /* 0x16, 0x0 SAT */ + {0x16, 0, 0, PDT_TAPE, MVP_STD, "Tape diagnostic data", "tdd", + show_tape_diag_data_page}, /* 0x16, 0x0 SSC */ + {0x16, 0, 0, PDT_MCHANGER, MVP_STD, "Media changer diagnostic data", + "mcdd", show_mchanger_diag_data_page}, /* 0x16, 0x0 SMC */ + {0x17, 0, 0, 0, MVP_STD, "Non volatile cache", "nvc", + show_non_volatile_cache_page}, /* 0x17, 0x0 SBC */ + {0x17, 0, 0xf, PDT_TAPE, MVP_STD, "Volume statistics", "vs", + show_volume_stats_pages}, /* 0x17, 0x0...0xf SSC */ + {PROTO_SPECIFIC_LPAGE, 0, 0, -1, MVP_STD, "Protocol specific port", + "psp", show_protocol_specific_port_page}, /* 0x18, 0x0 */ + {STATS_LPAGE, 0, 0, -1, MVP_STD, "General Statistics and Performance", + "gsp", show_stats_perform_pages}, /* 0x19, 0x0 */ + {STATS_LPAGE, 0x1, 0x1f, -1, MVP_STD, "Group Statistics and Performance", + "grsp", show_stats_perform_pages}, /* 0x19, 0x1...0x1f */ + {STATS_LPAGE, CACHE_STATS_SUBPG, 0, -1, MVP_STD, /* 0x19, 0x20 */ + "Cache memory statistics", "cms", show_cache_stats_page}, + {STATS_LPAGE, CMD_DUR_LIMITS_SUBPG, 0, -1, MVP_STD, /* 0x19, 0x21 */ + "Command duration limits statistics", "cdl", + show_cmd_dur_limits_page /* spc6r01 */ }, + {PCT_LPAGE, 0, 0, -1, MVP_STD, "Power condition transitions", "pct", + show_power_condition_transitions_page}, /* 0x1a, 0 */ + {0x1b, 0, 0, PDT_TAPE, MVP_STD, "Data compression", "dc", + show_data_compression_page}, /* 0x1b, 0 SSC */ + {0x2d, 0, 0, PDT_TAPE, MVP_STD, "Current service information", "csi", + NULL}, /* 0x2d, 0 SSC */ + {TAPE_ALERT_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Tape alert", "ta", + show_tape_alert_ssc_page}, /* 0x2e, 0 SSC */ + {IE_LPAGE, 0, 0, -1, (MVP_STD | MVP_HITA), + "Informational exceptions", "ie", show_ie_page}, /* 0x2f, 0 */ +/* vendor specific */ + {0x30, 0, 0, PDT_DISK, MVP_HITA, "Performance counters (Hitachi)", + "pc_hi", show_hgst_perf_page}, /* 0x30, 0 SBC */ + {0x30, 0, 0, PDT_TAPE, OVP_LTO, "Tape usage (lto-5, 6)", "tu_", + show_tape_usage_page}, /* 0x30, 0 SSC */ + {0x31, 0, 0, PDT_TAPE, OVP_LTO, "Tape capacity (lto-5, 6)", + "tc_", show_tape_capacity_page}, /* 0x31, 0 SSC */ + {0x32, 0, 0, PDT_TAPE, MVP_LTO5, "Data compression (lto-5)", + "dc_", show_data_compression_page}, /* 0x32, 0 SSC; redirect to 0x1b */ + {0x33, 0, 0, PDT_TAPE, MVP_LTO5, "Write errors (lto-5)", "we_", + NULL}, /* 0x33, 0 SSC */ + {0x34, 0, 0, PDT_TAPE, MVP_LTO5, "Read forward errors (lto-5)", + "rfe_", NULL}, /* 0x34, 0 SSC */ + {0x35, 0, 0, PDT_TAPE, OVP_LTO, "DT Device Error (lto-5, 6)", + "dtde_", NULL}, /* 0x35, 0 SSC */ + {0x37, 0, 0, PDT_DISK, MVP_SEAG, "Cache (seagate)", "c_se", + show_seagate_cache_page}, /* 0x37, 0 SBC */ + {0x37, 0, 0, PDT_DISK, MVP_HITA, "Miscellaneous (hitachi)", "mi_hi", + show_hgst_misc_page}, /* 0x37, 0 SBC */ + {0x37, 0, 0, PDT_TAPE, MVP_LTO5, "Performance characteristics " + "(lto-5)", "pc_", NULL}, /* 0x37, 0 SSC */ + {0x38, 0, 0, PDT_TAPE, MVP_LTO5, "Blocks/bytes transferred " + "(lto-5)", "bbt_", NULL}, /* 0x38, 0 SSC */ + {0x39, 0, 0, PDT_TAPE, MVP_LTO5, "Host port 0 interface errors " + "(lto-5)", "hp0_", NULL}, /* 0x39, 0 SSC */ + {0x3a, 0, 0, PDT_TAPE, MVP_LTO5, "Drive control verification " + "(lto-5)", "dcv_", NULL}, /* 0x3a, 0 SSC */ + {0x3b, 0, 0, PDT_TAPE, MVP_LTO5, "Host port 1 interface errors " + "(lto-5)", "hp1_", NULL}, /* 0x3b, 0 SSC */ + {0x3c, 0, 0, PDT_TAPE, MVP_LTO5, "Drive usage information " + "(lto-5)", "dui_", NULL}, /* 0x3c, 0 SSC */ + {0x3d, 0, 0, PDT_TAPE, MVP_LTO5, "Subsystem statistics (lto-5)", + "ss_", NULL}, /* 0x3d, 0 SSC */ + {0x3e, 0, 0, PDT_DISK, MVP_SEAG, "Factory (seagate)", "f_se", + show_seagate_factory_page}, /* 0x3e, 0 SBC */ + {0x3e, 0, 0, PDT_DISK, MVP_HITA, "Factory (hitachi)", "f_hi", + NULL}, /* 0x3e, 0 SBC */ + {0x3e, 0, 0, PDT_TAPE, OVP_LTO, "Device Status (lto-5, 6)", + "ds_", NULL}, /* 0x3e, 0 SSC */ + + {-1, -1, -1, -1, 0, NULL, "zzzzz", NULL}, /* end sentinel */ +}; + +/* Supported vendor product codes */ +/* Arrange in alphabetical order by acronym */ +static struct vp_name_t vp_arr[] = { + {VP_SEAG, "sea", "Seagate", "SEAGATE", NULL}, + {VP_HITA, "hit", "Hitachi", "HGST", NULL}, + {VP_HITA, "wdc", "WDC/Hitachi", "WDC", NULL}, + {VP_TOSH, "tos", "Toshiba", "TOSHIBA", NULL}, + {VP_LTO5, "lto5", "LTO-5 (tape drive consortium)", NULL, NULL}, + {VP_LTO6, "lto6", "LTO-6 (tape drive consortium)", NULL, NULL}, + {VP_ALL, "all", "enumerate all vendor specific", NULL, NULL}, + {0, NULL, NULL, NULL, NULL}, +}; + +static char t10_vendor_str[10]; +static char t10_product_str[18]; + +#ifdef SG_LIB_WIN32 +static bool win32_spt_init_state = false; +static bool win32_spt_curr_state = false; +#endif + + +static void +usage(int hval) +{ + if (1 == hval) { + pr2serr( + "Usage: sg_logs [-ALL] [--all] [--brief] [--control=PC] " + "[--enumerate]\n" + " [--exclude] [--filter=FL] [--full] [--help] " + "[--hex]\n" + " [--in=FN] [--json[=JO]] [--list] [--maxlen=LEN] " + "[--name]\n" + " [--no_inq] [--page=PG] [--paramp=PP] [--pcb] " + "[--ppc]\n" + " [--pdt=DT] [--raw] [--readonly] [--reset] " + "[--select]\n" + " [--sp] [--temperature] [--transport] " + "[--undefined]\n" + " [--vendor=VP] [--verbose] [--version] DEVICE\n" + " where the main options are:\n" + " --ALL|-A fetch and decode all log pages and " + "subpages\n" + " --all|-a fetch and decode all log pages, but not " + "subpages; use\n" + " twice to fetch and decode all log pages " + "and subpages\n" + " --brief|-b shorten the output of some log pages\n" + " --enumerate|-e enumerate known pages, ignore DEVICE. " + "Sort order,\n" + " '-e': all by acronym; '-ee': non-vendor " + "by acronym;\n" + " '-eee': all numerically; '-eeee': " + "non-v numerically\n" + " --filter=FL|-f FL FL is parameter code to display (def: " + "all);\n" + " with '-e' then FL>=0 enumerate that " + "pdt + spc\n" + " FL=-1 all (default), FL=-2 spc only\n" + " --full|-F drill down in application client log page\n" + " --help|-h print usage message then exit. Use twice " + "for more help\n" + " --hex|-H output response in hex (default: decode if " + "known)\n" + " --in=FN|-i FN FN is a filename containing a log page " + "in ASCII hex\n" + " or binary if --raw also given. --inhex=FN " + "also accepted\n" + " --json[=JO]|-j[JO] output in JSON instead of human " + "readable\n" + " test. Use --json=? for JSON help\n" + " --list|-l list supported log pages; twice: list " + "supported log\n" + " pages and subpages page; thrice: merge of " + "both pages\n" + " --page=PG|-p PG PG is either log page acronym, PGN or " + "PGN,SPGN\n" + " where (S)PGN is a (sub) page number\n"); + pr2serr( + " --raw|-r either output response in binary to stdout " + "or, if\n" + " '--in=FN' is given, FN is decoded as " + "binary\n" + " --temperature|-t decode temperature (log page 0xd or " + "0x2f)\n" + " --transport|-T decode transport (protocol specific port " + "0x18) page\n" + " --vendor=VP|-M VP vendor/product abbreviation [or " + "number]\n" + " --verbose|-v increase verbosity\n\n" + "Performs a SCSI LOG SENSE (or LOG SELECT) command and decodes " + "the response.\nIf only DEVICE is given then '-p sp' (supported " + "pages) is assumed. Use\n'-e' to see known pages and their " + "acronyms. For more help use '-hh'.\n"); + } else if (hval > 1) { + pr2serr( + " where sg_logs' lesser used options are:\n" + " --control=PC|-c PC page control(PC) (default: 1)\n" + " 0: current threshold, 1: current " + "cumulative\n" + " 2: default threshold, 3: default " + "cumulative\n" + " --exclude|-E exclude vendor specific pages and " + "parameters\n" + " --list|-l list supported log page names (equivalent to " + "'-p sp')\n" + " use twice to list supported log page and " + "subpage names\n" + " --maxlen=LEN|-m LEN max response length (def: 0 " + "-> everything)\n" + " when > 1 will request LEN bytes\n" + " --name|-n decode some pages into multiple name=value " + "lines\n" + " --no_inq|-x no initial INQUIRY output (twice: and no " + "INQUIRY call)\n" + " --old|-O use old interface (use as first option)\n" + " --paramp=PP|-P PP place PP in parameter pointer field in " + "cdb (def: 0)\n" + " --pcb|-q show parameter control bytes in decoded " + "output\n" + " --ppc|-Q set the Parameter Pointer Control (PPC) bit " + "(def: 0)\n" + " --pdt=DT|-D DT DT is peripheral device type to use with " + "'--in=FN'\n" + " or when '--no_inq' is used\n" + " --readonly|-X open DEVICE read-only (def: first " + "read-write then if\n" + " fails try open again read-only)\n" + " --reset|-R reset log parameters (takes PC and SP into " + "account)\n" + " (uses PCR bit in LOG SELECT)\n" + " --select|-S perform LOG SELECT (def: LOG SENSE)\n" + " --sp|-s set the Saving Parameters (SP) bit (def: " + "0)\n" + " --undefined|-u hex format for undefined/unrecognized " + "fields,\n" + " use one or more times; format as per " + "--hex\n" + " --version|-V output version string then exit\n\n" + "If DEVICE and --select are given, a LOG SELECT command will be " + "issued.\nIf DEVICE is not given and '--in=FN' is given then FN " + "will decoded as if\nit were a log page. The contents of FN " + "generated by either a prior\n'sg_logs -HHH ...' invocation or " + "by a text editor.\nLog pages defined in SPC are common " + "to all device types.\n"); + } +} + +static void +usage_old() +{ + printf("Usage: sg_logs [-a] [-A] [-b] [-c=PC] [-D=DT] [-e] [-E] [-f=FL] " + "[-F]\n" + " [-h] [-H] [-i=FN] [-l] [-L] [-m=LEN] [-M=VP] " + "[-n] [-p=PG]\n" + " [-paramp=PP] [-pcb] [-ppc] [-r] [-select] [-sp] " + "[-t] [-T]\n" + " [-u] [-v] [-V] [-x] [-X] [-?] DEVICE\n" + " where:\n" + " -a fetch and decode all log pages\n" + " -A fetch and decode all log pages and subpages\n" + " -b shorten the output of some log pages\n" + " -c=PC page control(PC) (default: 1)\n" + " 0: current threshold, 1: current cumulative\n" + " 2: default threshold, 3: default cumulative\n" + " -e enumerate known log pages\n" + " -D=DT DT is peripheral device type to use with " + "'--in=FN'\n" + " -E exclude vendor specific pages and parameters\n" + " -f=FL filter match parameter code or pdt\n" + " -F drill down in application client log page\n" + " -h output in hex (default: decode if known)\n" + " -H output in hex (same as '-h')\n" + " -i=FN FN is a filename containing a log page " + "in ASCII hex.\n" + " -l list supported log page names (equivalent to " + "'-p=0')\n" + " -L list supported log page and subpages names " + "(equivalent to\n" + " '-p=0,ff')\n" + " -m=LEN max response length (decimal) (def: 0 " + "-> everything)\n" + " -M=VP vendor/product abbreviation [or number]\n" + " -n decode some pages into multiple name=value " + "lines\n" + " -N|--new use new interface\n" + " -p=PG PG is an acronym (def: 'sp')\n" + " -p=PGN page code in hex (def: 0)\n" + " -p=PGN,SPGN page and subpage codes in hex, (defs: 0,0)\n" + " -paramp=PP (in hex) (def: 0)\n" + " -pcb show parameter control bytes in decoded " + "output\n"); + printf(" -ppc set the Parameter Pointer Control (PPC) bit " + "(def: 0)\n" + " -r reset log parameters (takes PC and SP into " + "account)\n" + " (uses PCR bit in LOG SELECT)\n" + " -select perform LOG SELECT (def: LOG SENSE)\n" + " -sp set the Saving Parameters (SP) bit (def: 0)\n" + " -t outputs temperature log page (0xd)\n" + " -T outputs transport (protocol specific port) log " + "page (0x18)\n" + " -u hex format for undefined/unrecognized fields\n" + " -v increase verbosity\n" + " -V output version string\n" + " -x no initial INQUIRY output (twice: no INQUIRY call)\n" + " -X open DEVICE read-only (def: first read-write then " + "if fails\n" + " try open again with read-only)\n" + " -? output this usage message\n\n" + "Performs a SCSI LOG SENSE (or LOG SELECT) command\n"); +} + +/* Return vendor product mask given vendor product number */ +static int +get_vp_mask(int vpn) +{ + if (vpn < 0) + return 0; + else + return (vpn >= (32 - MVP_OFFSET)) ? OVP_ALL : + (1 << (vpn + MVP_OFFSET)); +} + +static int +asort_comp(const void * lp, const void * rp) +{ + const struct log_elem * const * lepp = + (const struct log_elem * const *)lp; + const struct log_elem * const * repp = + (const struct log_elem * const *)rp; + + return strcmp((*lepp)->acron, (*repp)->acron); +} + +static void +enumerate_helper(const struct log_elem * lep, bool first, + const struct opts_t * op) +{ + char b[80]; + char bb[80]; + const char * cp; + bool vendor_lpage = ! (MVP_STD & lep->flags); + + if (first) { + if (1 == op->verbose) { + printf("acronym pg[,spg] name\n"); + printf("===============================================\n"); + } else if (2 == op->verbose) { + printf("acronym pg[,spg] pdt name\n"); + printf("===================================================\n"); + } + } + if ((0 == (op->do_enumerate % 2)) && vendor_lpage) + return; /* if do_enumerate is even then skip vendor pages */ + else if ((! op->filter_given) || (-1 == op->filter)) + ; /* otherwise enumerate all lpages if no --filter= */ + else if (-2 == op->filter) { /* skip non-SPC pages */ + if (lep->pdt >= 0) + return; + } else if (-10 == op->filter) { /* skip non-disk like pages */ + if (sg_lib_pdt_decay(lep->pdt) != 0) + return; + } else if (-11 == op->filter) { /* skip tape like device pages */ + if (sg_lib_pdt_decay(lep->pdt) != 1) + return; + } else if ((op->filter >= 0) && (op->filter <= 0x1f)) { + if ((lep->pdt >= 0) && (lep->pdt != op->filter) && + (lep->pdt != sg_lib_pdt_decay(op->filter))) + return; + } + if (op->vend_prod_num >= 0) { + if (! (lep->flags & get_vp_mask(op->vend_prod_num))) + return; + } + if (op->deduced_vpn >= 0) { + if (! (lep->flags & get_vp_mask(op->deduced_vpn))) + return; + } + if (lep->subpg_high > 0) + snprintf(b, sizeof(b), "0x%x,0x%x->0x%x", lep->pg_code, + lep->subpg_code, lep->subpg_high); + else if (lep->subpg_code > 0) + snprintf(b, sizeof(b), "0x%x,0x%x", lep->pg_code, + lep->subpg_code); + else + snprintf(b, sizeof(b), "0x%x", lep->pg_code); + snprintf(bb, sizeof(bb), "%-16s", b); + cp = (op->verbose && (! lep->show_pagep)) ? " [hex only]" : ""; + if (op->verbose > 1) { + if (lep->pdt < 0) + printf(" %-8s%s- %s%s\n", lep->acron, bb, lep->name, cp); + else + printf(" %-8s%s0x%02x %s%s\n", lep->acron, bb, lep->pdt, + lep->name, cp); + } else + printf(" %-8s%s%s%s\n", lep->acron, bb, lep->name, cp); +} + +static void +enumerate_pages(const struct opts_t * op) +{ + int j; + struct log_elem * lep; + struct log_elem ** lep_arr; + + if (op->do_enumerate < 3) { /* -e, -ee: sort by acronym */ + int k; + struct log_elem ** lepp; + + for (k = 0, lep = log_arr; lep->pg_code >=0; ++lep, ++k) + ; + ++k; + lep_arr = (struct log_elem **)calloc(k, sizeof(struct log_elem *)); + if (NULL == lep_arr) { + pr2serr("%s: out of memory\n", __func__); + return; + } + for (k = 0, lep = log_arr; lep->pg_code >=0; ++lep, ++k) + lep_arr[k] = lep; + lep_arr[k++] = lep; /* put sentinel on end */ + qsort(lep_arr, k, sizeof(struct log_elem *), asort_comp); + printf("Known log pages in acronym order:\n"); + for (lepp = lep_arr, j = 0; (*lepp)->pg_code >=0; ++lepp, ++j) + enumerate_helper(*lepp, (0 == j), op); + free(lep_arr); + } else { /* -eee, -eeee numeric sort (as per table) */ + printf("Known log pages in numerical order:\n"); + for (lep = log_arr, j = 0; lep->pg_code >=0; ++lep, ++j) + enumerate_helper(lep, (0 == j), op); + } +} + +static const struct log_elem * +acron_search(const char * acron) +{ + const struct log_elem * lep; + + for (lep = log_arr; lep->pg_code >=0; ++lep) { + if (0 == strcmp(acron, lep->acron)) + return lep; + } + return NULL; +} + +static int +find_vpn_by_acron(const char * vp_ap) +{ + const struct vp_name_t * vpp; + + for (vpp = vp_arr; vpp->acron; ++vpp) { + size_t k; + size_t len = strlen(vpp->acron); + + for (k = 0; k < len; ++k) { + if (tolower((uint8_t)vp_ap[k]) != (uint8_t)vpp->acron[k]) + break; + } + if (k < len) + continue; + return vpp->vend_prod_num; + } + return VP_NONE; +} + +/* Find vendor product number using T10 VENDOR and PRODUCT ID fields in a + INQUIRY response. */ +static int +find_vpn_by_inquiry(void) +{ + size_t len; + size_t t10_v_len = strlen(t10_vendor_str); + size_t t10_p_len = strlen(t10_product_str); + const struct vp_name_t * vpp; + + if ((0 == t10_v_len) && (0 == t10_p_len)) + return VP_NONE; + for (vpp = vp_arr; vpp->acron; ++vpp) { + bool matched = false; + + if (vpp->t10_vendorp && (t10_v_len > 0)) { + len = strlen(vpp->t10_vendorp); + len = (len > t10_v_len) ? t10_v_len : len; + if (strncmp(vpp->t10_vendorp, t10_vendor_str, len)) + continue; + matched = true; + } + if (vpp->t10_productp && (t10_p_len > 0)) { + len = strlen(vpp->t10_productp); + len = (len > t10_p_len) ? t10_p_len : len; + if (strncmp(vpp->t10_productp, t10_product_str, len)) + continue; + matched = true; + } + if (matched) + return vpp->vend_prod_num; + } + return VP_NONE; +} + +static void +enumerate_vp(void) +{ + const struct vp_name_t * vpp; + bool seen = false; + + for (vpp = vp_arr; vpp->acron; ++vpp) { + if (vpp->name) { + if (! seen) { + printf("\nVendor/product identifiers:\n"); + seen = true; + } + printf(" %-10s %d %s\n", vpp->acron, + vpp->vend_prod_num, vpp->name); + } + } +} + +static const struct log_elem * +pg_subpg_pdt_search(int pg_code, int subpg_code, int pdt, int vpn) +{ + const struct log_elem * lep; + int d_pdt; + int vp_mask = get_vp_mask(vpn); + + d_pdt = sg_lib_pdt_decay(pdt); + for (lep = log_arr; lep->pg_code >=0; ++lep) { + if (pg_code == lep->pg_code) { + if (subpg_code == lep->subpg_code) { + if ((MVP_STD & lep->flags) || (0 == vp_mask) || + (vp_mask & lep->flags)) + ; + else + continue; + if ((lep->pdt < 0) || (pdt == lep->pdt) || (pdt < 0)) + return lep; + else if (d_pdt == lep->pdt) + return lep; + else if (pdt == sg_lib_pdt_decay(lep->pdt)) + return lep; + } else if ((lep->subpg_high > 0) && + (subpg_code > lep->subpg_code) && + (subpg_code <= lep->subpg_high)) + return lep; + } + } + return NULL; +} + +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) +{ + + if ((NULL == jsp) || (NULL == jop)) + return; + if (sgj_is_snake_name(conv2sname)) + sgj_js_nv_ihexstr_nex(jsp, jop, conv2sname, val_i, hex_as_well, + str_name, val_s, nex_s); + else { + char b[128]; + + sgj_convert_to_snake_name(conv2sname, b, sizeof(b)); + sgj_js_nv_ihexstr_nex(jsp, jop, b, val_i, hex_as_well, str_name, + val_s, nex_s); + } +} + +static void +usage_for(int hval, const struct opts_t * op) +{ + if (op->opt_new) + usage(hval); + else + usage_old(); +} + +/* Processes command line options according to new option format. Returns + * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */ +static int +new_parse_cmd_line(struct opts_t * op, int argc, char * argv[]) +{ + while (1) { + int c, n; + int option_index = 0; + + c = getopt_long(argc, argv, "aAbc:D:eEf:FhHi:j::lLm:M:nNOp:P:qQrRsStT" + "uvVxX", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'a': + ++op->do_all; + break; + case 'A': + op->do_all += 2; + break; + case 'b': + ++op->do_brief; + break; + case 'c': + n = sg_get_num(optarg); + if ((n < 0) || (n > 3)) { + pr2serr("bad argument to '--control='\n"); + usage(2); + return SG_LIB_SYNTAX_ERROR; + } + op->page_control = n; + break; + case 'D': + if (0 == memcmp("-1", optarg, 3)) + op->dev_pdt = -1; + else { + n = sg_get_num(optarg); + if ((n < 0) || (n > 31)) { + pr2serr("bad argument to '--pdt='\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->dev_pdt = n; + } + break; + case 'e': + ++op->do_enumerate; + break; + case 'E': + op->exclude_vendor = true; + break; + case 'f': + if ('-' == optarg[0]) { + n = sg_get_num(optarg + 1); + if ((n < 0) || (n > 0x30)) { + pr2serr("bad negated argument to '--filter='\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->filter = -n; + } else { + n = sg_get_num(optarg); + if ((n < 0) || (n > 0xffff)) { + pr2serr("bad argument to '--filter='\n"); + usage(1); + return SG_LIB_SYNTAX_ERROR; + } + op->filter = n; + } + op->filter_given = true; + break; + case 'F': + op->do_full = true; + break; + case 'h': + case '?': + ++op->do_help; + break; + case 'H': + ++op->do_hex; + break; + case 'i': + op->in_fn = optarg; + break; + case 'j': + if (! sgj_init_state(&op->json_st, optarg)) { + int bad_char = op->json_st.first_bad_char; + char e[1500]; + + if (bad_char) { + pr2serr("bad argument to --json= option, unrecognized " + "character '%c'\n\n", bad_char); + } + sg_json_usage(0, e, sizeof(e)); + pr2serr("%s", e); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 'l': + ++op->do_list; + break; + case 'L': + op->do_list += 2; + break; + case 'm': + n = sg_get_num(optarg); + if ((n < 0) || (1 == n)) { + pr2serr("bad argument to '--maxlen=', from 2 and up " + "expected\n"); + usage(2); + return SG_LIB_SYNTAX_ERROR; + } else if (n < 4) { + pr2serr("Warning: setting '--maxlen' to 4\n"); + n = 4; + } + op->maxlen = n; + op->maxlen_given = true; + break; + case 'M': + if (op->vend_prod) { + pr2serr("only one '--vendor=' option permitted\n"); + usage(2); + return SG_LIB_SYNTAX_ERROR; + } else + op->vend_prod = optarg; + break; + case 'n': + op->do_name = true; + break; + case 'N': + break; /* ignore */ + case 'O': + op->opt_new = false; + return 0; + case 'p': + op->pg_arg = optarg; + break; + case 'P': + n = sg_get_num(optarg); + if (n < 0) { + pr2serr("bad argument to '--paramp='\n"); + usage(2); + return SG_LIB_SYNTAX_ERROR; + } + op->paramp = n; + break; + case 'q': + op->do_pcb = true; + break; + case 'Q': /* N.B. PPC bit obsoleted in SPC-4 rev 18 */ + op->do_ppc = true; + break; + case 'r': + op->do_raw = true; + break; + case 'R': + op->do_pcreset = true; + op->do_select = true; + break; + case 's': + op->do_sp = true; + break; + case 'S': + op->do_select = true; + break; + case 't': + op->do_temperature = true; + break; + case 'T': + op->do_transport = true; + break; + case 'u': + ++op->undefined_hex; + break; + case 'v': + op->verbose_given = true; + ++op->verbose; + break; + case 'V': + op->version_given = true; + break; + case 'x': + ++op->no_inq; + break; + case 'X': + op->o_readonly = true; + break; + default: + pr2serr("unrecognised option code %c [0x%x]\n", c, c); + if (op->do_help) + break; + usage(1); + return SG_LIB_SYNTAX_ERROR; + } + } + if (optind < argc) { + if (NULL == op->device_name) { + op->device_name = argv[optind]; + ++optind; + } + if (optind < argc) { + for (; optind < argc; ++optind) + pr2serr("Unexpected extra argument: %s\n", argv[optind]); + usage(1); + return SG_LIB_SYNTAX_ERROR; + } + } + return 0; +} + +/* Processes command line options according to old option format. Returns + * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */ +static int +old_parse_cmd_line(struct opts_t * op, int argc, char * argv[]) +{ + bool jmp_out; + int k, num, n; + unsigned int u, uu; + const char * cp; + + for (k = 1; k < argc; ++k) { + int plen; + + cp = argv[k]; + plen = strlen(cp); + if (plen <= 0) + continue; + if ('-' == *cp) { + for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) { + switch (*cp) { + case 'a': + ++op->do_all; + break; + case 'A': + op->do_all += 2; + break; + case 'b': + ++op->do_brief; + break; + case 'e': + ++op->do_enumerate; + break; + case 'E': + op->exclude_vendor = true; + break; + case 'F': + op->do_full = true; + break; + case 'h': + case 'H': + ++op->do_hex; + break; + case 'l': + ++op->do_list; + break; + case 'L': + op->do_list += 2; + break; + case 'n': + op->do_name = true; + break; + case 'N': + op->opt_new = true; + return 0; + case 'O': + break; + case 'r': + op->do_pcreset = true; + op->do_select = true; + break; + case 't': + op->do_temperature = true; + break; + case 'T': + op->do_transport = true; + break; + case 'u': + ++op->undefined_hex; + break; + case 'v': + op->verbose_given = true; + ++op->verbose; + break; + case 'V': + op->version_given = true; + break; + case 'x': + ++op->no_inq; + break; + case 'X': + op->o_readonly = true; + break; + case '?': + ++op->do_help; + break; + case '-': + ++cp; + jmp_out = true; + break; + default: + jmp_out = true; + break; + } + if (jmp_out) + break; + } + if (plen <= 0) + continue; + if (0 == strncmp("c=", cp, 2)) { + num = sscanf(cp + 2, "%6x", &u); + if ((1 != num) || (u > 3)) { + pr2serr("Bad page control after '-c=' option [0..3]\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + op->page_control = u; + } else if (0 == strncmp("D=", cp, 2)) { + n = sg_get_num(cp + 2); + if ((n < 0) || (n > 31)) { + pr2serr("Bad argument after '-D=' option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + op->dev_pdt = n; + } else if (0 == strncmp("f=", cp, 2)) { + n = sg_get_num(cp + 2); + if ((n < 0) || (n > 0xffff)) { + pr2serr("Bad argument after '-f=' option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + op->filter = n; + op->filter_given = true; + } else if (0 == strncmp("i=", cp, 2)) + op->in_fn = cp + 2; + else if (0 == strncmp("m=", cp, 2)) { + num = sscanf(cp + 2, "%8d", &n); + if ((1 != num) || (n < 0)) { + pr2serr("Bad maximum response length after '-m=' " + "option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + op->maxlen_given = true; + op->maxlen = n; + } else if (0 == strncmp("M=", cp, 2)) { + if (op->vend_prod) { + pr2serr("only one '-M=' option permitted\n"); + usage(2); + return SG_LIB_SYNTAX_ERROR; + } else + op->vend_prod = cp + 2; + } else if (0 == strncmp("p=", cp, 2)) { + const char * ccp = cp + 2; + const struct log_elem * lep; + + if (isalpha((uint8_t)ccp[0])) { + char * xp; + char b[80]; + + if (strlen(ccp) >= (sizeof(b) - 1)) { + pr2serr("argument to '-p=' is too long\n"); + return SG_LIB_SYNTAX_ERROR; + } + strcpy(b, ccp); + xp = (char *)strchr(b, ','); + if (xp) + *xp = '\0'; + lep = acron_search(b); + if (NULL == lep) { + pr2serr("bad argument to '--page=' no acronyn match " + "to '%s'\n", b); + pr2serr(" Try using '-e' or'-ee' to see available " + "acronyns\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->lep = lep; + op->pg_code = lep->pg_code; + if (xp) { + n = sg_get_num_nomult(xp + 1); + if ((n < 0) || (n > 255)) { + pr2serr("Bad second value in argument to " + "'--page='\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->subpg_code = n; + } else + op->subpg_code = lep->subpg_code; + } else { + /* numeric arg: either 'pg_num' or 'pg_num,subpg_num' */ + if (NULL == strchr(cp + 2, ',')) { + num = sscanf(cp + 2, "%6x", &u); + if ((1 != num) || (u > 63)) { + pr2serr("Bad page code value after '-p=' " + "option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + op->pg_code = u; + } else if (2 == sscanf(cp + 2, "%4x,%4x", &u, &uu)) { + if (uu > 255) { + pr2serr("Bad sub page code value after '-p=' " + "option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + op->pg_code = u; + op->subpg_code = uu; + } else { + pr2serr("Bad page code, subpage code sequence after " + "'-p=' option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + } + } else if (0 == strncmp("paramp=", cp, 7)) { + num = sscanf(cp + 7, "%8x", &u); + if ((1 != num) || (u > 0xffff)) { + pr2serr("Bad parameter pointer after '-paramp=' " + "option\n"); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + op->paramp = u; + } else if (0 == strncmp("pcb", cp, 3)) + op->do_pcb = true; + else if (0 == strncmp("ppc", cp, 3)) + op->do_ppc = true; + else if (0 == strncmp("select", cp, 6)) + op->do_select = true; + else if (0 == strncmp("sp", cp, 2)) + op->do_sp = true; + else if (0 == strncmp("old", cp, 3)) + ; + else if (jmp_out) { + pr2serr("Unrecognized option: %s\n", cp); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + } else if (0 == op->device_name) + op->device_name = cp; + else { + pr2serr("too many arguments, got: %s, not expecting: %s\n", + op->device_name, cp); + usage_old(); + return SG_LIB_SYNTAX_ERROR; + } + } + return 0; +} + +/* Process command line options. First check using new option format unless + * the SG3_UTILS_OLD_OPTS environment variable is defined which causes the + * old option format to be checked first. Both new and old format can be + * countermanded by a '-O' and '-N' options respectively. As soon as either + * of these options is detected (when processing the other format), processing + * stops and is restarted using the other format. Clear? */ +static int +parse_cmd_line(struct opts_t * op, int argc, char * argv[]) +{ + int res; + char * cp; + + cp = getenv("SG3_UTILS_OLD_OPTS"); + if (cp) { + op->opt_new = false; + res = old_parse_cmd_line(op, argc, argv); + if ((0 == res) && op->opt_new) + res = new_parse_cmd_line(op, argc, argv); + } else { + op->opt_new = true; + res = new_parse_cmd_line(op, argc, argv); + if ((0 == res) && (0 == op->opt_new)) + res = old_parse_cmd_line(op, argc, argv); + } + return res; +} + +static void +dStrRaw(const uint8_t * str, int len) +{ + int k; + + for (k = 0; k < len; ++k) + printf("%c", str[k]); +} + +/* Returns 'xp' with "unknown" if all bits set; otherwise decoded (big endian) + * number in 'xp'. Number rendered in decimal if pr_in_hex=false otherwise in + * hex with leading '0x' prepended. */ +static char * +num_or_unknown(const uint8_t * xp, int num_bytes /* max is 8 */, + bool pr_in_hex, char * b, int blen) +{ + if (sg_all_ffs(xp, num_bytes)) + snprintf(b, blen, "%s", unknown_s); + else { + uint64_t num = sg_get_unaligned_be(num_bytes, xp); + + if (pr_in_hex) + snprintf(b, blen, "0x%" PRIx64, num); + else + snprintf(b, blen, "%" PRIu64, num); + } + return b; +} + +/* Call LOG SENSE twice: the first time ask for 4 byte response to determine + actual length of response; then a second time requesting the + min(actual_len, mx_resp_len) bytes. If the calculated length for the + second fetch is odd then it is incremented (perhaps should be made modulo + 4 in the future for SAS). Returns 0 if ok, SG_LIB_CAT_INVALID_OP for + log_sense not supported, SG_LIB_CAT_ILLEGAL_REQ for bad field in log sense + command, SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION, + SG_LIB_CAT_ABORTED_COMMAND and -1 for other errors. */ +static int +do_logs(int sg_fd, uint8_t * resp, int mx_resp_len, + const struct opts_t * op) +{ + int calc_len, request_len, res, resid, vb; + +#ifdef SG_LIB_WIN32 +#ifdef SG_LIB_WIN32_DIRECT + if (! win32_spt_init_state) { + if (win32_spt_curr_state) { + if (mx_resp_len < 16384) { + scsi_pt_win32_direct(0); + win32_spt_curr_state = false; + } + } else { + if (mx_resp_len >= 16384) { + scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT direct */); + win32_spt_curr_state = true; + } + } + } +#endif +#endif + memset(resp, 0, mx_resp_len); + vb = op->verbose; + if (op->maxlen > 1) + request_len = mx_resp_len; + else { + request_len = LOG_SENSE_PROBE_ALLOC_LEN; + if ((res = sg_ll_log_sense_v2(sg_fd, op->do_ppc, op->do_sp, + op->page_control, op->pg_code, + op->subpg_code, op->paramp, + resp, request_len, LOG_SENSE_DEF_TIMEOUT, + &resid, true /* noisy */, vb))) + return res; + if (resid > 0) { + res = SG_LIB_WILD_RESID; + goto resid_err; + } + calc_len = sg_get_unaligned_be16(resp + 2) + 4; + if ((! op->do_raw) && (vb > 1)) { + pr2serr(" Log sense (find length) response:\n"); + hex2stderr(resp, LOG_SENSE_PROBE_ALLOC_LEN, 1); + pr2serr(" hence calculated response length=%d\n", calc_len); + } + if (op->pg_code != (0x3f & resp[0])) { + if (vb) + pr2serr("Page code does not appear in first byte of " + "response so it's suspect\n"); + if (calc_len > 0x40) { + calc_len = 0x40; + if (vb) + pr2serr("Trim response length to 64 bytes due to " + "suspect response format\n"); + } + } + /* Some HBAs don't like odd transfer lengths */ + if (calc_len % 2) + calc_len += 1; + if (calc_len > mx_resp_len) + calc_len = mx_resp_len; + request_len = calc_len; + } + if ((res = sg_ll_log_sense_v2(sg_fd, op->do_ppc, op->do_sp, + op->page_control, op->pg_code, + op->subpg_code, op->paramp, + resp, request_len, + LOG_SENSE_DEF_TIMEOUT, &resid, + true /* noisy */, vb))) + return res; + if (resid > 0) { + request_len -= resid; + if (request_len < 4) { + request_len += resid; + res = SG_LIB_WILD_RESID; + goto resid_err; + } + } + if ((! op->do_raw) && (vb > 1)) { + pr2serr(" Log sense response:\n"); + hex2stderr(resp, request_len, 1); + } + return 0; +resid_err: + pr2serr("%s: request_len=%d, resid=%d, problems\n", __func__, request_len, + resid); + request_len -= resid; + if ((request_len > 0) && (! op->do_raw) && (vb > 1)) { + pr2serr(" Log sense (resid_err) response:\n"); + hex2stderr(resp, request_len, 1); + } + return res; +} + +sgj_opaque_p +sg_log_js_hdr(sgj_state * jsp, sgj_opaque_p jop, const char * name, + const uint8_t * log_hdrp) +{ + bool ds = !! (log_hdrp[0] & 0x80); + bool spf = !! (log_hdrp[0] & 0x40); + int pg = log_hdrp[0] & 0x3f; + int subpg = log_hdrp[1]; + size_t nlen = strlen(name); + sgj_opaque_p jo2p; + char b[80]; + + if ((nlen < 4) || (0 != strcmp("age", name + nlen - 3))) { + memcpy(b, name, nlen); + memcpy(b + nlen, " log page", 10); + jo2p = sgj_snake_named_subobject_r(jsp, jop, b); + } else + jo2p = sgj_snake_named_subobject_r(jsp, jop, name); + + sgj_js_nv_ihex_nex(jsp, jo2p, "ds", (int)ds, false, "Did not Save"); + sgj_js_nv_ihex_nex(jsp, jo2p, "spf", (int)spf, NULL, "SubPage Format"); + sgj_js_nv_ihex(jsp, jo2p, "page_code", pg); + sgj_js_nv_ihex(jsp, jo2p, "subpage_code", subpg); + return jo2p; +} + + + +/* DS made obsolete in spc4r03; TMC and ETC made obsolete in spc5r03. */ +static char * +get_pcb_str(int pcb, char * outp, int maxoutlen) +{ + char buff[PCB_STR_LEN]; + int n; + + n = sprintf(buff, "du=%d [ds=%d] tsd=%d [etc=%d] ", ((pcb & 0x80) ? 1 : 0), + ((pcb & 0x40) ? 1 : 0), ((pcb & 0x20) ? 1 : 0), + ((pcb & 0x10) ? 1 : 0)); + if (pcb & 0x10) + n += sprintf(buff + n, "[tmc=%d] ", ((pcb & 0xc) >> 2)); +#if 1 + n += sprintf(buff + n, "format+linking=%d [0x%.2x]", pcb & 3, + pcb); +#else + if (pcb & 0x1) + n += sprintf(buff + n, "lbin=%d ", ((pcb & 0x2) >> 1)); + n += sprintf(buff + n, "lp=%d [0x%.2x]", pcb & 0x1, pcb); +#endif + if (outp && (n < maxoutlen)) { + memcpy(outp, buff, n); + outp[n] = '\0'; + } else if (outp && (maxoutlen > 0)) + outp[0] = '\0'; + return outp; +} + +static void +js_pcb(sgj_state * jsp, sgj_opaque_p jop, int pcb) +{ + sgj_opaque_p jo2p = sgj_snake_named_subobject_r(jsp, jop, + "parameter_control_byte"); + + sgj_js_nv_ihex_nex(jsp, jo2p, "du", (pcb & 0x80) ? 1 : 0, false, + "Disable Update"); + sgj_js_nv_ihex_nex(jsp, jo2p, "ds", (pcb & 0x40) ? 1 : 0, false, + "Disable Save [obsolete]"); + sgj_js_nv_ihex_nex(jsp, jo2p, "tsd", (pcb & 0x20) ? 1 : 0, false, + "Target Save Disable"); + sgj_js_nv_ihex_nex(jsp, jo2p, "etc", (pcb & 0x10) ? 1 : 0, false, + "Enable Threshold Comparison [obsolete]"); + sgj_js_nv_ihex_nex(jsp, jo2p, "tmc", (pcb & 0xc) >> 2, false, + "Threshold Met Criteria [obsolete]"); + sgj_js_nv_ihex_nex(jsp, jo2p, "format_and_linking", pcb & 0x3, false, + NULL); +} + +/* SUPP_PAGES_LPAGE [0x0,0x0] <sp> */ +static bool +show_supported_pgs_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, k; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p, jo3p; + sgj_opaque_p jap = NULL; + char b[64]; + static const char * slpgs = "Supported log pages"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x0]:\n", slpgs); /* introduced in: SPC-2 */ + num = len - 4; + bp = &resp[0] + 4; + if ((op->do_hex > 0) || op->do_raw) { + if (op->do_raw) + dStrRaw(resp, len); + else + hex2stdout(resp, len, op->dstrhex_no_ascii); + return true; + } + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, slpgs, resp); + jap = sgj_named_subarray_r(jsp, jo2p, "supported_pages_list"); + } + + for (k = 0; k < num; ++k) { + int pg_code = bp[k] & 0x3f; + const struct log_elem * lep; + + snprintf(b, sizeof(b) - 1, " 0x%02x ", pg_code); + lep = pg_subpg_pdt_search(pg_code, 0, op->dev_pdt, -1); + if (lep) { + if (op->do_brief > 1) + sgj_pr_hr(jsp, " %s\n", lep->name); + else if (op->do_brief) + sgj_pr_hr(jsp, "%s%s\n", b, lep->name); + else + sgj_pr_hr(jsp, "%s%s [%s]\n", b, lep->name, lep->acron); + } else + sgj_pr_hr(jsp, "%s\n", b); + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + sgj_js_nv_ihex(jsp, jo3p, "page_code", pg_code); + sgj_js_nv_s(jsp, jo3p, "name", lep ? lep->name : unknown_s); + sgj_js_nv_s(jsp, jo3p, "acronym", lep ? lep->acron : unknown_s); + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + } + } + return true; +} + +/* SUPP_PAGES_LPAGE,SUPP_SPGS_SUBPG [0x0,0xff] <ssp> or all subpages of a + * given page code: [<pg_code>,0xff] where <pg_code> > 0 */ +static bool +show_supported_pgs_sub_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, k; + const uint8_t * bp; + const struct log_elem * lep = NULL; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p, jo3p; + sgj_opaque_p jap = NULL; + char b[64]; + static const char * slpass = "Supported log pages and subpages"; + static const char * sss = "Supported subpages"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (op->pg_code > 0) + sgj_pr_hr(jsp, "%s [0x%x, 0xff]:\n", sss, op->pg_code); + else + sgj_pr_hr(jsp, "%s [0x0, 0xff]:\n", sss); + } + num = len - 4; + bp = &resp[0] + 4; + if ((op->do_hex > 0) || op->do_raw) { + if (op->do_raw) + dStrRaw(resp, len); + else + hex2stdout(resp, len, op->dstrhex_no_ascii); + return true; + } + if (jsp->pr_as_json) { + if (op->pg_code > 0) { + jo2p = sg_log_js_hdr(jsp, jop, sss, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "supported_subpage_descriptors"); + } else { + jo2p = sg_log_js_hdr(jsp, jop, slpass, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "supported_page_subpage_descriptors"); + } + } + + for (k = 0; k < num; k += 2) { + bool pr_name = true; + int pg_code = bp[k]; + int subpg_code = bp[k + 1]; + + /* formerly ignored [pg, 0xff] when pg > 0, don't know why */ + if (NOT_SPG_SUBPG == subpg_code) + snprintf(b, sizeof(b) - 1, " 0x%02x ", pg_code); + else + snprintf(b, sizeof(b) - 1, " 0x%02x,0x%02x ", pg_code, + subpg_code); + if ((pg_code > 0) && (subpg_code == 0xff)) { + sgj_pr_hr(jsp, "%s\n", b); + pr_name = false; + } else { + lep = pg_subpg_pdt_search(pg_code, subpg_code, op->dev_pdt, -1); + if (lep) { + if (op->do_brief > 1) + sgj_pr_hr(jsp, " %s\n", lep->name); + else if (op->do_brief) + sgj_pr_hr(jsp, "%s%s\n", b, lep->name); + else + sgj_pr_hr(jsp, "%s%s [%s]\n", b, lep->name, lep->acron); + } else + sgj_pr_hr(jsp, "%s\n", b); + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + sgj_js_nv_ihex(jsp, jo3p, "page_code", pg_code); + sgj_js_nv_ihex(jsp, jo3p, "subpage_code", subpg_code); + if (pr_name) { + sgj_js_nv_s(jsp, jo3p, "name", lep ? lep->name : unknown_s); + sgj_js_nv_s(jsp, jo3p, "acronym", lep ? lep->acron : + unknown_s); + } + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + } + } + return true; +} + +/* BUFF_OVER_UNDER_LPAGE [0x1] <bou> introduced: SPC-2 */ +static bool +show_buffer_over_under_run_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + uint64_t count; + const uint8_t * bp; + const char * cp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + static const char * bourlp = "Buffer over-run/under-run log page"; + static const char * orurc = "over_run_under_run_counter"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x1]\n", bourlp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, bourlp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "buffer_over_run_under_run_log_parameters"); + } + while (num > 3) { + cp = NULL; + pl = bp[3] + 4; + count = (pl > 4) ? sg_get_unaligned_be(pl - 4, bp + 4) : 0; + pc = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + switch (pc) { + case 0x0: + cp = "under-run"; + break; + case 0x1: + cp = "over-run"; + break; + case 0x2: + cp = "service delivery subsystem busy, under-run"; + break; + case 0x3: + cp = "service delivery subsystem busy, over-run"; + break; + case 0x4: + cp = "transfer too slow, under-run"; + break; + case 0x5: + cp = "transfer too slow, over-run"; + break; + case 0x20: + cp = "command, under-run"; + break; + case 0x21: + cp = "command, over-run"; + break; + case 0x22: + cp = "command, service delivery subsystem busy, under-run"; + break; + case 0x23: + cp = "command, service delivery subsystem busy, over-run"; + break; + case 0x24: + cp = "command, transfer too slow, under-run"; + break; + case 0x25: + cp = "command, transfer too slow, over-run"; + break; + case 0x40: + cp = "I_T nexus, under-run"; + break; + case 0x41: + cp = "I_T nexus, over-run"; + break; + case 0x42: + cp = "I_T nexus, service delivery subsystem busy, under-run"; + break; + case 0x43: + cp = "I_T nexus, service delivery subsystem busy, over-run"; + break; + case 0x44: + cp = "I_T nexus, transfer too slow, under-run"; + break; + case 0x45: + cp = "I_T nexus, transfer too slow, over-run"; + break; + case 0x80: + cp = "time, under-run"; + break; + case 0x81: + cp = "time, over-run"; + break; + case 0x82: + cp = "time, service delivery subsystem busy, under-run"; + break; + case 0x83: + cp = "time, service delivery subsystem busy, over-run"; + break; + case 0x84: + cp = "time, transfer too slow, under-run"; + break; + case 0x85: + cp = "time, transfer too slow, over-run"; + break; + default: + pr2serr(" undefined %s [0x%x], count = %" PRIu64 "\n", + param_c, pc, count); + break; + } + sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc); + sgj_pr_hr(jsp, " %s=0x%x\n", param_c, pc); + if (cp) { + sgj_pr_hr(jsp, " %s = %" PRIu64 "\n", cp, count); + js_snakenv_ihexstr_nex(jsp, jo3p, param_c, pc, true, + NULL, cp, NULL); + sgj_js_nv_ihex(jsp, jo3p, orurc, count); + } else + sgj_pr_hr(jsp, " counter = %" PRIu64 "\n", count); + + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* WRITE_ERR_LPAGE; READ_ERR_LPAGE; READ_REV_ERR_LPAGE; VERIFY_ERR_LPAGE */ +/* [0x2, 0x3, 0x4, 0x5] <we, re, rre, ve> introduced: SPC-3 */ +static bool +show_error_counter_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool skip_out = false; + bool evsm_output = false; + int n, num, pl, pc, pg_code; + uint64_t val; + const uint8_t * bp; + const char * pg_cp = NULL; + const char * par_cp = NULL; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[128] SG_C_CPP_ZERO_INIT; + char d[128]; + char e[64]; + static const char * wec = "Write error counter"; + static const char * rec = "Read error counter"; + static const char * rrec = "Read reverse error counter"; + static const char * vec = "Verify error counter"; + + pg_code = resp[0] & 0x3f; + switch(pg_code) { + case WRITE_ERR_LPAGE: + pg_cp = wec; + break; + case READ_ERR_LPAGE: + pg_cp = rec; + break; + case READ_REV_ERR_LPAGE: + pg_cp = rrec; + break; + case VERIFY_ERR_LPAGE: + pg_cp = vec; + break; + default: + pr2serr("expecting error counter page, got page = 0x%x\n", + pg_code); + return false; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s log page [0x%x]\n", pg_cp, pg_code); + if (jsp->pr_as_json) { + n = strlen(pg_cp); + memcpy(b, pg_cp, n); + memcpy(b + n, " log", 4); + n = strlen(b); + memcpy(b + n, " page", 5); + jo2p = sg_log_js_hdr(jsp, jop, b, resp); + memcpy(b + n, " parameters", 11); + sgj_convert_to_snake_name(b, d, sizeof(d) - 1); + jap = sgj_named_subarray_r(jsp, jo2p, d); + } + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + par_cp = NULL; + switch (pc) { + case 0: + par_cp = "Errors corrected without substantial delay"; + break; + case 1: + par_cp = "Errors corrected with possible delays"; + break; + case 2: + par_cp = "Total rewrites or rereads"; + break; + case 3: + par_cp = "Total errors corrected"; + break; + case 4: + par_cp = "Total times correction algorithm processed"; + break; + case 5: + par_cp = "Total bytes processed"; + break; + case 6: + par_cp = "Total uncorrected errors"; + break; + default: + if (op->exclude_vendor) { + skip_out = true; + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + pr2serr(" %s parameter(s) being ignored\n", vend_spec); + } + } else { + if (0x8009 == pc) + par_cp = "Track following errors [Hitachi]"; + else if (0x8015 == pc) + par_cp = "Positioning errors [Hitachi]"; + else { + snprintf(e, sizeof(e), "Reserved or %s [0x%x]", vend_spec, + pc); + par_cp = e; + } + } + break; + } + + if (skip_out) + skip_out = false; + else if (par_cp) { + val = sg_get_unaligned_be(pl - 4, bp + 4); + if (val > ((uint64_t)1 << 40)) + snprintf(d, sizeof(d), "%" PRIu64 " [%" PRIu64 " TB]", + val, (val / (1000UL * 1000 * 1000 * 1000))); + else if (val > ((uint64_t)1 << 30)) + snprintf(d, sizeof(d), "%" PRIu64 " [%" PRIu64 " GB]", + val, (val / (1000UL * 1000 * 1000))); + else + snprintf(d, sizeof(d), "%" PRIu64, val); + sgj_pr_hr(jsp, " %s = %s\n", par_cp, d); + if (jsp->pr_as_json) { + js_snakenv_ihexstr_nex(jsp, jo3p, param_c, pc, true, + NULL, par_cp, NULL); + sgj_convert_to_snake_name(pg_cp, e, sizeof(e) - 1); + n = strlen(e); + memcpy(e + n, "_counter", 9); /* take trailing null */ + sgj_js_nv_ihexstr(jsp, jo3p, e, val, as_s_s, d); + } + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* NON_MEDIUM_LPAGE [0x6] <nm> introduced: SPC-2 */ +static bool +show_non_medium_error_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool skip_out = false; + bool evsm_output = false; + int num, pl, pc; + uint64_t count; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[128] SG_C_CPP_ZERO_INIT; + static const char * nmelp = "Non-medium error log page"; + static const char * nmec = "Non-medium error count"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x6]\n", nmelp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, nmelp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "non_medium_error_log_parameters"); + } + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + switch (pc) { + case 0: + snprintf(b, sizeof(b), "%s", nmec); + break; + default: + if (pc <= 0x7fff) + snprintf(b, sizeof(b), " Reserved [0x%x]", pc); + else { + if (op->exclude_vendor) { + skip_out = true; + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + pr2serr(" %s parameter(s) being ignored\n", + vend_spec); + } + } else + snprintf(b, sizeof(b), "%s [0x%x]", vend_spec, pc); + } + break; + } + if (skip_out) + skip_out = false; + else { + count = sg_get_unaligned_be(pl - 4, bp + 4); + sgj_pr_hr(jsp, " %s = %" PRIu64 "\n", b, count); + js_snakenv_ihexstr_nex(jsp, jo3p, param_c, pc, true, + NULL, b, NULL); + js_snakenv_ihexstr_nex(jsp, jo3p, nmec, count, true, NULL, NULL, + NULL); + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* PCT_LPAGE [0x1a] <pct> introduced: SPC-4 */ +static bool +show_power_condition_transitions_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool partial; + int num, pl, pc; + uint64_t count; + const uint8_t * bp; + const char * cp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[128]; + char bb[64]; + static const char * pctlp = "Power condition transitions log page"; + static const char * att = "Accumulated transitions to"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x1a]\n", pctlp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, pctlp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "power_condition_transition_log_parameters"); + } + + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + cp = NULL; + partial = true; + switch (pc) { + case 1: + cp = "active"; + break; + case 2: + cp = "idle_a"; + break; + case 3: + cp = "idle_b"; + break; + case 4: + cp = "idle_c"; + break; + case 8: + cp = "standby_z"; + break; + case 9: + cp = "standby_y"; + break; + default: + snprintf(bb, sizeof(bb), "Reserved [0x%x]", pc); + cp = bb; + partial = false; + break; + } + if (partial) { + snprintf(b, sizeof(b), "%s %s", att, cp); + cp = b; + } + count = sg_get_unaligned_be(pl - 4, bp + 4); + sgj_pr_hr(jsp, " %s = %" PRIu64 "\n", cp, count); + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); + if (jsp->pr_as_json) { + js_snakenv_ihexstr_nex(jsp, jo3p, cp, count, true, + NULL, NULL, "saturating counter"); + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + } + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +static char * +temperature_str(int8_t t, bool reporting, char * b, int blen) +{ + if (-128 == t) { + if (reporting) + snprintf(b, blen, "%s", not_avail); + else + snprintf(b, blen, "no limit"); + } else + snprintf(b, blen, "%d C", t); + return b; +} + +static char * +humidity_str(uint8_t h, bool reporting, char * b, int blen) +{ + if (255 == h) { + if (reporting) + snprintf(b, blen, "%s", not_avail); + else + snprintf(b, blen, "no limit"); + } else if (h <= 100) + snprintf(b, blen, "%u %%", h); + else + snprintf(b, blen, "%s value [%u]", rsv_s, h); + return b; +} + +/* ENV_REPORTING_SUBPG [0xd,0x1] <env> introduced: SPC-5 (rev 02). "mounted" + * changed to "other" in spc5r11 */ +static bool +show_environmental_reporting_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc, blen; + bool other_valid; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[32]; + static const char * erlp = "Environmental reporting log page"; + static const char * temp = "Temperature"; + static const char * lmaxt = "Lifetime maximum temperature"; + static const char * lmint = "Lifetime minimum temperature"; + static const char * maxtspo = "Maximum temperature since power on"; + static const char * mintspo = "Minimum temperature since power on"; + static const char * maxot = "Maximum other temperature"; + static const char * minot = "Minimum other temperature"; + static const char * relhum = "Relative humidity"; + static const char * lmaxrh = "Lifetime maximum relative humidity"; + static const char * lminrh = "Lifetime minimum relative humidity"; + static const char * maxrhspo = "Maximum relative humidity since power on"; + static const char * minrhspo = "Minimum relative humidity since power on"; + static const char * maxorh = "Maximum other relative humidity"; + static const char * minorh = "Minimum other relative humidity"; + + blen = sizeof(b); + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0xd,0x1]\n", erlp); + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, erlp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "environmental_reporting_log_parameters"); + } + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + other_valid = !!(bp[4] & 1); + if (pc < 0x100) { + if (pl < 12) { + pr2serr(" <<expect parameter 0x%x to be at least 12 bytes " + "long, got %d, skip>>\n", pc, pl); + goto inner; + } + sgj_pr_hr(jsp, " %s=0x%x\n", param_c, pc); + sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc); + sgj_pr_hr(jsp, " OTV=%d\n", (int)other_valid); + sgj_js_nv_ihex_nex(jsp, jo3p, "otv", (int)other_valid, + false, "Other Temperature Valid"); + + temperature_str(bp[5], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", temp, b); + js_snakenv_ihexstr_nex(jsp, jo3p, temp, bp[5], false, + NULL, b, "current [Celsius]"); + temperature_str(bp[6], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lmaxt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lmaxt, bp[6], false, + NULL, b, NULL); + temperature_str(bp[7], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lmint, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lmint, bp[7], false, + NULL, b, NULL); + temperature_str(bp[8], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", maxtspo, b); + js_snakenv_ihexstr_nex(jsp, jo3p, maxtspo, bp[8], false, + NULL, b, NULL); + temperature_str(bp[9], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", mintspo, b); + js_snakenv_ihexstr_nex(jsp, jo3p, mintspo, bp[9], false, + NULL, b, NULL); + if (other_valid) { + temperature_str(bp[10], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", maxot, b); + js_snakenv_ihexstr_nex(jsp, jo3p, maxot, bp[10], false, + NULL, b, NULL); + temperature_str(bp[11], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", minot, b); + js_snakenv_ihexstr_nex(jsp, jo3p, minot, bp[11], false, + NULL, b, NULL); + } + } else if (pc < 0x200) { + if (pl < 12) { + pr2serr(" <<expect parameter 0x%x to be at least 12 bytes " + "long, got %d, skip>>\n", pc, pl); + goto inner; + } + sgj_pr_hr(jsp, " %s=0x%x\n", param_c, pc); + sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc); + sgj_pr_hr(jsp, " ORHV=%d\n", (int)other_valid); + sgj_js_nv_ihex_nex(jsp, jo3p, "orhv", (int)other_valid, + false, "Other Relative Humidity Valid"); + + humidity_str(bp[5], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", relhum, b); + js_snakenv_ihexstr_nex(jsp, jo3p, relhum, bp[5], false, + NULL, b, NULL); + humidity_str(bp[6], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lmaxrh, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lmaxrh, bp[6], false, + NULL, b, NULL); + humidity_str(bp[7], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lminrh, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lminrh, bp[7], false, + NULL, b, NULL); + humidity_str(bp[8], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", maxrhspo, b); + js_snakenv_ihexstr_nex(jsp, jo3p, maxrhspo, bp[8], false, + NULL, b, NULL); + humidity_str(bp[9], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", minrhspo, b); + js_snakenv_ihexstr_nex(jsp, jo3p, minrhspo, bp[9], false, + NULL, b, NULL); + if (other_valid) { + humidity_str(bp[10], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", maxorh, b); + js_snakenv_ihexstr_nex(jsp, jo3p, maxorh, bp[10], false, + NULL, b, NULL); + humidity_str(bp[11], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", minorh, b); + js_snakenv_ihexstr_nex(jsp, jo3p, minorh, bp[11], false, + NULL, b, NULL); + } + } else + sgj_pr_hr(jsp, " <<unexpected %s 0x%x\n", param_c, pc); + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +inner: + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* ENV_LIMITS_SUBPG [0xd,0x2] <enl> introduced: SPC-5 (rev 02) */ +static bool +show_environmental_limits_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc, blen; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[32]; + static const char * ellp = "Environmental limits log page"; + static const char * hctlt = "High critical temperature limit trigger"; + static const char * hctlr = "High critical temperature limit reset"; + static const char * lctlr = "High critical temperature limit reset"; + static const char * lctlt = "High critical temperature limit trigger"; + static const char * hotlt = "High operating temperature limit trigger"; + static const char * hotlr = "High operating temperature limit reset"; + static const char * lotlr = "High operating temperature limit reset"; + static const char * lotlt = "High operating temperature limit trigger"; + static const char * hcrhlt = + "High critical relative humidity limit trigger"; + static const char * hcrhlr = + "High critical relative humidity limit reset"; + static const char * lcrhlr = + "High critical relative humidity limit reset"; + static const char * lcrhlt = + "High critical relative humidity limit trigger"; + static const char * horhlt = + "High operating relative humidity limit trigger"; + static const char * horhlr = + "High operating relative humidity limit reset"; + static const char * lorhlr = + "High operating relative humidity limit reset"; + static const char * lorhlt = + "High operating relative humidity limit trigger"; + + blen = sizeof(b); + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0xd,0x2]\n", ellp); + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, ellp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "environmental_limits_log_parameters"); + } + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + if (pc < 0x100) { + if (pl < 12) { + pr2serr(" <<expect parameter 0x%x to be at least 12 bytes " + "long, got %d, skip>>\n", pc, pl); + goto inner; + } + sgj_pr_hr(jsp, " %s=0x%x\n", param_c, pc); + sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc); + + temperature_str(bp[4], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", hctlt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, hctlt, bp[4], false, + NULL, b, "[Celsius]"); + temperature_str(bp[5], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", hctlr, b); + js_snakenv_ihexstr_nex(jsp, jo3p, hctlr, bp[5], false, + NULL, b, NULL); + temperature_str(bp[6], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lctlr, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lctlr, bp[6], false, + NULL, b, NULL); + temperature_str(bp[7], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lctlt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lctlt, bp[7], false, + NULL, b, NULL); + temperature_str(bp[8], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", hotlt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, hotlt, bp[8], false, + NULL, b, NULL); + temperature_str(bp[9], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", hotlr, b); + js_snakenv_ihexstr_nex(jsp, jo3p, hotlr, bp[9], false, + NULL, b, NULL); + temperature_str(bp[10], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lotlr, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lotlr, bp[10], false, + NULL, b, NULL); + temperature_str(bp[11], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lotlt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lotlt, bp[11], false, + NULL, b, NULL); + } else if (pc < 0x200) { + if (pl < 12) { + pr2serr(" <<expect parameter 0x%x to be at least 12 bytes " + "long, got %d, skip>>\n", pc, pl); + goto inner; + } + sgj_pr_hr(jsp, " %s=0x%x\n", param_c, pc); + sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc); + + humidity_str(bp[4], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", hcrhlt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, hcrhlt, bp[4], false, + NULL, b, "[percentage]"); + humidity_str(bp[5], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", hcrhlr, b); + js_snakenv_ihexstr_nex(jsp, jo3p, hcrhlr, bp[5], false, + NULL, b, NULL); + humidity_str(bp[6], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lcrhlr, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lcrhlr, bp[6], false, + NULL, b, NULL); + humidity_str(bp[7], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lcrhlt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lcrhlt, bp[7], false, + NULL, b, NULL); + humidity_str(bp[8], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", horhlt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, horhlt, bp[8], false, + NULL, b, NULL); + humidity_str(bp[9], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", horhlr, b); + js_snakenv_ihexstr_nex(jsp, jo3p, horhlr, bp[9], false, + NULL, b, NULL); + humidity_str(bp[10], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lorhlr, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lorhlr, bp[10], false, + NULL, b, NULL); + humidity_str(bp[11], true, b, blen); + sgj_pr_hr(jsp, " %s: %s\n", lorhlt, b); + js_snakenv_ihexstr_nex(jsp, jo3p, lorhlt, bp[11], false, + NULL, b, NULL); + } else + sgj_pr_hr(jsp, " <<unexpected %s 0x%x\n", param_c, pc); + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); +inner: + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* CMD_DUR_LIMITS_SUBPG [0x19,0x21] <cdl> + * introduced: SPC-6 rev 1, significantly changed rev 6 */ +static bool +show_cmd_dur_limits_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + uint32_t count, noitmc_v, noatmc_v, noitatmc_v, noc_v; + const uint8_t * bp; + const char * cp; + const char * thp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[144]; + static const char * cdllp = "Command duration limits statistics log page"; + static const char * t2cdld = "T2 command duration limit descriptor"; + static const char * cdlt2amp = "CDL T2A mode page"; + static const char * cdlt2bmp = "CDL T2B mode page"; + static const char * first_7[] = {"First", "Second", "Third", "Fourth", + "Fifth", "Sixth", "Seventh"}; + static const char * noitmc = "Number of inactive target miss commands"; + static const char * noatmc = "Number of active target miss commands"; + static const char * noitatmc = + "Number of inactive target and active target miss commands"; + static const char * noc = "Number of commands"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x19,0x21]\n", cdllp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, cdllp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "command_duration_limits_statistcs_log_parameters"); + } + + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; /* parameter length */ + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + switch (pc) { + case 0x1: + /* spc6r06: table 349 name "Number of READ commands" seems to + * be wrong. Use what surrounding text and table 347 suggest */ + cp = "Achievable latency target"; + count = sg_get_unaligned_be32(bp + 4); + sgj_pr_hr(jsp, " %s = %" PRIu32 "\n", cp, count); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, cp); + js_snakenv_ihexstr_nex(jsp, jop, cp, count, true, NULL, NULL, + "unit: microsecond"); + } + break; + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + sgj_pr_hr(jsp, " %s code 0x%x restricted\n", param_c, pc); + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, rstrict_s); + break; + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + thp = first_7[pc - 0x21]; + sgj_pr_hr(jsp, " %s %s for %s [pc=0x%x]:\n", thp, t2cdld, + cdlt2amp, pc); + noitmc_v = sg_get_unaligned_be32(bp + 4); + sgj_pr_hr(jsp, " %s = %u\n", noitmc, noitmc_v); + noatmc_v = sg_get_unaligned_be32(bp + 8); + sgj_pr_hr(jsp, " %s = %u\n", noatmc, noatmc_v); + noitatmc_v = sg_get_unaligned_be32(bp + 12); + sgj_pr_hr(jsp, " %s = %u\n", noitatmc, noitatmc_v); + noc_v = sg_get_unaligned_be32(bp + 16); + sgj_pr_hr(jsp, " %s = %u\n", noc, noc_v); + if (jsp->pr_as_json) { + snprintf(b, sizeof(b), "%s %s for %s", thp, t2cdld, cdlt2amp); + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, b); + + js_snakenv_ihexstr_nex(jsp, jop, noitmc, noitmc_v, true, NULL, + NULL, NULL); + js_snakenv_ihexstr_nex(jsp, jop, noatmc, noatmc_v, true, NULL, + NULL, NULL); + js_snakenv_ihexstr_nex(jsp, jop, noitatmc, noitatmc_v, true, + NULL, NULL, NULL); + js_snakenv_ihexstr_nex(jsp, jop, noc, noc_v, true, NULL, + NULL, NULL); + } + break; + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + sgj_pr_hr(jsp, " %s 0x%x restricted\n", param_c, pc); + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, rstrict_s); + break; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + /* This short form introduced in draft spc6r06 */ + thp = first_7[pc - 0x41]; + sgj_pr_hr(jsp, " %s %s for %s [pc=0x%x]:\n", thp, t2cdld, + cdlt2bmp, pc); + noitmc_v = sg_get_unaligned_be32(bp + 4); + sgj_pr_hr(jsp, " %s = %u\n", noitmc, noitmc_v); + noatmc_v = sg_get_unaligned_be32(bp + 8); + sgj_pr_hr(jsp, " %s = %u\n", noatmc, noatmc_v); + noitatmc_v = sg_get_unaligned_be32(bp + 12); + sgj_pr_hr(jsp, " %s = %u\n", noitatmc, noitatmc_v); + noc_v = sg_get_unaligned_be32(bp + 16); + sgj_pr_hr(jsp, " %s = %u\n", noc, noc_v); + if (jsp->pr_as_json) { + snprintf(b, sizeof(b), "%s %s for %s", thp, t2cdld, cdlt2amp); + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, b); + + js_snakenv_ihexstr_nex(jsp, jop, noitmc, noitmc_v, true, NULL, + NULL, NULL); + js_snakenv_ihexstr_nex(jsp, jop, noatmc, noatmc_v, true, NULL, + NULL, NULL); + js_snakenv_ihexstr_nex(jsp, jop, noitatmc, noitatmc_v, true, + NULL, NULL, NULL); + js_snakenv_ihexstr_nex(jsp, jop, noc, noc_v, true, NULL, + NULL, NULL); + } + + break; + default: + sgj_pr_hr(jsp, " <<unexpected %s 0x%x\n", param_c, pc); + break; + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Tape usage: Vendor specific (LTO-5 and LTO-6): 0x30 */ +static bool +show_tape_usage_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int k, num, extra; + unsigned int n; + uint64_t ull; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + pr2serr("badly formed tape usage page\n"); + return false; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Tape usage page (LTO-5 and LTO-6 specific) [0x30]\n"); + for (k = num; k > 0; k -= extra, bp += extra) { + int pc = sg_get_unaligned_be16(bp + 0); + + extra = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + break; + } else if (op->do_hex) { + hex2stdout(bp, extra, op->dstrhex_no_ascii); + break; + } + ull = n = 0; + switch (bp[3]) { + case 2: + n = sg_get_unaligned_be16(bp + 4); + break; + case 4: + n = sg_get_unaligned_be32(bp + 4); + break; + case 8: + ull = sg_get_unaligned_be64(bp + 4); + break; + } + switch (pc) { + case 0x01: + if (extra == 8) + printf(" Thread count: %u", n); + break; + case 0x02: + if (extra == 12) + printf(" Total data sets written: %" PRIu64, ull); + break; + case 0x03: + if (extra == 8) + printf(" Total write retries: %u", n); + break; + case 0x04: + if (extra == 6) + printf(" Total unrecovered write errors: %u", n); + break; + case 0x05: + if (extra == 6) + printf(" Total suspended writes: %u", n); + break; + case 0x06: + if (extra == 6) + printf(" Total fatal suspended writes: %u", n); + break; + case 0x07: + if (extra == 12) + printf(" Total data sets read: %" PRIu64, ull); + break; + case 0x08: + if (extra == 8) + printf(" Total read retries: %u", n); + break; + case 0x09: + if (extra == 6) + printf(" Total unrecovered read errors: %u", n); + break; + case 0x0a: + if (extra == 6) + printf(" Total suspended reads: %u", n); + break; + case 0x0b: + if (extra == 6) + printf(" Total fatal suspended reads: %u", n); + break; + default: + printf(" unknown %s = 0x%x, contents in hex:\n", param_c, pc); + hex2stdout(bp, extra, 1); + break; + } + printf("\n"); + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); + if (op->filter_given) + break; + } + return true; +} + +/* 0x30 */ +static bool +show_hgst_perf_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + bool valid = false; + int num, pl; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("HGST/WDC performance counters page [0x30]\n"); + num = len - 4; + if (num < 0x30) { + printf("HGST/WDC performance counters page too short (%d) < 48\n", + num); + return valid; + } + bp = &resp[0] + 4; + while (num > 3) { + int pc = sg_get_unaligned_be16(bp + 0); + + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + switch (pc) { + case 0: + valid = true; + printf(" Zero Seeks = %u\n", sg_get_unaligned_be16(bp + 4)); + printf(" Seeks >= 2/3 = %u\n", sg_get_unaligned_be16(bp + 6)); + printf(" Seeks >= 1/3 and < 2/3 = %u\n", + sg_get_unaligned_be16(bp + 8)); + printf(" Seeks >= 1/6 and < 1/3 = %u\n", + sg_get_unaligned_be16(bp + 10)); + printf(" Seeks >= 1/12 and < 1/6 = %u\n", + sg_get_unaligned_be16(bp + 12)); + printf(" Seeks > 0 and < 1/12 = %u\n", + sg_get_unaligned_be16(bp + 14)); + printf(" Overrun Counter = %u\n", + sg_get_unaligned_be16(bp + 20)); + printf(" Underrun Counter = %u\n", + sg_get_unaligned_be16(bp + 22)); + printf(" Device Cache Full Read Hits = %u\n", + sg_get_unaligned_be32(bp + 24)); + printf(" Device Cache Partial Read Hits = %u\n", + sg_get_unaligned_be32(bp + 28)); + printf(" Device Cache Write Hits = %u\n", + sg_get_unaligned_be32(bp + 32)); + printf(" Device Cache Fast Writes = %u\n", + sg_get_unaligned_be32(bp + 36)); + printf(" Device Cache Read Misses = %u\n", + sg_get_unaligned_be32(bp + 40)); + break; + default: + valid = false; + printf(" Unknown HGST/WDC %s = 0x%x", param_c, pc); + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return valid; +} + +/* Tape capacity: vendor specific (LTO-5 and LTO-6 ?): 0x31 */ +static bool +show_tape_capacity_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int k, num, extra; + unsigned int n; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + pr2serr("badly formed tape capacity page\n"); + return false; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Tape capacity page (LTO-5 and LTO-6 specific) [0x31]\n"); + for (k = num; k > 0; k -= extra, bp += extra) { + int pc = sg_get_unaligned_be16(bp + 0); + + extra = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + break; + } else if (op->do_hex) { + hex2stdout(bp, extra, op->dstrhex_no_ascii); + break; + } + if (extra != 8) + continue; + n = sg_get_unaligned_be32(bp + 4); + switch (pc) { + case 0x01: + printf(" Main partition remaining capacity (in MiB): %u", n); + break; + case 0x02: + printf(" Alternate partition remaining capacity (in MiB): %u", n); + break; + case 0x03: + printf(" Main partition maximum capacity (in MiB): %u", n); + break; + case 0x04: + printf(" Alternate partition maximum capacity (in MiB): %u", n); + break; + default: + printf(" unknown %s = 0x%x, contents in hex:\n", param_c, pc); + hex2stdout(bp, extra, 1); + break; + } + printf("\n"); + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); + if (op->filter_given) + break; + } + return true; +} + +/* Data compression: originally vendor specific 0x32 (LTO-5), then + * ssc-4 standardizes it at 0x1b <dc> */ +static bool +show_data_compression_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int k, j, pl, num, extra, pc, pg_code; + uint64_t n; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + pg_code = resp[0] & 0x3f; + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + pr2serr("badly formed data compression page\n"); + return false; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (0x1b == pg_code) + printf("Data compression page (ssc-4) [0x1b]\n"); + else + printf("Data compression page (LTO-5 specific) [0x%x]\n", + pg_code); + } + for (k = num; k > 0; k -= extra, bp += extra) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3]; + extra = pl + 4; + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + break; + } else if (op->do_hex) { + hex2stdout(bp, extra, op->dstrhex_no_ascii); + break; + } + if ((0 == pl) || (pl > 8)) { + printf("badly formed data compression log parameter\n"); + printf(" %s = 0x%x, contents in hex:\n", param_c, pc); + hex2stdout(bp, extra, op->dstrhex_no_ascii); + goto skip_para; + } + /* variable length integer, max length 8 bytes */ + for (j = 0, n = 0; j < pl; ++j) { + if (j > 0) + n <<= 8; + n |= bp[4 + j]; + } + switch (pc) { + case 0x00: + printf(" Read compression ratio x100: %" PRIu64 , n); + break; + case 0x01: + printf(" Write compression ratio x100: %" PRIu64 , n); + break; + case 0x02: + printf(" Megabytes transferred to server: %" PRIu64 , n); + break; + case 0x03: + printf(" Bytes transferred to server: %" PRIu64 , n); + break; + case 0x04: + printf(" Megabytes read from tape: %" PRIu64 , n); + break; + case 0x05: + printf(" Bytes read from tape: %" PRIu64 , n); + break; + case 0x06: + printf(" Megabytes transferred from server: %" PRIu64 , n); + break; + case 0x07: + printf(" Bytes transferred from server: %" PRIu64 , n); + break; + case 0x08: + printf(" Megabytes written to tape: %" PRIu64 , n); + break; + case 0x09: + printf(" Bytes written to tape: %" PRIu64 , n); + break; + case 0x100: + printf(" Data compression enabled: 0x%" PRIx64, n); + break; + default: + printf(" unknown %s = 0x%x, contents in hex:\n", param_c, pc); + hex2stdout(bp, extra, op->dstrhex_no_ascii); + break; + } +skip_para: + printf("\n"); + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); + if (op->filter_given) + break; + } + return true; +} + +/* LAST_N_ERR_LPAGE [0x7] <lne> introduced: SPC-2 */ +static bool +show_last_n_error_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int k, num, pl; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[256]; + static const char * lneelp = "Last n error events log page"; + static const char * eed = "error_event_data"; + + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + sgj_pr_hr(jsp, "No error events logged\n"); + return true; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x7]\n", lneelp); + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, lneelp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, "error_event_log_parameters"); + } + + for (k = num; k > 0; k -= pl, bp += pl) { + uint16_t pc; + + if (k < 3) { + pr2serr("short %s\n", lneelp); + return false; + } + pl = bp[3] + 4; + pc = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, NULL); + } + + sgj_pr_hr(jsp, " Error event %u [0x%x]:\n", pc, pc); + if (pl > 4) { + if ((bp[2] & 0x1) && (bp[2] & 0x2)) { + sgj_pr_hr(jsp, " [binary]:\n"); + hex2str(bp + 4, pl - 4, " ", op->hex2str_oformat, + sizeof(b), b); + sgj_pr_hr(jsp, "%s\n", b); + if (jsp->pr_as_json) + sgj_js_nv_hex_bytes(jsp, jo3p, eed, bp + 4, pl - 4); + } else if (0x01 == (bp[2] & 0x3)) { /* ASCII */ + sgj_pr_hr(jsp, " %.*s\n", pl - 4, (const char *)(bp + 4)); + if (jsp->pr_as_json) + sgj_js_nv_s_len(jsp, jo3p, eed, + (const char *)(bp + 4), pl - 4); + } else { + sgj_pr_hr(jsp, " [data counter?? (LP bit should be " + "set)]:\n"); + hex2str(bp + 4, pl - 4, " ", op->hex2str_oformat, + sizeof(b), b); + sgj_pr_hr(jsp, "%s\n", b); + if (jsp->pr_as_json) + sgj_js_nv_hex_bytes(jsp, jo3p, eed, bp + 4, pl - 4); + } + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; + } + return true; +} + +/* LAST_N_DEFERRED_LPAGE [0xb] <lnd> introduced: SPC-2 */ +static bool +show_last_n_deferred_error_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int k, n, num, pl; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p, jo4p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[512]; + static const char * lndeoaelp = + "Last n deferred errors or asynchronous events log page"; + static const char * deoae = "Deferred error or asynchronous event"; + static const char * sd = "sense_data"; + + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + pr2serr("No deferred errors logged\n"); + return true; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0xb]\n", lndeoaelp); + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, lndeoaelp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "deferred_error_or_asynchronous_event_log_parameters"); + } + + for (k = num; k > 0; k -= pl, bp += pl) { + int pc; + + if (k < 3) { + pr2serr("short %s\n", lndeoaelp); + return false; + } + pl = bp[3] + 4; + pc = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, deoae); + } + sgj_pr_hr(jsp, " %s [0x%x]:\n", deoae, pc); + if (op->do_brief > 0) { + hex2stdout(bp + 4, pl - 4, op->dstrhex_no_ascii); + hex2str(bp + 4, pl - 4, " ", op->hex2str_oformat, + sizeof(b), b); + sgj_pr_hr(jsp, "%s\n", b); + if (jsp->pr_as_json) + sgj_js_nv_hex_bytes(jsp, jo3p, sd, bp + 4, pl - 4); + } else { + + n = sg_get_sense_str(" ", bp + 4, pl - 4, false, sizeof(b), + b); + sgj_pr_hr(jsp, "%.*s\n", n, b); + if (jsp->pr_as_json) { + jo4p = sgj_named_subobject_r(jsp, jo3p, sd); + sgj_js_sense(jsp, jo4p, bp + 4, pl - 4); + } + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; + } + return true; +} + +static const char * clgc = "Change list generation code"; +static const char * cgn = "Changed generation number"; + +/* LAST_N_INQUIRY_DATA_CH_SUBPG [0xb,0x1] <lnic> introduced: SPC-5 (rev 17) */ +static bool +show_last_n_inq_data_ch_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool vpd; + int j, num, pl, vpd_pg; + uint32_t k, n; + const uint8_t * bp; + const char * vpd_pg_name = NULL; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p, jo4p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + sgj_opaque_p ja2p; + char str[PCB_STR_LEN]; + char b[128]; + static const char * lnidclp = "Last n inquiry data changed log page"; + static const char * idci = "Inquiry data changed indicator"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0xb,0x1]\n", lnidclp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, lnidclp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "inquiry_data_changed_log_parameters"); + } + + while (num > 3) { + int pc = sg_get_unaligned_be16(bp + 0); + + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + 0 == pc ? clgc : idci); + } + if (0 == pc) { + if (pl < 8) { + pr2serr(" <<expect parameter 0x%x to be at least 8 bytes " + "long, got %d, skip>>\n", pc, pl); + goto skip; + } + sgj_pr_hr(jsp, " %s [pc=0x0]:\n", clgc); + for (j = 4, k = 1; j < pl; j +=4, ++k) { + n = sg_get_unaligned_be32(bp + j); + sgj_pr_hr(jsp, " %s [0x%x]: %u\n", cgn, k, n); + } + if (jsp->pr_as_json) { + ja2p = sgj_named_subarray_r(jsp, jo3p, + "changed_generation_numbers"); + for (j = 4, k = 1; j < pl; j +=4, ++k) { + jo4p = sgj_new_unattached_object_r(jsp); + n = sg_get_unaligned_be32(bp + j); + js_snakenv_ihexstr_nex(jsp, jo4p, cgn, n, true, NULL, + NULL, NULL); + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo4p); + } + } + } else { /* pc > 0x0 */ + int m; + const int nn = sg_lib_names_mode_len; + struct sg_lib_simple_value_name_t * nvp = sg_lib_names_vpd_arr; + + snprintf(b, sizeof(b), " %s 0x%x, ", param_c, pc); + vpd = !! (1 & *(bp + 4)); + vpd_pg = *(bp + 5); + if (vpd) { + for (m = 0; m < nn; ++m, ++nvp) { + if (nvp->value == vpd_pg) + break; + } + vpd_pg_name = (m < nn) ? nvp->name : NULL; + } else + vpd_pg_name = "Standard INQUIRY"; + + if (jsp->pr_as_json) { + sgj_js_nv_i(jsp, jo3p, "vpd", (int)vpd); + sgj_js_nv_ihex(jsp, jo3p, "changed_page_code", vpd_pg); + if (vpd_pg_name) + sgj_js_nv_s(jsp, jo3p, "changed_page_name", vpd_pg_name); + } + if (vpd) { + sgj_pr_hr(jsp, "%sVPD page 0x%x changed\n", b, vpd_pg); + if (0 == op->do_brief) { + if (vpd_pg_name) + sgj_pr_hr(jsp, " name: %s\n", vpd_pg_name); + } + } else + sgj_pr_hr(jsp, "%sStandard INQUIRY data changed\n", b); + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); +skip: + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; + num -= pl; + bp += pl; + } + return true; +} + +/* LAST_N_MODE_PG_DATA_CH_SUBPG [0xb,0x2] <lnmc> introduced: SPC-5 (rev 17) */ +static bool +show_last_n_mode_pg_data_ch_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool spf; + int j, k, num, pl, pg_code, spg_code; + uint32_t n; + const uint8_t * bp; + const char * mode_pg_name = NULL; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p, jo4p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + sgj_opaque_p ja2p; + char str[PCB_STR_LEN]; + char b[128]; + static const char * lnmpdclp = "Last n mode page data changed log page"; + static const char * mpdci = "Mode page data changed indicator"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0xb,0x2]\n", lnmpdclp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, lnmpdclp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "mode_page_data_changed_log_parameters"); + } + + while (num > 3) { + int pc = sg_get_unaligned_be16(bp + 0); + + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + 0 == pc ? clgc : mpdci); + } + if (0 == pc) { /* Same as LAST_N_INQUIRY_DATA_CH_SUBPG [0xb,0x1] */ + if (pl < 8) { + pr2serr(" <<expect parameter 0x%x to be at least 8 bytes " + "long, got %d, skip>>\n", pc, pl); + goto skip; + } + sgj_pr_hr(jsp, " %s [pc=0x0]:\n", clgc); + for (j = 4, k = 1; j < pl; j +=4, ++k) { + n = sg_get_unaligned_be32(bp + j); + sgj_pr_hr(jsp, " %s [0x%x]: %u\n", cgn, k, n); + } + if (jsp->pr_as_json) { + ja2p = sgj_named_subarray_r(jsp, jo3p, + "changed_generation_numbers"); + for (j = 4, k = 1; j < pl; j +=4, ++k) { + jo4p = sgj_new_unattached_object_r(jsp); + n = sg_get_unaligned_be32(bp + j); + js_snakenv_ihexstr_nex(jsp, jo4p, cgn, n, true, NULL, + NULL, NULL); + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo4p); + } + } + } else { /* pc > 0x0 */ + int k, val; + const int nn = sg_lib_names_mode_len; + struct sg_lib_simple_value_name_t * nmp = sg_lib_names_mode_arr; + + snprintf(b, sizeof(b), " %s 0x%x, ", param_c, pc); + spf = !! (0x40 & *(bp + 4)); + pg_code = 0x3f & *(bp + 4); + spg_code = *(bp + 5); + if (spf) /* SPF bit set */ + sgj_pr_hr(jsp, "%smode page 0x%x,0%x changed\n", b, pg_code, + spg_code); + else + sgj_pr_hr(jsp, "%smode page 0x%x changed\n", b, pg_code); + + val = (pg_code << 8) | spg_code; + for (k = 0; k < nn; ++k, ++nmp) { + if (nmp->value == val) + break; + } + mode_pg_name = (k < nn) ? nmp->name : NULL; + if ((0 == op->do_brief) && mode_pg_name) + sgj_pr_hr(jsp, " name: %s\n", nmp->name); + if (jsp->pr_as_json) { + sgj_js_nv_i(jsp, jo3p, "spf", (int)spf); + sgj_js_nv_ihex(jsp, jo3p, "mode_page_code", pg_code); + sgj_js_nv_ihex(jsp, jo3p, "subpage_code", spg_code); + if (mode_pg_name) + sgj_js_nv_s(jsp, jo3p, "mode_page_name", mode_pg_name); + } + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); +skip: + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->filter_given) + break; + num -= pl; + bp += pl; + } + return true; +} + +static const char * self_test_code[] = { + "default", "background short", "background extended", rsv_s, + "aborted background", "foreground short", "foreground extended", + rsv_s}; + +static const char * self_test_result[] = { + "completed without error", + "aborted by SEND DIAGNOSTIC", + "aborted other than by SEND DIAGNOSTIC", + "unknown error, unable to complete", + "self test completed with failure in test segment (which one unknown)", + "first segment in self test failed", + "second segment in self test failed", + "another segment in self test failed", + rsv_s, rsv_s, rsv_s, rsv_s, rsv_s, rsv_s, + rsv_s, + "self test in progress"}; + +/* SELF_TEST_LPAGE [0x10] <str> introduced: SPC-3 */ +static bool +show_self_test_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + bool addr_all_ffs; + int k, num, res, st_c; + unsigned int v; + uint32_t n; + uint64_t ull; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[80]; + static const char * strlp = "Self-test results log page"; + static const char * stc_s = "Self-test code"; + static const char * str_s = "Self-test result"; + static const char * stn_s = "Self-test number"; + static const char * apoh = "Accumulated power on hours"; + + num = len - 4; + if (num < 0x190) { + pr2serr("short %s [length 0x%x rather than 0x190 bytes]\n", strlp, + num); + return false; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x10]\n", strlp); + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, strlp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "self_test_results_log_parameters"); + } + + for (k = 0, bp = resp + 4; k < 20; ++k, bp += 20 ) { + int pc = sg_get_unaligned_be16(bp + 0); + int pl = bp[3] + 4; + + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, pl); + break; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + "Self-test results"); + } + n = sg_get_unaligned_be16(bp + 6); + if ((0 == n) && (0 == bp[4])) { + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + break; + } + sgj_pr_hr(jsp, " %s = %d, accumulated power-on hours = %d\n", + param_c, pc, n); + st_c = (bp[4] >> 5) & 0x7; + sgj_pr_hr(jsp, " %s: %s [%d]\n", stc_s, self_test_code[st_c], + st_c); + res = bp[4] & 0xf; + sgj_pr_hr(jsp, " %s: %s [%d]\n", str_s, self_test_result[res], + res); + if (bp[5]) + sgj_pr_hr(jsp, " %s = %d\n", stn_s, (int)bp[5]); + ull = sg_get_unaligned_be64(bp + 8); + + addr_all_ffs = sg_all_ffs(bp + 8, 8); + if (! addr_all_ffs) { + addr_all_ffs = false; + if ((res > 0) && ( res < 0xf)) + sgj_pr_hr(jsp, " address of first error = 0x%" PRIx64 "\n", + ull); + } + addr_all_ffs = false; + v = bp[16] & 0xf; + if (v) { + if (op->do_brief) + sgj_pr_hr(jsp, " %s = 0x%x , asc = 0x%x, ascq = 0x%x\n", + s_key, v, bp[17], bp[18]); + else { + sgj_pr_hr(jsp, " %s = 0x%x [%s]\n", s_key, v, + sg_get_sense_key_str(v, sizeof(b), b)); + + sgj_pr_hr(jsp, " asc = 0x%x, ascq = 0x%x [%s]\n", + bp[17], bp[18], sg_get_asc_ascq_str(bp[17], bp[18], + sizeof(b), b)); + } + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], + str, sizeof(str))); + if (jsp->pr_as_json) { + js_snakenv_ihexstr_nex(jsp, jo3p, stc_s, st_c, true, NULL, + self_test_code[st_c], NULL); + js_snakenv_ihexstr_nex(jsp, jo3p, str_s, res, true, NULL, + self_test_result[res], NULL); + js_snakenv_ihexstr_nex(jsp, jo3p, stn_s, bp[5], false, NULL, + NULL, "segment number that failed"); + js_snakenv_ihexstr_nex(jsp, jo3p, apoh, n, true, NULL, + (0xffff == n ? "65535 hours or more" : NULL), NULL); + sgj_js_nv_ihexstr(jsp, jo3p, "address_of_first_failure", pc, NULL, + addr_all_ffs ? "no errors detected" : NULL); + sgj_js_nv_ihexstr(jsp, jo3p, "sense_key", v, NULL, + sg_get_sense_key_str(v, sizeof(b), b)); + sgj_js_nv_ihexstr(jsp, jo3p, "additional_sense_code", bp[17], + NULL, NULL); + sgj_js_nv_ihexstr(jsp, jo3p, "additional_sense_code_qualifier", + bp[18], NULL, sg_get_asc_ascq_str(bp[17], + bp[18], sizeof(b), b)); + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + } + if (op->filter_given) + break; + } + return true; +} + +/* TEMPERATURE_LPAGE [0xd] <temp> introduced: SPC-3 + * N.B. The ENV_REPORTING_SUBPG [0xd,0x1] and the ENV_LIMITS_SUBPG [0xd,0x2] + * (both added SPC-5) are a superset of this page. */ +static bool +show_temperature_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int k, num, extra; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + static const char * tlp = "Temperature log page"; + static const char * ctemp = "Current temperature"; + static const char * rtemp = "Reference temperature"; + + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + pr2serr("badly formed Temperature page\n"); + return false; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (! op->do_temperature) + sgj_pr_hr(jsp, "%s [0xd]\n", tlp); + } + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, tlp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, "temperature_log_parameters"); + } + + for (k = num; k > 0; k -= extra, bp += extra) { + int pc; + + if (k < 3) { + pr2serr("short Temperature page\n"); + return true; + } + extra = bp[3] + 4; + pc = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + goto skip; + } else if (op->do_hex) { + hex2stdout(bp, extra, op->dstrhex_no_ascii); + goto skip; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + sgj_js_nv_ihex(jsp, jo3p, param_c_sn, pc); + } + + switch (pc) { + case 0: + if ((extra > 5) && (k > 5)) { + if (0 == bp[5]) + sgj_pr_hr(jsp, " %s = 0 C (or less)\n", ctemp); + else if (bp[5] < 0xff) + sgj_pr_hr(jsp, " %s = %d C\n", ctemp, bp[5]); + else + sgj_pr_hr(jsp, " %s = <%s>\n", ctemp, not_avail); + if (jsp->pr_as_json) { + const char * cp = NULL; + + if (0 == bp[5]) + cp = "0 or less Celsius"; + else if (0xff == bp[5]) + cp = "temperature not available"; + js_snakenv_ihexstr_nex(jsp, jo3p, "temperature", bp[5], + false, NULL, cp, + "current [unit: celsius]"); + } + } + break; + case 1: + if ((extra > 5) && (k > 5)) { + if (bp[5] < 0xff) + sgj_pr_hr(jsp, " %s = %d C\n", rtemp, bp[5]); + else + sgj_pr_hr(jsp, " %s = <%s>\n", rtemp, not_avail); + if (jsp->pr_as_json) { + const char * cp; + + if (0 == bp[5]) + cp = "in C (or less)"; + else if (0xff == bp[5]) + cp = not_avail; + else + cp = "in C"; + sgj_js_nv_ihex_nex(jsp, jo3p, "reference_temperature", + bp[5], true, cp); + } + } + break; + default: + if (! op->do_temperature) { + sgj_pr_hr(jsp, " unknown %s = 0x%x, contents in hex:\n", + param_c, pc); + hex2stdout(bp, extra, op->dstrhex_no_ascii); + } else { + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + continue; + } + break; + } + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +skip: + if (op->filter_given) + break; + } + return true; +} + +/* START_STOP_LPAGE [0xe] <sscc> introduced: SPC-3 */ +static bool +show_start_stop_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int k, num, extra; + uint32_t val; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[256]; + static const char * sscclp = "Start-stop cycle counter log page"; + static const char * dom = "Date of manufacture"; + static const char * ad = "Accounting date"; + static const char * sccodl = "Specified cycle count over device lifetime"; + static const char * assc = "Accumulated start-stop cycles"; + static const char * slucodl = + "Specified load-unload count over device lifetime"; + static const char * aluc = "Accumulated load-unload cycles"; + + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + pr2serr("badly formed Start-stop cycle counter page\n"); + return false; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0xe]\n", sscclp); + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, sscclp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "start_stop_cycle_log_parameters"); + } + + for (k = num; k > 0; k -= extra, bp += extra) { + int pc; + + if (k < 3) { + pr2serr("short %s\n", sscclp); + return false; + } + extra = bp[3] + 4; + pc = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + goto skip; + } else if (op->do_hex) { + hex2stdout(bp, extra, op->dstrhex_no_ascii); + goto skip; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + switch (pc) { + case 1: + if (10 == extra) { + sgj_pr_hr(jsp, " %s, year: %.4s, week: %.2s\n", dom, + bp + 4, bp + 8); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + "Date of manufacture"); + sgj_js_nv_s_len(jsp, jo3p, "year_of_manufacture", + (const char *)(bp + 4), 4); + sgj_js_nv_s_len(jsp, jo3p, "week_of_manufacture", + (const char *)(bp + 8), 2); + } + } else if (op->verbose) { + pr2serr("%s parameter length strange: %d\n", dom, extra - 4); + hex2stderr(bp, extra, 1); + } + break; + case 2: + if (10 == extra) { + sgj_pr_hr(jsp, " %s, year: %.4s, week: %.2s\n", ad, bp + 4, + bp + 8); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + "Accounting date"); + sgj_js_nv_s_len(jsp, jo3p, "year_of_manufacture", + (const char *)(bp + 4), 4); + sgj_js_nv_s_len(jsp, jo3p, "week_of_manufacture", + (const char *)(bp + 8), 2); + } + } else if (op->verbose) { + pr2serr("%s parameter length strange: %d\n", ad, extra - 4); + hex2stderr(bp, extra, 1); + } + break; + case 3: + if (extra > 7) { + val = sg_get_unaligned_be32(bp + 4); + sgj_pr_hr(jsp, " %s = %u\n", sccodl, val); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + sccodl); + js_snakenv_ihexstr_nex(jsp, jo3p, sccodl, val, false, + NULL, NULL, NULL); + } + } + break; + case 4: + if (extra > 7) { + val = sg_get_unaligned_be32(bp + 4); + sgj_pr_hr(jsp, " %s = %u\n", assc, val); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + assc); + js_snakenv_ihexstr_nex(jsp, jo3p, assc, val, false, + NULL, NULL, NULL); + } + } + break; + case 5: + if (extra > 7) { + val = sg_get_unaligned_be32(bp + 4); + sgj_pr_hr(jsp, " %s = %u\n", slucodl, val); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + slucodl); + js_snakenv_ihexstr_nex(jsp, jo3p, slucodl, val, false, + NULL, NULL, NULL); + } + } + break; + case 6: + if (extra > 7) { + val = sg_get_unaligned_be32(bp + 4); + sgj_pr_hr(jsp, " %s = %u\n", aluc, val); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, aluc); + js_snakenv_ihexstr_nex(jsp, jo3p, aluc, val, false, + NULL, NULL, NULL); + } + } + break; + default: + sgj_pr_hr(jsp, " unknown %s = 0x%x, contents in hex:\n", + param_c, pc); + hex2str(bp, extra, " ", op->hex2str_oformat, sizeof(b), b); + sgj_pr_hr(jsp, "%s\n", b); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, unknown_s); + sgj_js_nv_hex_bytes(jsp, jo3p, in_hex, bp, extra); + } + break; + } + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +skip: + if (op->filter_given) + break; + } + return true; +} + +/* APP_CLIENT_LPAGE [0xf] <ac> introduced: SPC-3 */ +static bool +show_app_client_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int k, n, num, extra; + char * mp; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p = NULL; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + static const char * aclp = "Application Client log page"; + static const char * guac = "General Usage Application Client"; + + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + pr2serr("badly formed %s\n", aclp); + return false; + } + if (op->verbose || ((! op->do_raw) && (op->do_hex == 0))) + sgj_pr_hr(jsp, "%s [0xf]\n", aclp); + if (jsp->pr_as_json) + jo2p = sg_log_js_hdr(jsp, jop, aclp, resp); + if ((0 == op->filter_given) && (! op->do_full)) { + if ((len > 128) && (0 == op->do_hex) && (0 == op->undefined_hex)) { + char d[256]; + + hex2str(resp, 64, " ", op->hex2str_oformat, sizeof(d), d); + sgj_pr_hr(jsp, "%s", d); + sgj_pr_hr(jsp, " ..... [truncated after 64 of %d bytes (use " + "'-H' to see the rest)]\n", len); + if (jsp->pr_as_json) { + sgj_js_nv_ihex(jsp, jo2p, "actual_length", len); + sgj_js_nv_ihex(jsp, jo2p, "truncated_length", 64); + sgj_js_nv_hex_bytes(jsp, jo2p, in_hex, resp, 64); + } + } else { + n = len * 4 + 32; + mp = malloc(n); + if (mp) { + hex2str(resp, len, " ", op->hex2str_oformat, n, mp); + sgj_pr_hr(jsp, "%s", mp); + if (jsp->pr_as_json) { + sgj_js_nv_ihex(jsp, jo2p, "length", len); + sgj_js_nv_hex_bytes(jsp, jo2p, in_hex, resp, len); + } + free(mp); + } + } + return true; + } + if (jsp->pr_as_json) + jap = sgj_named_subarray_r(jsp, jo2p, + "application_client_log_parameters"); + + /* here if filter_given set or --full given */ + for (k = num; k > 0; k -= extra, bp += extra) { + int pc; + char d[1024]; + + if (k < 3) { + pr2serr("short %s\n", aclp); + return true; + } + extra = bp[3] + 4; + pc = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + break; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + sgj_pr_hr(jsp, " %s = %d [0x%x] %s\n", param_c, pc, pc, + (pc <= 0xfff) ? guac : ""); + hex2str(bp, extra, " ", op->hex2str_oformat, sizeof(d), d); + sgj_pr_hr(jsp, "%s", d); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + (pc <= 0xfff) ? guac : NULL); + sgj_js_nv_hex_bytes(jsp, jo3p, in_hex, bp, extra); + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + } + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); + if (op->filter_given) + break; + } + return true; +} + +/* IE_LPAGE [0x2f] <ie> "Informational Exceptions" introduced: SPC-3 + * Previously known as "SMART Status and Temperature Reading" lpage. */ +static bool +show_ie_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + bool skip = false; + int k, num, param_len; + const uint8_t * bp; + const char * cp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[512]; + char bb[64]; + bool full, decoded; + static const char * ielp = "Informational exceptions log page"; + static const char * ieasc = + "informational_exceptions_additional_sense_code"; + static const char * ct = "Current temperature"; + static const char * tt = "Threshold temperature"; + static const char * mt = "Maximum temperature"; + static const char * ce = "common extension"; + + full = ! op->do_temperature; + num = len - 4; + bp = &resp[0] + 4; + if (num < 4) { + pr2serr("badly formed %s\n", ielp); + return false; + } + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (full) + sgj_pr_hr(jsp, "%s [0x2f]\n", ielp); + } + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, ielp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "informational_exceptions_log_parameters"); + } + + for (k = num; k > 0; k -= param_len, bp += param_len) { + int pc; + + if (k < 3) { + pr2serr("short %s\n", ielp); + return false; + } + param_len = bp[3] + 4; + pc = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, param_len); + goto skip; + } else if (op->do_hex) { + hex2stdout(bp, param_len, op->dstrhex_no_ascii); + goto skip; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + decoded = true; + cp = NULL; + + switch (pc) { + case 0x0: + if (param_len > 5) { + bool na; + uint8_t t; + + if (full) { + sgj_pr_hr(jsp, " IE asc = 0x%x, ascq = 0x%x\n", bp[4], + bp[5]); + if (bp[4] || bp[5]) + if(sg_get_asc_ascq_str(bp[4], bp[5], sizeof(b), b)) + sgj_pr_hr(jsp, " [%s]\n", b); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + "Informational exceptions general"); + sgj_js_nv_ihexstr(jsp, jo3p, ieasc, bp[4], NULL, + NULL); + snprintf(b, sizeof(b), "%s_qualifier", ieasc); + sgj_js_nv_ihexstr(jsp, jo3p, b, bp[5], NULL, + sg_get_asc_ascq_str(bp[4], bp[5], + sizeof(bb), bb)); + } + } + if (param_len <= 6) + break; + t = bp[6]; + na = (0xff == t); + if (na) + snprintf(b, sizeof(b), "%u C", t); + else + snprintf(b, sizeof(b), "<%s>", unknown_s); + sgj_pr_hr(jsp, " %s = %s\n", ct, b); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, ct, t, true, + NULL, na ? unknown_s : NULL, + "[unit: celsius]"); + if (param_len > 7) { + t = bp[7]; + na = (0xff == t); + if (na) + snprintf(b, sizeof(b), "%u C", t); + else + snprintf(b, sizeof(b), "<%s>", unknown_s); + sgj_pr_hr(jsp, " %s = %s [%s]\n", tt, b, ce); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, tt, t, true, NULL, + na ? unknown_s : NULL, ce); + t = bp[8]; + if ((param_len > 8) && (t >= bp[6])) { + na = (0xff == t); + if (na) + snprintf(b, sizeof(b), "%u C", t); + else + snprintf(b, sizeof(b), "<%s>", unknown_s); + sgj_pr_hr(jsp, " %s = %s [%s]\n", mt, b, ce); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, mt, t, true, + NULL, + na ? unknown_s : NULL, ce); + } + } + } + decoded = true; + break; + default: + if (op->do_brief > 0) { + cp = NULL; + skip = true; + break; + } + if (VP_HITA == op->vend_prod_num) { + switch (pc) { + case 0x1: + cp = "Remaining reserve 1"; + break; + case 0x2: + cp = "Remaining reserve XOR"; + break; + case 0x3: + cp = "XOR depletion"; + break; + case 0x4: + cp = "Volatile memory backup failure"; + break; + case 0x5: + cp = "Wear indicator"; + break; + case 0x6: + cp = "System area wear indicator"; + break; + case 0x7: + cp = "Channel hangs"; + break; + case 0x8: + cp = "Flash scan failure"; + break; + default: + decoded = false; + break; + } + if (cp) { + sgj_pr_hr(jsp, " %s:\n", cp); + sgj_pr_hr(jsp, " SMART sense_code=0x%x sense_qualifier" + "=0x%x threshold=%d%% trip=%d\n", bp[4], bp[5], + bp[6], bp[7]); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + cp); + sgj_js_nv_ihex(jsp, jo3p, "smart_sense_code", bp[4]); + sgj_js_nv_ihex(jsp, jo3p, "smart_sense_qualifier", + bp[5]); + sgj_js_nv_ihex(jsp, jo3p, "smart_threshold", bp[6]); + sgj_js_nv_ihex(jsp, jo3p, "smart_trip", bp[7]); + } + } + } else { + decoded = false; + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + unknown_s); + } + break; + } /* end of switch statement */ + if (skip) + skip = false; + else if ((! decoded) && full) { + hex2str(bp, param_len, " ", op->hex2str_oformat, sizeof(b), b); + sgj_pr_hr(jsp, " %s = 0x%x, contents in hex:\n%s", param_c, pc, + b); + } + + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +skip: + if (op->filter_given) + break; + } /* end of for loop */ + return true; +} + +/* called for SAS port of PROTO_SPECIFIC_LPAGE [0x18] */ +static const char * +show_sas_phy_event_info(int pes, unsigned int val, unsigned int thresh_val, + char * b, int blen) +{ + int n = 0; + unsigned int u; + const char * cp = ""; + static const char * pvdt = "Peak value detector threshold"; + + switch (pes) { + case 0: + cp = "No event"; + snprintf(b, blen, "%s", cp); + break; + case 0x1: + cp = "Invalid word count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x2: + cp = "Running disparity error count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x3: + cp = "Loss of dword synchronization count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x4: + cp = "Phy reset problem count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x5: + cp = "Elasticity buffer overflow count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x6: + cp = "Received ERROR count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x7: + cp = "Invalid SPL packet count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x8: + cp = "Loss of SPL packet synchronization count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x20: + cp = "Received address frame error count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x21: + cp = "Transmitted abandon-class OPEN_REJECT count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x22: + cp = "Received abandon-class OPEN_REJECT count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x23: + cp = "Transmitted retry-class OPEN_REJECT count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x24: + cp = "Received retry-class OPEN_REJECT count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x25: + cp = "Received AIP (WAITING ON PARTIAL) count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x26: + cp = "Received AIP (WAITING ON CONNECTION) count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x27: + cp = "Transmitted BREAK count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x28: + cp = "Received BREAK count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x29: + cp = "Break timeout count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x2a: + cp = "Connection count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x2b: + cp = "Peak transmitted pathway blocked count"; + n = sg_scnpr(b, blen, "%s: %u", cp, val & 0xff); + sg_scnpr(b + n, blen - n, "\t%s: %u", pvdt, thresh_val & 0xff); + break; + case 0x2c: + cp = "Peak transmitted arbitration wait time"; + u = val & 0xffff; + if (u < 0x8000) + n = sg_scnpr(b, blen, "%s (us): %u", cp, u); + else + n = sg_scnpr(b, blen, "%s (ms): %u", cp, 33 + (u - 0x8000)); + u = thresh_val & 0xffff; + if (u < 0x8000) + sg_scnpr(b + n, blen - n, "\t%s (us): %u", pvdt, u); + else + sg_scnpr(b + n, blen - n, "\t%s (ms): %u", pvdt, + 33 + (u - 0x8000)); + break; + case 0x2d: + cp = "Peak arbitration time"; + n = sg_scnpr(b, blen, "%s (us): %u", cp, val); + sg_scnpr(b + n, blen - n, "\t%s: %u", pvdt, thresh_val); + break; + case 0x2e: + cp = "Peak connection time"; + n = sg_scnpr(b, blen, "%s (us): %u", cp, val); + sg_scnpr(b + n, blen - n, "\t%s: %u", pvdt, thresh_val); + break; + case 0x2f: + cp = "Persistent connection count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x40: + cp = "Transmitted SSP frame count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x41: + cp = "Received SSP frame count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x42: + cp = "Transmitted SSP frame error count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x43: + cp = "Received SSP frame error count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x44: + cp = "Transmitted CREDIT_BLOCKED count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x45: + cp = "Received CREDIT_BLOCKED count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x50: + cp = "Transmitted SATA frame count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x51: + cp = "Received SATA frame count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x52: + cp = "SATA flow control buffer overflow count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x60: + cp = "Transmitted SMP frame count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x61: + cp = "Received SMP frame count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + case 0x63: + cp = "Received SMP frame error count"; + snprintf(b, blen, "%s: %u", cp, val); + break; + default: + cp = ""; + snprintf(b, blen, "Unknown phy event source: %d, val=%u, " + "thresh_val=%u", pes, val, thresh_val); + break; + } + return cp; +} + +static const char * sas_link_rate_arr[16] = { + "phy enabled; unknown rate", + "phy disabled", + "phy enabled; speed negotiation failed", + "phy enabled; SATA spinup hold state", + "phy enabled; port selector", + "phy enabled; reset in progress", + "phy enabled; unsupported phy attached", + "reserved [0x7]", + "1.5 Gbps", /* 0x8 */ + "3 Gbps", + "6 Gbps", + "12 Gbps", + "22.5 Gbps", + "reserved [0xd]", + "reserved [0xe]", + "reserved [0xf]", +}; + +static char * +sas_negot_link_rate(int lrate, char * b, int blen) +{ + int mask = 0xf; + + if (~mask & lrate) + snprintf(b, blen, "bad link_rate value=0x%x\n", lrate); + else + snprintf(b, blen, "%s", sas_link_rate_arr[lrate]); + return b; +} + +/* helper for SAS port of PROTO_SPECIFIC_LPAGE [0x18] */ +static void +show_sas_port_param(const uint8_t * bp, int param_len, struct opts_t * op, + sgj_opaque_p jop) +{ + int j, m, nphys, t, spld_len, pi; + uint64_t ull; + unsigned int ui, ui2, ui3, ui4; + char * cp; + const char * ccp; + const char * cc2p; + const char * cc3p; + const char * cc4p; + const uint8_t * vcp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p = NULL; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + sgj_opaque_p ja2p = NULL; + char b[160]; + char s[80]; + static char * rtpi = "Relative target port identifier"; + static char * psplpfstp = + "Protocol Specific Port log parameter for SAS target port"; + static char * at = "attached"; + static char * ip = "initiator_port"; + static char * tp = "target_port"; + static char * pvdt = "peak_value_detector_threshold"; + static const int sz = sizeof(s); + static const int blen = sizeof(b); + + t = sg_get_unaligned_be16(bp + 0); + if (op->do_name) + sgj_pr_hr(jsp, " rel_target_port=%d\n", t); + else + sgj_pr_hr(jsp, " %s = %d\n", rtpi, t); + if (op->do_name) + sgj_pr_hr(jsp, " gen_code=%d\n", bp[6]); + else + sgj_pr_hr(jsp, " generation code = %d\n", bp[6]); + nphys = bp[7]; + if (op->do_name) + sgj_pr_hr(jsp, " num_phys=%d\n", nphys); + else + sgj_pr_hr(jsp, " number of phys = %d\n", nphys); + if (jsp->pr_as_json) { + js_snakenv_ihexstr_nex(jsp, jop, param_c , t, true, + NULL, psplpfstp, rtpi); + pi = 0xf & bp[4]; + sgj_js_nv_ihexstr(jsp, jop, "protocol_identifier", pi, NULL, + sg_get_trans_proto_str(pi, blen, b)); + sgj_js_nv_ihex(jsp, jop, "generation_code", bp[6]); + sgj_js_nv_ihex(jsp, jop, "number_of_phys", bp[7]); + jap = sgj_named_subarray_r(jsp, jop, "sas_phy_log_descriptor_list"); + } + + for (j = 0, vcp = bp + 8; j < (param_len - 8); + vcp += spld_len, j += spld_len) { + if (jsp->pr_as_json) { + jo2p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo2p, vcp[2]); + } + if (op->do_name) + sgj_pr_hr(jsp, " phy_id=%d\n", vcp[1]); + else + sgj_haj_vi(jsp, jo2p, 2, "phy identifier", SGJ_SEP_EQUAL_1_SPACE, + vcp[1], true); + spld_len = vcp[3]; + if (spld_len < 44) + spld_len = 48; /* in SAS-1 and SAS-1.1 vcp[3]==0 */ + else + spld_len += 4; + if (op->do_name) { + t = ((0x70 & vcp[4]) >> 4); + sgj_pr_hr(jsp, " att_dev_type=%d\n", t); + sgj_pr_hr(jsp, " att_iport_mask=0x%x\n", vcp[6]); + sgj_pr_hr(jsp, " att_phy_id=%d\n", vcp[24]); + sgj_pr_hr(jsp, " att_reason=0x%x\n", (vcp[4] & 0xf)); + ull = sg_get_unaligned_be64(vcp + 16); + sgj_pr_hr(jsp, " att_sas_addr=0x%" PRIx64 "\n", ull); + sgj_pr_hr(jsp, " att_tport_mask=0x%x\n", vcp[7]); + ui = sg_get_unaligned_be32(vcp + 32); + sgj_pr_hr(jsp, " inv_dwords=%u\n", ui); + ui = sg_get_unaligned_be32(vcp + 40); + sgj_pr_hr(jsp, " loss_dword_sync=%u\n", ui); + sgj_pr_hr(jsp, " neg_log_lrate=%d\n", 0xf & vcp[5]); + ui = sg_get_unaligned_be32(vcp + 44); + sgj_pr_hr(jsp, " phy_reset_probs=%u\n", ui); + ui = sg_get_unaligned_be32(vcp + 36); + sgj_pr_hr(jsp, " running_disparity=%u\n", ui); + sgj_pr_hr(jsp, " reason=0x%x\n", (vcp[5] & 0xf0) >> 4); + ull = sg_get_unaligned_be64(vcp + 8); + sgj_pr_hr(jsp, " sas_addr=0x%" PRIx64 "\n", ull); + } else { + t = ((0x70 & vcp[4]) >> 4); + /* attached SAS device type. In SAS-1.1 case 2 was an edge + * expander; in SAS-2 case 3 is marked as obsolete. */ + switch (t) { + case 0: snprintf(s, sz, "no device %s", at); break; + case 1: snprintf(s, sz, "SAS or SATA device"); break; + case 2: snprintf(s, sz, "expander device"); break; + case 3: snprintf(s, sz, "expander device (fanout)"); break; + default: snprintf(s, sz, "%s [%d]", rsv_s, t); break; + } + /* the word 'SAS' in following added in spl4r01 */ + sgj_pr_hr(jsp, " %s SAS device type: %s\n", at, s); + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo2p, "attached_sas_device_type", t, + NULL, s); + t = 0xf & vcp[4]; + switch (t) { + case 0: snprintf(s, sz, "%s", unknown_s); break; + case 1: snprintf(s, sz, "power on"); break; + case 2: snprintf(s, sz, "hard reset"); break; + case 3: snprintf(s, sz, "SMP phy control function"); break; + case 4: snprintf(s, sz, "loss of dword synchronization"); break; + case 5: snprintf(s, sz, "mux mix up"); break; + case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA"); + break; + case 7: snprintf(s, sz, "break timeout timer expired"); break; + case 8: snprintf(s, sz, "phy test function stopped"); break; + case 9: snprintf(s, sz, "expander device reduced functionality"); + break; + default: snprintf(s, sz, "%s [0x%x]", rsv_s, t); break; + } + sgj_pr_hr(jsp, " %s reason: %s\n", at, s); + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo2p, "attached_reason", t, NULL, s); + t = (vcp[5] & 0xf0) >> 4; + switch (t) { + case 0: snprintf(s, sz, "%s", unknown_s); break; + case 1: snprintf(s, sz, "power on"); break; + case 2: snprintf(s, sz, "hard reset"); break; + case 3: snprintf(s, sz, "SMP phy control function"); break; + case 4: snprintf(s, sz, "loss of dword synchronization"); break; + case 5: snprintf(s, sz, "mux mix up"); break; + case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA"); + break; + case 7: snprintf(s, sz, "break timeout timer expired"); break; + case 8: snprintf(s, sz, "phy test function stopped"); break; + case 9: snprintf(s, sz, "expander device reduced functionality"); + break; + default: snprintf(s, sz, "%s [0x%x]", rsv_s, t); break; + } + sgj_pr_hr(jsp, " reason: %s\n", s); + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo2p, "reason", t, NULL, s); + t = (0xf & vcp[5]); + ccp = "negotiated logical link rate"; + cc2p = sas_negot_link_rate(t, s, sz); + sgj_pr_hr(jsp, " %s: %s\n", ccp, cc2p); + if (jsp->pr_as_json) { + sgj_convert_to_snake_name(ccp, b, blen); + sgj_js_nv_ihexstr(jsp, jo2p, b, t, NULL, cc2p); + } + + sgj_pr_hr(jsp, " %s initiator port: ssp=%d stp=%d smp=%d\n", + at, !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2)); + if (jsp->pr_as_json) { + snprintf(b, blen, "%s_ssp_%s", at, ip); + sgj_js_nv_i(jsp, jo2p, b, !! (vcp[6] & 8)); + snprintf(b, blen, "%s_stp_%s", at, ip); + sgj_js_nv_i(jsp, jo2p, b, !! (vcp[6] & 4)); + snprintf(b, blen, "%s_smp_%s", at, ip); + sgj_js_nv_i(jsp, jo2p, b, !! (vcp[6] & 2)); + } + sgj_pr_hr(jsp, " %s target port: ssp=%d stp=%d smp=%d\n", at, + !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2)); + if (jsp->pr_as_json) { + snprintf(b, blen, "%s_ssp_%s", at, tp); + sgj_js_nv_i(jsp, jo2p, b, !! (vcp[7] & 8)); + snprintf(b, blen, "%s_stp_%s", at, tp); + sgj_js_nv_i(jsp, jo2p, b, !! (vcp[7] & 4)); + snprintf(b, blen, "%s_smp_%s", at, tp); + sgj_js_nv_i(jsp, jo2p, b, !! (vcp[7] & 2)); + } + ull = sg_get_unaligned_be64(vcp + 8); + sgj_pr_hr(jsp, " SAS address = 0x%" PRIx64 "\n", ull); + if (jsp->pr_as_json) + sgj_js_nv_ihex(jsp, jo2p, "sas_address", ull); + ull = sg_get_unaligned_be64(vcp + 16); + sgj_pr_hr(jsp, " %s SAS address = 0x%" PRIx64 "\n", at, ull); + if (jsp->pr_as_json) + sgj_js_nv_ihex(jsp, jo2p, "attached_sas_address", ull); + ccp = "attached phy identifier"; + sgj_haj_vi(jsp, jo2p, 4, ccp, SGJ_SEP_EQUAL_1_SPACE, vcp[24], + true); + ccp = "Invalid DWORD count"; + ui = sg_get_unaligned_be32(vcp + 32); + cc2p = "Running disparity error count"; + ui2 = sg_get_unaligned_be32(vcp + 36); + cc3p = "Loss of DWORD synchronization count"; + ui3 = sg_get_unaligned_be32(vcp + 40); + cc4p = "Phy reset problem count"; + ui4 = sg_get_unaligned_be32(vcp + 44); + if (jsp->pr_as_json) { + sgj_convert_to_snake_name(ccp, b, blen); + sgj_js_nv_ihex(jsp, jo2p, b, ui); + sgj_convert_to_snake_name(cc2p, b, blen); + sgj_js_nv_ihex(jsp, jo2p, b, ui2); + sgj_convert_to_snake_name(cc3p, b, blen); + sgj_js_nv_ihex(jsp, jo2p, b, ui3); + sgj_convert_to_snake_name(cc4p, b, blen); + sgj_js_nv_ihex(jsp, jo2p, b, ui4); + } else { + if (0 == op->do_brief) { + sgj_pr_hr(jsp, " %s = %u\n", ccp, ui); + sgj_pr_hr(jsp, " %s = %u\n", cc2p, ui2); + sgj_pr_hr(jsp, " %s = %u\n", cc3p, ui3); + sgj_pr_hr(jsp, " %s = %u\n", cc4p, ui4); + } + } + } + if (op->do_brief > 0) + goto skip; + if (spld_len > 51) { + int num_ped; + const uint8_t * xcp; + + num_ped = vcp[51]; + if (op->verbose > 1) + sgj_pr_hr(jsp, " <<Phy event descriptors: %d, spld_len: " + "%d, calc_ped: %d>>\n", num_ped, spld_len, + (spld_len - 52) / 12); + if (num_ped > 0) { + if (op->do_name) { + sgj_pr_hr(jsp, " phy_event_desc_num=%d\n", num_ped); + return; /* don't decode at this stage */ + } else + sgj_pr_hr(jsp, " Phy event descriptors:\n"); + } + if (jsp->pr_as_json) { + sgj_js_nv_i(jsp, jo2p, "number_of_phy_event_descriptors", + num_ped); + if (num_ped > 0) + ja2p = sgj_named_subarray_r(jsp, jo2p, + "phy_event_descriptor_list"); + } + xcp = vcp + 52; + for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) { + int pes = xcp[3]; + unsigned int pvdt_v; + + if (jsp->pr_as_json) + jo3p = sgj_new_unattached_object_r(jsp); + ui = sg_get_unaligned_be32(xcp + 4); + pvdt_v = sg_get_unaligned_be32(xcp + 8); + ccp = show_sas_phy_event_info(pes, ui, pvdt_v, b, blen); + if (0 == strlen(ccp)) { + sgj_pr_hr(jsp, " %s\n", b); /* unknown pvdt_v */ + if (jsp->pr_as_json) { + int n; + + snprintf(s, sz, "%s_pes_0x%x", unknown_s, pes); + sgj_js_nv_ihex(jsp, jo3p, s, ui); + n = strlen(s); + sg_scnpr(s + n, sz - n, "_%s", "threshold"); + sgj_js_nv_ihex(jsp, jo3p, s, pvdt_v); + } + } else { + if (jsp->pr_as_json) { + sgj_convert_to_snake_name(ccp, s, sz); + sgj_js_nv_ihex(jsp, jo3p, s, ui); + if (0x2b == pes) + sgj_js_nv_ihex(jsp, jo3p, pvdt, pvdt_v); + else if (0x2c == pes) + sgj_js_nv_ihex(jsp, jo3p, pvdt, pvdt_v); + else if (0x2d == pes) + sgj_js_nv_ihex(jsp, jo3p, pvdt, pvdt_v); + else if (0x2e == pes) + sgj_js_nv_ihex(jsp, jo3p, pvdt, pvdt_v); + } else { + cp = strchr(b, '\t'); + if (cp) { + *cp = '\0'; + sgj_pr_hr(jsp, " %s\n", b); + sgj_pr_hr(jsp, " %s\n", cp + 1); + } else + sgj_pr_hr(jsp, " %s\n", b); + } + } + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); + } + } else if (op->verbose) + printf(" <<No phy event descriptors>>\n"); +skip: + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + } /* end of for loop over phys with this relative port */ +} + +/* PROTO_SPECIFIC_LPAGE [0x18] <psp> */ +static bool +show_protocol_specific_port_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int k, num, pl, pid; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char b[128]; + static const char * psplp = "Protocol specific port log page"; + static const char * fss = "for SAS SSP"; + + num = len - 4; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (op->do_name) + sgj_pr_hr(jsp, "log_page=0x%x\n", PROTO_SPECIFIC_LPAGE); + else + sgj_pr_hr(jsp, "%s [0x18]\n", psplp); + } + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, psplp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "protocol_specific_port_log_parameter_list"); + } + + for (k = 0, bp = resp + 4; k < num; ) { + int pc = sg_get_unaligned_be16(bp + 0); + + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto skip; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto skip; + } + pid = 0xf & bp[4]; + if (6 != pid) { + pr2serr("Protocol identifier: %d, only support SAS (SPL) which " + "is 6\n", pid); + return false; /* only decode SAS log page */ + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + if ((0 == k) && (! op->do_name)) + sgj_pr_hr(jsp, "%s %s [0x18]\n", psplp, fss); + /* call helper */ + show_sas_port_param(bp, pl, op, jo3p); + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if ((op->do_pcb) && (! op->do_name)) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], b, + sizeof(b))); + if (op->filter_given) + break; +skip: + k += pl; + bp += pl; + } + return true; +} + +/* Returns true if processed page, false otherwise */ +/* STATS_LPAGE [0x19], subpages: 0x0 to 0x1f <gsp,grsp> introduced: SPC-4 */ +static bool +show_stats_perform_pages(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool nam, spf; + int k, num, param_len, param_code, subpg_code, extra; + uint64_t ull; + const uint8_t * bp; + const char * ccp; + const char * pg_name; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + static const char * gsaplp = + "General statistics and performance log page"; + static const char * gr_saplp = + "Group statistics and performance log page"; + +// yyyyyyyyyyyyyyyyyy + nam = op->do_name; + num = len - 4; + bp = resp + 4; + spf = !!(resp[0] & 0x40); + subpg_code = spf ? resp[1] : NOT_SPG_SUBPG; + if (0 == subpg_code) + pg_name = gsaplp; + else if (subpg_code < 0x20) + pg_name = gr_saplp; + else + pg_name = "Unknown subpage"; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (nam) { + sgj_pr_hr(jsp, "log_page=0x%x\n", STATS_LPAGE); + if (subpg_code > 0) + sgj_pr_hr(jsp, "log_subpage=0x%x\n", subpg_code); + } else { + if (0 == subpg_code) + sgj_pr_hr(jsp, "%s [0x19]\n", gsaplp); + else if (subpg_code < 0x20) + sgj_pr_hr(jsp, "%s (%d) [0x19,0x%x]\n", gr_saplp, subpg_code, + subpg_code); + else + sgj_pr_hr(jsp, "%s: %d [0x19,0x%x]\n", pg_name, subpg_code, + subpg_code); + } + } + if (jsp->pr_as_json) + jo2p = sg_log_js_hdr(jsp, jop, pg_name, resp); + if (subpg_code > 31) + return false; + if (jsp->pr_as_json) + jap = sgj_named_subarray_r(jsp, jo2p, 0 == subpg_code ? + "general_statistics_and_performance_log_parameters" : + "group_statistics_and_performance_log_parameters"); + if (0 == subpg_code) { /* General statistics and performance log page */ + if (num < 0x5c) + return false; + for (k = num; k > 0; k -= extra, bp += extra) { + unsigned int ui; + + if (k < 3) + return false; + param_len = bp[3]; + extra = param_len + 4; + param_code = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (param_code != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + goto skip; + } else if (op->do_hex) { + hex2stdout(bp, extra, op->dstrhex_no_ascii); + goto skip; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + switch (param_code) { + case 1: /* Statistics and performance log parameter */ + ccp = nam ? "parameter_code=1" : "Statistics and performance " + "log parameter"; + sgj_pr_hr(jsp, "%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "read_commands=" : "number of read commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 12); + ccp = nam ? "write_commands=" : "number of write commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 20); + ccp = nam ? "lb_received=" + : "number of logical blocks received = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 28); + ccp = nam ? "lb_transmitted=" + : "number of logical blocks transmitted = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 36); + ccp = nam ? "read_proc_intervals=" + : "read command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 44); + ccp = nam ? "write_proc_intervals=" + : "write command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 52); + ccp = nam ? "weight_rw_commands=" : "weighted number of " + "read commands plus write commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 60); + ccp = nam ? "weight_rw_processing=" : "weighted read command " + "processing plus write command processing = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + break; + case 2: /* Idle time log parameter */ + ccp = nam ? "parameter_code=2" : "Idle time log parameter"; + sgj_pr_hr(jsp, "%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "idle_time_intervals=" : "idle time " + "intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + break; + case 3: /* Time interval log parameter for general stats */ + ccp = nam ? "parameter_code=3" : "Time interval log " + "parameter for general stats"; + sgj_pr_hr(jsp, "%s\n", ccp); + ui = sg_get_unaligned_be32(bp + 4); + ccp = nam ? "time_interval_neg_exp=" : "time interval " + "negative exponent = "; + sgj_pr_hr(jsp, " %s%u\n", ccp, ui); + ui = sg_get_unaligned_be32(bp + 8); + ccp = nam ? "time_interval_int=" : "time interval " + "integer = "; + sgj_pr_hr(jsp, " %s%u\n", ccp, ui); + break; + case 4: /* FUA statistics and performance log parameter */ + ccp = nam ? "parameter_code=4" : "Force unit access " + "statistics and performance log parameter "; + sgj_pr_hr(jsp, "%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "read_fua_commands=" : "number of read FUA " + "commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 12); + ccp = nam ? "write_fua_commands=" : "number of write FUA " + "commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 20); + ccp = nam ? "read_fua_nv_commands=" + : "number of read FUA_NV commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 28); + ccp = nam ? "write_fua_nv_commands=" + : "number of write FUA_NV commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 36); + ccp = nam ? "read_fua_proc_intervals=" + : "read FUA command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 44); + ccp = nam ? "write_fua_proc_intervals=" + : "write FUA command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 52); + ccp = nam ? "read_fua_nv_proc_intervals=" + : "read FUA_NV command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 60); + ccp = nam ? "write_fua_nv_proc_intervals=" + : "write FUA_NV command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + break; + case 6: /* Time interval log parameter for cache stats */ + ccp = nam ? "parameter_code=6" : "Time interval log " + "parameter for cache stats"; + sgj_pr_hr(jsp, "%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "time_interval_neg_exp=" : "time interval " + "negative exponent = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 8); + ccp = nam ? "time_interval_int=" : "time interval " + "integer = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + break; + default: + if (nam) { + sgj_pr_hr(jsp, "parameter_code=%d\n", param_code); + sgj_pr_hr(jsp, " unknown=1\n"); + } else + pr2serr("show_performance... unknown %s %d\n", param_c, + param_code); + if (op->verbose) + hex2stderr(bp, extra, 1); + break; + } + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if ((op->do_pcb) && (! op->do_name)) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +skip: + if (op->filter_given) + break; + } + } else { /* Group statistics and performance (n) log page */ + if (num < 0x34) + return false; + for (k = num; k > 0; k -= extra, bp += extra) { + if (k < 3) + return false; + param_len = bp[3]; + extra = param_len + 4; + param_code = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (param_code != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + goto skip2; + } else if (op->do_hex) { + hex2stdout(bp, extra, op->dstrhex_no_ascii); + goto skip2; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + switch (param_code) { + case 1: /* Group n Statistics and performance log parameter */ + if (nam) + sgj_pr_hr(jsp, "parameter_code=1\n"); + else + sgj_pr_hr(jsp, "Group %d Statistics and performance log " + "parameter\n", subpg_code); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "gn_read_commands=" : "group n number of read " + "commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 12); + ccp = nam ? "gn_write_commands=" : "group n number of write " + "commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 20); + ccp = nam ? "gn_lb_received=" + : "group n number of logical blocks received = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 28); + ccp = nam ? "gn_lb_transmitted=" + : "group n number of logical blocks transmitted = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 36); + ccp = nam ? "gn_read_proc_intervals=" + : "group n read command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 44); + ccp = nam ? "gn_write_proc_intervals=" + : "group n write command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + break; + case 4: /* Group n FUA statistics and performance log parameter */ + ccp = nam ? "parameter_code=4" : "Group n force unit access " + "statistics and performance log parameter"; + sgj_pr_hr(jsp, "%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "gn_read_fua_commands=" + : "group n number of read FUA commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 12); + ccp = nam ? "gn_write_fua_commands=" + : "group n number of write FUA commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 20); + ccp = nam ? "gn_read_fua_nv_commands=" + : "group n number of read FUA_NV commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 28); + ccp = nam ? "gn_write_fua_nv_commands=" + : "group n number of write FUA_NV commands = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 36); + ccp = nam ? "gn_read_fua_proc_intervals=" + : "group n read FUA command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 44); + ccp = nam ? "gn_write_fua_proc_intervals=" : "group n write " + "FUA command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 52); + ccp = nam ? "gn_read_fua_nv_proc_intervals=" : "group n " + "read FUA_NV command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + ull = sg_get_unaligned_be64(bp + 60); + ccp = nam ? "gn_write_fua_nv_proc_intervals=" : "group n " + "write FUA_NV command processing intervals = "; + sgj_pr_hr(jsp, " %s%" PRIu64 "\n", ccp, ull); + break; + default: + if (nam) { + sgj_pr_hr(jsp, "parameter_code=%d\n", param_code); + sgj_pr_hr(jsp, " unknown=1\n"); + } else + pr2serr("show_performance... unknown %s %d\n", param_c, + param_code); + if (op->verbose) + hex2stderr(bp, extra, 1); + break; + } + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if ((op->do_pcb) && (! op->do_name)) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +skip2: + if (op->filter_given) + break; + } + } + return true; +} + +/* Returns true if processed page, false otherwise */ +/* STATS_LPAGE [0x19], CACHE_STATS_SUBPG [0x20] <cms> introduced: SPC-4 */ +static bool +show_cache_stats_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int k, num, subpg_code, extra; + bool nam, spf; + unsigned int ui; + const uint8_t * bp; + const char * ccp; + uint64_t ull; + char str[PCB_STR_LEN]; + +if (jop) { }; + nam = op->do_name; + num = len - 4; + bp = resp + 4; + if (num < 4) { + pr2serr("badly formed Cache memory statistics page\n"); + return false; + } + spf = !!(resp[0] & 0x40); + subpg_code = spf ? resp[1] : NOT_SPG_SUBPG; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (nam) { + printf("log_page=0x%x\n", STATS_LPAGE); + if (subpg_code > 0) + printf("log_subpage=0x%x\n", subpg_code); + } else + printf("Cache memory statistics page [0x19,0x20]\n"); + } + + for (k = num; k > 0; k -= extra, bp += extra) { + int pc; + + if (k < 3) { + pr2serr("short Cache memory statistics page\n"); + return false; + } + if (8 != bp[3]) { + printf("Cache memory statistics page parameter length not " + "8\n"); + return false; + } + extra = bp[3] + 4; + pc = sg_get_unaligned_be16(bp + 0); + if (op->filter_given) { + if (pc != op->filter) + continue; + } + if (op->do_raw) { + dStrRaw(bp, extra); + goto skip; + } else if (op->do_hex) { + hex2stdout(bp, extra, op->dstrhex_no_ascii); + goto skip; + } + switch (pc) { + case 1: /* Read cache memory hits log parameter */ + ccp = nam ? "parameter_code=1" : + "Read cache memory hits log parameter"; + printf("%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "read_cache_memory_hits=" : + "read cache memory hits = "; + printf(" %s%" PRIu64 "\n", ccp, ull); + break; + case 2: /* Reads to cache memory log parameter */ + ccp = nam ? "parameter_code=2" : + "Reads to cache memory log parameter"; + printf("%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "reads_to_cache_memory=" : + "reads to cache memory = "; + printf(" %s%" PRIu64 "\n", ccp, ull); + break; + case 3: /* Write cache memory hits log parameter */ + ccp = nam ? "parameter_code=3" : + "Write cache memory hits log parameter"; + printf("%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "write_cache_memory_hits=" : + "write cache memory hits = "; + printf(" %s%" PRIu64 "\n", ccp, ull); + break; + case 4: /* Writes from cache memory log parameter */ + ccp = nam ? "parameter_code=4" : + "Writes from cache memory log parameter"; + printf("%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "writes_from_cache_memory=" : + "writes from cache memory = "; + printf(" %s%" PRIu64 "\n", ccp, ull); + break; + case 5: /* Time from last hard reset log parameter */ + ccp = nam ? "parameter_code=5" : + "Time from last hard reset log parameter"; + printf("%s\n", ccp); + ull = sg_get_unaligned_be64(bp + 4); + ccp = nam ? "time_from_last_hard_reset=" : + "time from last hard reset = "; + printf(" %s%" PRIu64 "\n", ccp, ull); + break; + case 6: /* Time interval log parameter for cache stats */ + ccp = nam ? "parameter_code=6" : + "Time interval log parameter"; + printf("%s\n", ccp); + ui = sg_get_unaligned_be32(bp + 4); + ccp = nam ? "time_interval_neg_exp=" : "time interval " + "negative exponent = "; + printf(" %s%u\n", ccp, ui); + ui = sg_get_unaligned_be32(bp + 8); + ccp = nam ? "time_interval_int=" : "time interval " + "integer = "; + printf(" %s%u\n", ccp, ui); + break; + default: + if (nam) { + printf("parameter_code=%d\n", pc); + printf(" unknown=1\n"); + } else + pr2serr("show_performance... unknown %s %d\n", param_c, + pc); + if (op->verbose) + hex2stderr(bp, extra, 1); + break; + } + if ((op->do_pcb) && (! op->do_name)) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +skip: + if (op->filter_given) + break; + } + return true; +} + +/* FORMAT_STATUS_LPAGE [0x8] <fs> introduced: SBC-2 */ +static bool +show_format_status_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool is_count, is_not_avail; + int k, num, pl, pc; + uint64_t ull; + const char * cp = ""; + const uint8_t * bp; + const uint8_t * xp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[512]; + static const char * fslp = "Format status log page"; + static const char * fso = "Format status out"; + static const char * fso_sn = "format_status_out"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x8]\n", fslp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, fslp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, "format_status_log_parameters"); + } + + + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + is_count = true; + + switch (pc) { + case 0: + is_not_avail = false; + if (pl < 5) + sgj_pr_hr(jsp, " %s: <empty>\n", fso); + else { + if (sg_all_ffs(bp + 4, pl - 4)) { + sgj_pr_hr(jsp, " %s: <%s>\n", fso, not_avail); + is_not_avail = true; + } else { + hex2str(bp + 4, pl - 4, " ", op->hex2str_oformat, + sizeof(b), b); + sgj_pr_hr(jsp, " %s:\n%s", fso, b); + + + } + } + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, fso); + if (is_not_avail) + sgj_js_nv_ihexstr(jsp, jo3p, fso_sn, 0, NULL, not_avail); + else + sgj_js_nv_hex_bytes(jsp, jo3p, fso_sn, bp + 4, pl - 4); + } + is_count = false; + break; + case 1: + cp = "Grown defects during certification"; + break; + case 2: + cp = "Total blocks reassigned during format"; + break; + case 3: + cp = "Total new blocks reassigned"; + break; + case 4: + cp = "Power on minutes since format"; + break; + default: + sgj_pr_hr(jsp, " Unknown Format %s = 0x%x\n", param_c, pc); + is_count = false; + hex2fp(bp, pl, " ", op->hex2str_oformat, stdout); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, unknown_s); + sgj_js_nv_hex_bytes(jsp, jo3p, in_hex, bp, pl); + } + break; + } + if (is_count) { + k = pl - 4; + xp = bp + 4; + is_not_avail = false; + ull = 0; + if (sg_all_ffs(xp, k)) { + sgj_pr_hr(jsp, " %s: <%s>\n", cp, not_avail); + is_not_avail = true; + } else { + if (k > (int)sizeof(ull)) { + xp += (k - sizeof(ull)); + k = sizeof(ull); + } + ull = sg_get_unaligned_be(k, xp); + sgj_pr_hr(jsp, " %s = %" PRIu64 "\n", cp, ull); + } + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, cp); + sgj_convert_to_snake_name(cp, b, sizeof(b)); + if (is_not_avail) + sgj_js_nv_ihexstr(jsp, jo3p, b, 0, NULL, not_avail); + else + sgj_js_nv_ihex(jsp, jo3p, b, ull); + } + } + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if ((op->do_pcb) && (! op->do_name)) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Non-volatile cache page [0x17] <nvc> introduced: SBC-2 + * Standard vacillates between "non-volatile" and "nonvolatile" */ +static bool +show_non_volatile_cache_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int j, num, pl, pc; + const char * cp; + const char * c2p; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[96]; + static const char * nvclp = "Non-volatile cache log page"; + static const char * ziinv = "0 (i.e. it is now volatile)"; + static const char * indef = "indefinite"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x17]\n", nvclp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, nvclp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "nonvolatile_cache_log_parameters"); + } + + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + cp = NULL; + switch (pc) { + case 0: + cp = "Remaining nonvolatile time"; + c2p = NULL; + j = sg_get_unaligned_be24(bp + 5); + switch (j) { + case 0: + c2p = ziinv; + sgj_pr_hr(jsp, " %s: %s\n", cp, c2p); + break; + case 1: + c2p = unknown_s; + sgj_pr_hr(jsp, " %s: <%s>\n", cp, c2p); + break; + case 0xffffff: + c2p = indef; + sgj_pr_hr(jsp, " %s: <%s>\n", cp, c2p); + break; + default: + snprintf(b, sizeof(b), "%d minutes [%d:%d]", j, (j / 60), + (j % 60)); + c2p = b; + sgj_pr_hr(jsp, " %s: %s\n", cp, c2p); + break; + } + break; + case 1: + cp = "Maximum non-volatile time"; + c2p = NULL; + j = sg_get_unaligned_be24(bp + 5); + switch (j) { + case 0: + c2p = ziinv; + sgj_pr_hr(jsp, " %s: %s\n", cp, c2p); + break; + case 1: + c2p = rsv_s; + sgj_pr_hr(jsp, " %s: <%s>\n", cp, c2p); + break; + case 0xffffff: + c2p = indef; + sgj_pr_hr(jsp, " %s: <%s>\n", cp, c2p); + break; + default: + snprintf(b, sizeof(b), "%d minutes [%d:%d]", j, (j / 60), + (j % 60)); + c2p = b; + sgj_pr_hr(jsp, " %s: %s\n", cp, c2p); + break; + } + break; + default: + sgj_pr_hr(jsp, " Unknown %s = 0x%x\n", param_c, pc); + hex2stdout(bp, pl, op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + cp ? cp : unknown_s); + if (cp) + js_snakenv_ihexstr_nex(jsp, jo3p, cp , j, true, + NULL, c2p, NULL); + else if (pl > 4) + sgj_js_nv_hex_bytes(jsp, jo3p, in_hex, bp + 4, pl - 4); + + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + } + if ((op->do_pcb) && (! op->do_name)) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* LB_PROV_LPAGE [0xc] <lbp> introduced: SBC-3 */ +static bool +show_lb_provisioning_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool evsm_output = false; + int num, pl, pc; + const uint8_t * bp; + const char * cp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Logical block provisioning page [0xc]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x1: + cp = " Available LBA mapping threshold"; + break; + case 0x2: + cp = " Used LBA mapping threshold"; + break; + case 0x3: + cp = " Available provisioning resource percentage"; + break; + case 0x100: + cp = " De-duplicated LBA"; + break; + case 0x101: + cp = " Compressed LBA"; + break; + case 0x102: + cp = " Total efficiency LBA"; + break; + default: + cp = NULL; + break; + } + if (cp) { + if ((pl < 8) || (num < 8)) { + if (num < 8) + pr2serr("\n truncated by response length, expected at " + "least 8 bytes\n"); + else + pr2serr("\n parameter length >= 8 expected, got %d\n", + pl); + break; + } + if (0x3 == pc) /* resource percentage log parameter */ + printf(" %s: %u %%\n", cp, sg_get_unaligned_be16(bp + 4)); + else /* resource count log parameters */ + printf(" %s resource count: %u\n", cp, + sg_get_unaligned_be32(bp + 4)); + if (pl > 8) { + switch (bp[8] & 0x3) { /* SCOPE field */ + case 0: cp = not_rep; break; + case 1: cp = "dedicated to lu"; break; + case 2: cp = "not dedicated to lu"; break; + case 3: cp = rsv_s; break; + } + printf(" Scope: %s\n", cp); + } + } else if ((pc >= 0xfff0) && (pc <= 0xffff)) { + if (op->exclude_vendor) { + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + printf(" %s parameter(s) being ignored\n", vend_spec); + } + } else { + printf(" %s [0x%x]:", vend_spec, pc); + hex2stdout(bp, ((pl < num) ? pl : num), op->dstrhex_no_ascii); + } + } else { + printf(" Reserved [%s=0x%x]:\n", param_c_sn, pc); + hex2stdout(bp, ((pl < num) ? pl : num), op->dstrhex_no_ascii); + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* UTILIZATION_SUBPG [0xe,0x1] <util> introduced: SBC-4 */ +static bool +show_utilization_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int num, pl, pc, k; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Utilization page [0xe,0x1]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x0: + printf(" Workload utilization:"); + if ((pl < 6) || (num < 6)) { + if (num < 6) + pr2serr("\n truncated by response length, expected " + "at least 6 bytes\n"); + else + pr2serr("\n parameter length >= 6 expected, got %d\n", + pl); + break; + } + k = sg_get_unaligned_be16(bp + 4); + printf(" %d.%02d %%\n", k / 100, k % 100); + break; + case 0x1: + printf(" Utilization usage rate based on date and time:"); + if ((pl < 6) || (num < 6)) { + if (num < 6) + pr2serr("\n truncated by response length, expected " + "at least 6 bytes\n"); + else + pr2serr("\n parameter length >= 6 expected, got %d\n", + pl); + break; + } + printf(" %d %%\n", bp[4]); + break; + default: + printf(" Reserved [parameter_code=0x%x]:\n", pc); + hex2stdout(bp, ((pl < num) ? pl : num), op->dstrhex_no_ascii); + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* SOLID_STATE_MEDIA_LPAGE [0x11] <ssm> introduced: SBC-3 */ +static bool +show_solid_state_media_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + const uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + static const char * ssmlp = "Solid state media log page"; + static const char * puei = "Percentage used endurance indicator"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x11]\n", ssmlp); + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, ssmlp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, + "solid_state_media_log_parameters"); + } + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + switch (pc) { + case 0x1: + if ((pl < 8) || (num < 8)) { + if (num < 8) + pr2serr("\n truncated by response length, expected " + "at least 8 bytes\n"); + else + pr2serr("\n parameter length >= 8 expected, got %d\n", + pl); + break; + } + sgj_pr_hr(jsp, " %s: %u %%\n", puei, bp[7]); + if (jsp->pr_as_json) { + js_snakenv_ihexstr_nex(jsp, jo3p, param_c, pc, true, + NULL, puei, NULL); + js_snakenv_ihexstr_nex(jsp, jo3p, puei, bp[7], false, + NULL, NULL, NULL); + } + break; + default: + printf(" Reserved [parameter_code=0x%x]:\n", pc); + hex2stdout(bp, ((pl < num) ? pl : num), op->dstrhex_no_ascii); + break; + } + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +static const char * dt_dev_activity[] = { + "No DT device activity", + "Cleaning operation in progress", + "Volume is being loaded", + "Volume is being unloaded", + "Other medium activity", + "Reading from medium", + "Writing to medium", + "Locating medium", + "Rewinding medium", /* 8 */ + "Erasing volume", + "Formatting volume", + "Calibrating", + "Other DT device activity", + "Microcode update in progress", + "Reading encrypted from medium", + "Writing encrypted to medium", + "Diagnostic operation in progress", /* 10 */ +}; + +/* DT device status [0x11] <dtds> (ssc, adc) */ +static bool +show_dt_device_status_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool evsm_output = false; + int num, pl, pc, j; + const uint8_t * bp; + char str[PCB_STR_LEN]; + char b[512]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("DT device status page (ssc-3, adc-3) [0x11]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x0: + printf(" Very high frequency data:\n"); + if ((pl < 8) || (num < 8)) { + if (num < 8) + pr2serr(" truncated by response length, expected at " + "least 8 bytes\n"); + else + pr2serr(" parameter length >= 8 expected, got %d\n", + pl); + break; + } + printf(" PAMR=%d HUI=%d MACC=%d CMPR=%d ", !!(0x80 & bp[4]), + !!(0x40 & bp[4]), !!(0x20 & bp[4]), !!(0x10 & bp[4])); + printf("WRTP=%d CRQST=%d CRQRD=%d DINIT=%d\n", !!(0x8 & bp[4]), + !!(0x4 & bp[4]), !!(0x2 & bp[4]), !!(0x1 & bp[4])); + printf(" INXTN=%d RAA=%d MPRSNT=%d ", !!(0x80 & bp[5]), + !!(0x20 & bp[5]), !!(0x10 & bp[5])); + printf("MSTD=%d MTHRD=%d MOUNTED=%d\n", + !!(0x4 & bp[5]), !!(0x2 & bp[5]), !!(0x1 & bp[5])); + printf(" DT device activity: "); + j = bp[6]; + if (j < (int)SG_ARRAY_SIZE(dt_dev_activity)) + printf("%s\n", dt_dev_activity[j]); + else if (j < 0x80) + printf("Reserved [0x%x]\n", j); + else + printf("%s [0x%x]\n", vend_spec, j); + printf(" VS=%d TDDEC=%d EPP=%d ", !!(0x80 & bp[7]), + !!(0x20 & bp[7]), !!(0x10 & bp[7])); + printf("ESR=%d RRQST=%d INTFC=%d TAFC=%d\n", !!(0x8 & bp[7]), + !!(0x4 & bp[7]), !!(0x2 & bp[7]), !!(0x1 & bp[7])); + break; + case 0x1: + printf(" Very high frequency polling delay: "); + if ((pl < 6) || (num < 6)) { + if (num < 6) + pr2serr("\n truncated by response length, expected at " + "least 6 bytes\n"); + else + pr2serr("\n parameter length >= 6 expected, got %d\n", + pl); + break; + } + printf(" %d milliseconds\n", sg_get_unaligned_be16(bp + 4)); + break; + case 0x2: + printf(" DT device ADC data encryption control status (hex " + "only now):\n"); + if ((pl < 12) || (num < 12)) { + if (num < 12) + pr2serr(" truncated by response length, expected at " + "least 12 bytes\n"); + else + pr2serr(" parameter length >= 12 expected, got %d\n", + pl); + break; + } + hex2fp(bp + 4, 8, " ", op->hex2str_oformat, stdout); + break; + case 0x3: + printf(" Key management error data (hex only now):\n"); + if ((pl < 16) || (num < 16)) { + if (num < 16) + pr2serr(" truncated by response length, expected at " + "least 16 bytes\n"); + else + pr2serr(" parameter length >= 16 expected, got %d\n", + pl); + break; + } + hex2fp(bp + 4, 12, " ", op->hex2str_oformat, stdout); + break; + default: + if ((pc >= 0x101) && (pc <= 0x1ff)) { + printf(" Primary port %d status:\n", pc - 0x100); + if (12 == bp[3]) { /* if length of desc is 12, assume SAS */ + printf(" SAS: negotiated physical link rate: %s\n", + sas_negot_link_rate((0xf & (bp[4] >> 4)), b, + sizeof(b))); + printf(" signal=%d, pic=%d, ", !!(0x2 & bp[4]), + !!(0x1 & bp[4])); + printf("hashed SAS addr: 0x%u\n", + sg_get_unaligned_be24(bp + 5)); + printf(" SAS addr: 0x%" PRIx64 "\n", + sg_get_unaligned_be64(bp + 8)); + } else { + printf(" non-SAS transport, in hex:\n"); + hex2fp(bp + 4, ((pl < num) ? pl : num) - 4, " ", + op->hex2str_oformat, stdout); + } + } else if (pc >= 0x8000) { + if (op->exclude_vendor) { + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + printf(" %s parameter(s) being ignored\n", + vend_spec); + } + } else { + printf(" %s [%s=0x%x]:\n", vend_spec, param_c_sn, pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } + } else { + printf(" Reserved [%s=0x%x]:\n", param_c_sn, pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* TapeAlert response [0x12] <tar> (adc,ssc) */ +static bool +show_tapealert_response_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool evsm_output = false; + int num, pl, pc, k, mod, div; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("TapeAlert response page (ssc-3, adc-3) [0x12]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x0: + if (pl < 12) { + + } + for (k = 1; k < 0x41; ++k) { + mod = ((k - 1) % 8); + div = (k - 1) / 8; + if (0 == mod) { + if (div > 0) + printf("\n"); + printf(" Flag%02Xh: %d", k, !! (bp[4 + div] & 0x80)); + } else + printf(" %02Xh: %d", k, + !! (bp[4 + div] & (1 << (7 - mod)))); + } + printf("\n"); + break; + default: + if (pc <= 0x8000) { + printf(" Reserved [parameter_code=0x%x]:\n", pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } else { + if (op->exclude_vendor) { + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + printf(" %s parameter(s) being ignored\n", + vend_spec); + } + } else { + printf(" %s [%s=0x%x]:\n", vend_spec, param_c_sn, pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } + } + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +#define NUM_REQ_REC_ARR_ELEMS 16 +static const char * req_rec_arr[NUM_REQ_REC_ARR_ELEMS] = { + "Recovery not requested", + "Recovery requested, no recovery procedure defined", + "Instruct operator to push volume", + "Instruct operator to remove and re-insert volume", + "Issue UNLOAD command. Instruct operator to remove and re-insert volume", + "Instruct operator to power cycle target device", + "Issue LOAD command", + "Issue UNLOAD command", + "Issue LOGICAL UNIT RESET task management function", /* 0x8 */ + "No recovery procedure defined. Contact service organization", + "Issue UNLOAD command. Instruct operator to remove and quarantine " + "volume", + "Instruct operator to not insert a volume. Contact service organization", + "Issue UNLOAD command. Instruct operator to remove volume. Contact " + "service organization", + "Request creation of target device error log", + "Retrieve a target device error log", + "Modify configuration to all microcode update and instruct operator to " + "re-insert volume", /* 0xf */ +}; + +/* REQ_RECOVERY_LPAGE Requested recovery [0x13] <rr> (ssc) */ +static bool +show_requested_recovery_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool evsm_output = false; + int num, pl, pc, j, k; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Requested recovery page (ssc-3) [0x13]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x0: + printf(" Recovery procedures:\n"); + for (k = 4; k < pl; ++ k) { + j = bp[k]; + if (j < NUM_REQ_REC_ARR_ELEMS) + printf(" %s\n", req_rec_arr[j]); + else if (j < 0x80) + printf(" Reserved [0x%x]\n", j); + else + printf(" Vendor specific [0x%x]\n", j); + } + break; + default: + if (pc <= 0x8000) { + printf(" Reserved [parameter_code=0x%x]:\n", pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } else { + if (op->exclude_vendor) { + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + printf(" Vendor specific parameter(s) being " + "ignored\n"); + } + } else { + printf(" Vendor specific [parameter_code=0x%x]:\n", pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } + } + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* SAT_ATA_RESULTS_LPAGE (SAT-2) [0x16] <aptr> */ +static bool +show_ata_pt_results_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + const uint8_t * bp; + const uint8_t * dp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("ATA pass-through results page (sat-2) [0x16]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + if ((pc < 0xf) && (pl > 17)) { + int extend, count; + + printf(" Log_index=0x%x (parameter_code=0x%x)\n", pc + 1, pc); + dp = bp + 4; /* dp is start of ATA Return descriptor + * which is 14 bytes long */ + extend = dp[2] & 1; + count = dp[5] + (extend ? (dp[4] << 8) : 0); + printf(" extend=%d error=0x%x count=0x%x\n", extend, + dp[3], count); + if (extend) + printf(" lba=0x%02x%02x%02x%02x%02x%02x\n", dp[10], dp[8], + dp[6], dp[11], dp[9], dp[7]); + else + printf(" lba=0x%02x%02x%02x\n", dp[11], dp[9], dp[7]); + printf(" device=0x%x status=0x%x\n", dp[12], dp[13]); + } else if (pl > 17) { + printf(" Reserved [parameter_code=0x%x]:\n", pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } else { + printf(" short parameter length: %d [parameter_code=0x%x]:\n", + pl, pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +static const char * bms_status[] = { + "no background scans active", + "background medium scan is active", + "background pre-scan is active", + "background scan halted due to fatal error", + "background scan halted due to a vendor specific pattern of error", + "background scan halted due to medium formatted without P-List", + "background scan halted - vendor specific cause", + "background scan halted due to temperature out of range", + ("background scan enabled, none active (waiting for BMS interval timer " + "to expire)"), /* clang warns about this, add parens to quieten */ + "background scan halted - scan results list full", + "background scan halted - pre-scan time limit timer expired" /* 10 */, +}; + +static const char * reassign_status[] = { + "Reserved [0x0]", + "Reassignment pending receipt of Reassign or Write command", + "Logical block successfully reassigned by device server", + "Reserved [0x3]", + "Reassignment by device server failed", + "Logical block recovered by device server via rewrite", + "Logical block reassigned by application client, has valid data", + "Logical block reassigned by application client, contains no valid data", + "Logical block unsuccessfully reassigned by application client", /* 8 */ +}; + +/* Background scan results [0x15,0] <bsr> for disk introduced: SBC-3 */ +static bool +show_background_scan_results_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool skip_out = false; + bool evsm_output = false; + bool ok; + int j, m, n, num, pl, pc; + const uint8_t * bp; + double d; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + sgj_opaque_p jo3p = NULL; + sgj_opaque_p jap = NULL; + char str[PCB_STR_LEN]; + char b[144]; + char e[80]; + static const int blen = sizeof(b); + static const int elen = sizeof(e); + static const char * bsrlp = "Background scan results log page"; + static const char * bss = "Background scan status"; + static const char * bms = "Background medium scan"; + static const char * bsr = "Background scan results"; + static const char * bs = "background scan"; + static const char * ms = "Medium scan"; + static const char * apom = "Accumulated power on minutes"; + static const char * rs = "Reassign status"; + + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + sgj_pr_hr(jsp, "%s [0x15]\n", bsrlp); + num = len - 4; + bp = &resp[0] + 4; + if (jsp->pr_as_json) { + jo2p = sg_log_js_hdr(jsp, jop, bsrlp, resp); + jap = sgj_named_subarray_r(jsp, jo2p, "background_scan_parameters"); + } + + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + if (jsp->pr_as_json) { + jo3p = sgj_new_unattached_object_r(jsp); + if (op->do_pcb) + js_pcb(jsp, jo3p, bp[2]); + } + + switch (pc) { + case 0: + sgj_pr_hr(jsp, " Status parameters:\n"); + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, bss); + if ((pl < 16) || (num < 16)) { + if (num < 16) + pr2serr(" truncated by response length, expected at " + "least 16 bytes\n"); + else + pr2serr(" parameter length >= 16 expected, got %d\n", + pl); + break; + } + sg_scnpr(b, blen, " %s: ", apom); + j = sg_get_unaligned_be32(bp + 4); + sgj_pr_hr(jsp, "%s%d [h:m %d:%d]\n", b, j, (j / 60), (j % 60)); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, apom, j, false, NULL, NULL, + NULL); + sg_scnpr(b, blen, " Status: "); + j = bp[9]; + ok = (j < (int)SG_ARRAY_SIZE(bms_status)); + if (ok) + sgj_pr_hr(jsp, "%s%s\n", b, bms_status[j]); + else + sgj_pr_hr(jsp, "%sunknown [0x%x] %s value\n", b, j, bss); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, bss, j, true, NULL, + ok ? bms_status[j] : unknown_s, NULL); + j = sg_get_unaligned_be16(bp + 10); + snprintf(b, blen, "Number of %ss performed", bs); + sgj_pr_hr(jsp, " %s: %d\n", b, j); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, b, j, true, NULL, NULL, + NULL); + j = sg_get_unaligned_be16(bp + 12); + snprintf(b, blen, "%s progress", bms); + d = (100.0 * j / 65536.0); +#ifdef SG_LIB_MINGW + snprintf(e, elen, "%g %%", d); +#else + snprintf(e, elen, "%.2f %%", d); +#endif + sgj_pr_hr(jsp, " %s: %s\n", b, e); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, b, j, true, NULL, e, + NULL); + j = sg_get_unaligned_be16(bp + 14); + snprintf(b, blen, "Number of %ss performed", bms); + + ok = (j > 0); + if (ok) + sgj_pr_hr(jsp, " %s: %d\n", b, j); + else + sgj_pr_hr(jsp, " %s: 0 [%s]\n", b, not_rep); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, b, j, true, NULL, + ok ? NULL : not_rep, NULL); + break; + default: + if (pc > 0x800) { + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, + (pc >= 0x8000) ? vend_spec : NULL); + if ((pc >= 0x8000) && (pc <= 0xafff)) { + if (op->exclude_vendor) { + skip_out = true; + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + sgj_pr_hr(jsp, " %s parameter(s) being " + "ignored\n", vend_spec); + } + } else + sgj_pr_hr(jsp, " %s parameter # %d [0x%x], %s\n", + ms, pc, pc, vend_spec); + } else + sgj_pr_hr(jsp, " %s parameter # %d [0x%x], %s\n", ms, pc, + pc, rsv_s); + if (skip_out) + skip_out = false; + else + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + break; + } else { + sgj_pr_hr(jsp, " %s parameter # %d [0x%x]\n", ms, pc, pc); + if (jsp->pr_as_json) + sgj_js_nv_ihexstr(jsp, jo3p, param_c_sn, pc, NULL, bsr); + } + if ((pl < 24) || (num < 24)) { + if (num < 24) + pr2serr(" truncated by response length, expected at " + "least 24 bytes\n"); + else + pr2serr(" parameter length >= 24 expected, got %d\n", + pl); + break; + } + j = sg_get_unaligned_be32(bp + 4); + n = (j % 60); + sgj_pr_hr(jsp, " %s when error detected: %d [%d:%d]\n", apom, + j, (j / 60), n); + if (jsp->pr_as_json) { + snprintf(b, blen, "%d hours, %d minute%s", (j / 60), n, + n != 1 ? "s" : ""); + js_snakenv_ihexstr_nex(jsp, jo3p, apom, j, true, NULL, b, + "when error detected [unit: minute]"); + } + j = (bp[8] >> 4) & 0xf; + ok = (j < (int)SG_ARRAY_SIZE(reassign_status)); + if (ok) + sgj_pr_hr(jsp, " %s: %s\n", rs, reassign_status[j]); + else + sgj_pr_hr(jsp, " %s: %s [0x%x]\n", rs, rsv_s, j); + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, rs, j, true, NULL, + ok ? reassign_status[j] : NULL, NULL); + n = 0xf & b[8]; + sgj_pr_hr(jsp, " %s: %s [sk,asc,ascq: 0x%x,0x%x,0x%x]\n", + s_key, sg_get_sense_key_str(n, blen, b), n, bp[9], + bp[10]); + if (bp[9] || bp[10]) + sgj_pr_hr(jsp, " %s\n", + sg_get_asc_ascq_str(bp[9], bp[10], blen, b)); + if (jsp->pr_as_json) { + sgj_js_nv_ihexstr(jsp, jo3p, "sense_key", n, NULL, + sg_get_sense_key_str(n, blen, b)); + sgj_js_nv_ihexstr(jsp, jo3p, "additional_sense_code", bp[9], + NULL, NULL); + sgj_js_nv_ihexstr(jsp, jo3p, "additional_sense_code_qualifier", + bp[10], NULL, sg_get_asc_ascq_str(bp[9], + bp[10], blen, b)); + } + if (op->verbose) { + n = sg_scnpr(b, blen, " vendor bytes [11 -> 15]: "); + for (m = 0; m < 5; ++m) + n += sg_scnpr(b + n, blen - n, "0x%02x ", bp[11 + m]); + sgj_pr_hr(jsp, "%s\n", b); + } + n = sg_scnpr(b, blen, " LBA (associated with medium error): " + "0x"); + if (sg_all_zeros(bp + 16, 8)) + sgj_pr_hr(jsp, "%s0\n", b); + else { + for (m = 0; m < 8; ++m) + n += sg_scnpr(b + n, blen - n, "%02x", bp[16 + m]); + sgj_pr_hr(jsp, "%s\n", b); + } + if (jsp->pr_as_json) + js_snakenv_ihexstr_nex(jsp, jo3p, "logical_block_address", + sg_get_unaligned_be64(bp + 16), true, + NULL, NULL, "of medium error"); + break; + } /* end of switch statement block */ + if (jsp->pr_as_json) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p); + if (op->do_pcb) + sgj_pr_hr(jsp, " <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* ZONED_BLOCK_DEV_STATS_SUBPG [0x14,0x1] <zbds> introduced: zbc2r01 */ +static bool +show_zoned_block_dev_stats(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool trunc, bad_pl; + int num, pl, pc; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Zoned block device statistics page [0x14,0x1]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + trunc = false; + bad_pl = false; + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (4 == pl) /* DC HC560 has empty descriptors */ + goto skip; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x0: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Maximum open zones: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x1: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Maximum explicitly open zones: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x2: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Maximum implicitly open zones: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x3: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Minimum empty zones: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x4: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Maximum non-sequential zones: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x5: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Zones emptied: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x6: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Suboptimal write commands: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x7: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Commands exceeding optimal limit: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x8: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Failed explicit opens: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0x9: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Read rule violations: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0xa: + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Write rule violations: %" PRIu32 "\n", + sg_get_unaligned_be32(bp + 8)); + break; + case 0xb: /* added zbc2r04 */ + if ((pl < 8) || (num < 8)) { + if (num < 8) + trunc = true; + else + bad_pl = true; + } else + printf(" Maximum implicitly open or before required zones: " + "%" PRIu32 "\n", sg_get_unaligned_be32(bp + 8)); + break; + default: + printf(" Reserved [parameter_code=0x%x]:\n", pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + break; + } + if (trunc) + pr2serr(" truncated by response length, expected at least " + "8 bytes\n"); + if (bad_pl) + pr2serr(" parameter length >= 8 expected, got %d\n", pl); + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* PENDING_DEFECTS_SUBPG [0x15,0x1] <pd> introduced: SBC-4 */ +static bool +show_pending_defects_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + uint32_t count; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Pending defects page [0x15,0x1]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x0: + printf(" Pending defect count: "); + if ((pl < 8) || (num < 8)) { + if (num < 8) + pr2serr("\n truncated by response length, expected " + "at least 8 bytes\n"); + else + pr2serr("\n parameter length >= 8 expected, got %d\n", + pl); + break; + } + count = sg_get_unaligned_be32(bp + 4); + if (0 == count) { + printf("0\n"); + break; + } + printf("%3u | LBA Accumulated power_on\n", count); + printf("-----------------------------|---------------"); + printf("-----------hours---------\n"); + break; + default: + printf(" Pending defect %4d: ", pc); + if ((pl < 16) || (num < 16)) { + if (num < 16) + pr2serr("\n truncated by response length, expected " + "at least 16 bytes\n"); + else + pr2serr("\n parameter length >= 16 expected, got %d\n", + pl); + break; + } + printf(" 0x%-16" PRIx64 " %5u\n", + sg_get_unaligned_be64(bp + 8), + sg_get_unaligned_be32(bp + 4)); + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* BACKGROUND_OP_SUBPG [0x15,0x2] <bop> introduced: SBC-4 rev 7 */ +static bool +show_background_op_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Background operation page [0x15,0x2]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x0: + printf(" Background operation:"); + if ((pl < 8) || (num < 8)) { + if (num < 8) + pr2serr("\n truncated by response length, expected " + "at least 8 bytes\n"); + else + pr2serr("\n parameter length >= 8 expected, got %d\n", + pl); + break; + } + printf(" BO_STATUS=%d\n", bp[4]); + break; + default: + printf(" Reserved [parameter_code=0x%x]:\n", pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* LPS misalignment page [0x15,0x3] <lps> introduced: SBC-4 rev 10 + LPS: "Long Physical Sector" a term from an ATA feature set */ +static bool +show_lps_misalignment_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("LPS misalignment page [0x15,0x3]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0x0: + printf(" LPS misalignment count: "); + if (4 == bp[3]) + printf("max lpsm: %" PRIu16 ", count=%" PRIu16 "\n", + sg_get_unaligned_be16(bp + 4), + sg_get_unaligned_be16(bp + 6)); + else + printf("<unexpected pc=0 parameter length=%d>\n", bp[4]); + break; + default: + if (pc <= 0xf000) { /* parameter codes 0x1 to 0xf000 */ + if (8 == bp[3]) + printf(" LBA of misaligned block: 0x%" PRIx64 "\n", + sg_get_unaligned_be64(bp + 4)); + else + printf("<unexpected pc=0x%x parameter length=%d>\n", + pc, bp[4]); + } else { + printf("<unexpected pc=0x%x>\n", pc); + hex2fp(bp, ((pl < num) ? pl : num), " ", + op->hex2str_oformat, stdout); + } + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Service buffer information [0x15] <sbi> (adc) */ +static bool +show_service_buffer_info_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool evsm_output = false; + int num, pl, pc; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Service buffer information page (adc-3) [0x15]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + if (pc < 0x100) { + printf(" Service buffer identifier: 0x%x\n", pc); + printf(" Buffer id: 0x%x, tu=%d, nmp=%d, nmm=%d, " + "offline=%d\n", bp[4], !!(0x10 & bp[5]), + !!(0x8 & bp[5]), !!(0x4 & bp[5]), !!(0x2 & bp[5])); + printf(" pd=%d, code_set: %s, Service buffer title:\n", + !!(0x1 & bp[5]), sg_get_desig_code_set_str(0xf & bp[6])); + printf(" %.*s\n", pl - 8, bp + 8); + } else if (pc < 0x8000) { + printf(" parameter_code=0x%x, Reserved, parameter in hex:\n", + pc); + hex2fp(bp + 4, pl - 4, " ", op->hex2str_oformat, stdout); + } else { + if (op->exclude_vendor) { + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + printf(" Vendor specific parameter(s) being " + "ignored\n"); + } + } else { + printf(" parameter_code=0x%x, Vendor-specific, parameter in " + "hex:\n", pc); + hex2fp(bp + 4, pl - 4, " ", op->hex2str_oformat, stdout); + } + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Sequential access device page [0xc] <sad> for tape */ +static bool +show_sequential_access_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool evsm_output = false; + int num, pl, pc; + const uint8_t * bp; + uint64_t ull, gbytes; + bool all_set; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Sequential access device page (ssc-3)\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + ull = sg_get_unaligned_be(pl - 4, bp + 4); + all_set = sg_all_ffs(bp + 4, pl - 4); + gbytes = ull / 1000000000; + switch (pc) { + case 0: + printf(" Data bytes received with WRITE commands: %" PRIu64 + " GB", gbytes); + if (op->verbose) + printf(" [%" PRIu64 " bytes]", ull); + printf("\n"); + break; + case 1: + printf(" Data bytes written to media by WRITE commands: %" PRIu64 + " GB", gbytes); + if (op->verbose) + printf(" [%" PRIu64 " bytes]", ull); + printf("\n"); + break; + case 2: + printf(" Data bytes read from media by READ commands: %" PRIu64 + " GB", gbytes); + if (op->verbose) + printf(" [%" PRIu64 " bytes]", ull); + printf("\n"); + break; + case 3: + printf(" Data bytes transferred by READ commands: %" PRIu64 + " GB", gbytes); + if (op->verbose) + printf(" [%" PRIu64 " bytes]", ull); + printf("\n"); + break; + case 4: + if (! all_set) + printf(" Native capacity from BOP to EOD: %" PRIu64 " MB\n", + ull); + break; + case 5: + if (! all_set) + printf(" Native capacity from BOP to EW of current " + "partition: %" PRIu64 " MB\n", ull); + break; + case 6: + if (! all_set) + printf(" Minimum native capacity from EW to EOP of current " + "partition: %" PRIu64 " MB\n", ull); + break; + case 7: + if (! all_set) + printf(" Native capacity from BOP to current position: %" + PRIu64 " MB\n", ull); + break; + case 8: + if (! all_set) + printf(" Maximum native capacity in device object buffer: %" + PRIu64 " MB\n", ull); + break; + case 0x100: + if (ull > 0) + printf(" Cleaning action required\n"); + else + printf(" Cleaning action not required (or completed)\n"); + if (op->verbose) + printf(" cleaning value: %" PRIu64 "\n", ull); + break; + default: + if (pc >= 0x8000) { + if (op->exclude_vendor) { + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + printf(" Vendor specific parameter(s) being " + "ignored\n"); + } + } else + printf(" Vendor specific parameter [0x%x] value: %" + PRIu64 "\n", pc, ull); + } else + printf(" Reserved parameter [0x%x] value: %" PRIu64 "\n", + pc, ull); + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Device statistics 0x14 <ds> for tape and ADC */ +static bool +show_device_stats_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool evsm_output = false; + int num, pl, pc; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Device statistics page (ssc-3 and adc)\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + if (pc < 0x1000) { + bool vl_num = true; + + switch (pc) { + case 0: + printf(" Lifetime media loads:"); + break; + case 1: + printf(" Lifetime cleaning operations:"); + break; + case 2: + printf(" Lifetime power on hours:"); + break; + case 3: + printf(" Lifetime media motion (head) hours:"); + break; + case 4: + printf(" Lifetime metres of tape processed:"); + break; + case 5: + printf(" Lifetime media motion (head) hours when " + "incompatible media last loaded:"); + break; + case 6: + printf(" Lifetime power on hours when last temperature " + "condition occurred:"); + break; + case 7: + printf(" Lifetime power on hours when last power " + "consumption condition occurred:"); + break; + case 8: + printf(" Media motion (head) hours since last successful " + "cleaning operation:"); + break; + case 9: + printf(" Media motion (head) hours since 2nd to last " + "successful cleaning:"); + break; + case 0xa: + printf(" Media motion (head) hours since 3rd to last " + "successful cleaning:"); + break; + case 0xb: + printf(" Lifetime power on hours when last operator " + "initiated forced reset\n and/or emergency " + "eject occurred:"); + break; + case 0xc: + printf(" Lifetime power cycles:"); + break; + case 0xd: + printf(" Volume loads since last parameter reset:"); + break; + case 0xe: + printf(" Hard write errors:"); + break; + case 0xf: + printf(" Hard read errors:"); + break; + case 0x10: + printf(" Duty cycle sample time (ms):"); + break; + case 0x11: + printf(" Read duty cycle:"); + break; + case 0x12: + printf(" Write duty cycle:"); + break; + case 0x13: + printf(" Activity duty cycle:"); + break; + case 0x14: + printf(" Volume not present duty cycle:"); + break; + case 0x15: + printf(" Ready duty cycle:"); + break; + case 0x16: + printf(" MBs transferred from app client in duty cycle " + "sample time:"); + break; + case 0x17: + printf(" MBs transferred to app client in duty cycle " + "sample time:"); + break; + case 0x40: + printf(" Drive manufacturer's serial number:"); + break; + case 0x41: + printf(" Drive serial number:"); + break; + case 0x42: /* added ssc5r02b */ + vl_num = false; + printf(" Manufacturing date (yyyymmdd): %.*s\n", pl - 4, + bp + 4); + break; + case 0x43: /* added ssc5r02b */ + vl_num = false; + printf(" Manufacturing date (yyyyww): %.*s\n", pl - 4, + bp + 4); + break; + case 0x80: + printf(" Medium removal prevented:"); + break; + case 0x81: + printf(" Maximum recommended mechanism temperature " + "exceeded:"); + break; + default: + vl_num = false; + printf(" Reserved %s [0x%x] data in hex:\n", param_c, pc); + hex2fp(bp + 4, pl - 4, " ", op->hex2str_oformat, stdout); + break; + } + if (vl_num) + printf(" %" PRIu64 "\n", sg_get_unaligned_be(pl - 4, bp + 4)); + } else { /* parameter_code >= 0x1000 */ + int k; + const uint8_t * p = bp + 4; + + switch (pc) { + case 0x1000: + printf(" Media motion (head) hours for each medium type:\n"); + for (k = 0; ((pl - 4) - k) >= 8; k += 8, p += 8) + printf(" [%d] Density code: %u, Medium type: 0x%x, " + "hours: %u\n", ((k / 8) + 1), p[2], p[3], + sg_get_unaligned_be32(p + 4)); + break; + default: + if (pc >= 0x8000) { + if (op->exclude_vendor) { + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + printf(" Vendor specific parameter(s) being " + "ignored\n"); + } + } else + printf(" Vendor specific parameter [0x%x], dump in " + "hex:\n", pc); + } else { + printf(" Reserved parameter [0x%x], dump in hex:\n", pc); + hex2fp(bp + 4, pl - 4, " ", op->hex2str_oformat, + stdout); + } + break; + } + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Media changer statistics 0x14 <mcs> for media changer */ +static bool +show_media_stats_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int num, pl, pc; + const uint8_t * bp; + uint64_t ull; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Media statistics page (smc-3)\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + ull = sg_get_unaligned_be(pl - 4, bp + 4); + switch (pc) { + case 0: + printf(" Number of moves: %" PRIu64 "\n", ull); + break; + case 1: + printf(" Number of picks: %" PRIu64 "\n", ull); + break; + case 2: + printf(" Number of pick retries: %" PRIu64 "\n", ull); + break; + case 3: + printf(" Number of places: %" PRIu64 "\n", ull); + break; + case 4: + printf(" Number of place retries: %" PRIu64 "\n", ull); + break; + case 5: + printf(" Number of volume tags read by volume " + "tag reader: %" PRIu64 "\n", ull); + break; + case 6: + printf(" Number of invalid volume tags returned by " + "volume tag reader: %" PRIu64 "\n", ull); + break; + case 7: + printf(" Number of library door opens: %" PRIu64 "\n", ull); + break; + case 8: + printf(" Number of import/export door opens: %" PRIu64 "\n", + ull); + break; + case 9: + printf(" Number of physical inventory scans: %" PRIu64 "\n", + ull); + break; + case 0xa: + printf(" Number of medium transport unrecovered errors: " + "%" PRIu64 "\n", ull); + break; + case 0xb: + printf(" Number of medium transport recovered errors: " + "%" PRIu64 "\n", ull); + break; + case 0xc: + printf(" Number of medium transport X axis translation " + "unrecovered errors: %" PRIu64 "\n", ull); + break; + case 0xd: + printf(" Number of medium transport X axis translation " + "recovered errors: %" PRIu64 "\n", ull); + break; + case 0xe: + printf(" Number of medium transport Y axis translation " + "unrecovered errors: %" PRIu64 "\n", ull); + break; + case 0xf: + printf(" Number of medium transport Y axis translation " + "recovered errors: %" PRIu64 "\n", ull); + break; + case 0x10: + printf(" Number of medium transport Z axis translation " + "unrecovered errors: %" PRIu64 "\n", ull); + break; + case 0x11: + printf(" Number of medium transport Z axis translation " + "recovered errors: %" PRIu64 "\n", ull); + break; + case 0x12: + printf(" Number of medium transport rotational translation " + "unrecovered errors: %" PRIu64 "\n", ull); + break; + case 0x13: + printf(" Number of medium transport rotational translation " + "recovered errors: %" PRIu64 "\n", ull); + break; + case 0x14: + printf(" Number of medium transport inversion translation " + "unrecovered errors: %" PRIu64 "\n", ull); + break; + case 0x15: + printf(" Number of medium transport inversion translation " + "recovered errors: %" PRIu64 "\n", ull); + break; + case 0x16: + printf(" Number of medium transport auxiliary translation " + "unrecovered errors: %" PRIu64 "\n", ull); + break; + case 0x17: + printf(" Number of medium transport auxiliary translation " + "recovered errors: %" PRIu64 "\n", ull); + break; + default: + printf(" Reserved parameter [0x%x] value: %" PRIu64 "\n", + pc, ull); + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Element statistics page, 0x15 <els> for SMC */ +static bool +show_element_stats_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + unsigned int v; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Element statistics page (smc-3) [0x15]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + printf(" Element address: %d\n", pc); + v = sg_get_unaligned_be32(bp + 4); + printf(" Number of places: %u\n", v); + v = sg_get_unaligned_be32(bp + 8); + printf(" Number of place retries: %u\n", v); + v = sg_get_unaligned_be32(bp + 12); + printf(" Number of picks: %u\n", v); + v = sg_get_unaligned_be32(bp + 16); + printf(" Number of pick retries: %u\n", v); + v = sg_get_unaligned_be32(bp + 20); + printf(" Number of determined volume identifiers: %u\n", v); + v = sg_get_unaligned_be32(bp + 24); + printf(" Number of unreadable volume identifiers: %u\n", v); + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Tape diagnostic data [0x16] <tdd> for tape */ +static bool +show_tape_diag_data_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int k, n, num, pl, pc; + unsigned int v; + const uint8_t * bp; + char str[PCB_STR_LEN]; + char b[512]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Tape diagnostics data page (ssc-3) [0x16]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + printf(" %s: %d\n", param_c, pc); + printf(" Density code: 0x%x\n", bp[6]); + printf(" Medium type: 0x%x\n", bp[7]); + v = sg_get_unaligned_be32(bp + 8); + printf(" Lifetime media motion hours: %u\n", v); + printf(" Repeat: %d\n", !!(bp[13] & 0x80)); + v = bp[13] & 0xf; + printf(" Sense key: 0x%x [%s]\n", v, + sg_get_sense_key_str(v, sizeof(b), b)); + printf(" Additional sense code: 0x%x\n", bp[14]); + printf(" Additional sense code qualifier: 0x%x\n", bp[15]); + if (bp[14] || bp[15]) + printf(" [%s]\n", sg_get_asc_ascq_str(bp[14], bp[15], + sizeof(b), b)); + v = sg_get_unaligned_be32(bp + 16); + printf(" Vendor specific code qualifier: 0x%x\n", v); + v = sg_get_unaligned_be32(bp + 20); + printf(" Product revision level: %u\n", v); + v = sg_get_unaligned_be32(bp + 24); + printf(" Hours since last clean: %u\n", v); + printf(" Operation code: 0x%x\n", bp[28]); + printf(" Service action: 0x%x\n", bp[29] & 0xf); + // Check Medium id number for all zeros + // ssc4r03.pdf does not define this field, why? xxxxxx + if (sg_all_zeros(bp + 32, 32)) + printf(" Medium id number is 32 bytes of zero\n"); + else { + hex2str(bp + 32, 32, " ", 0 /* with ASCII */, sizeof(b), b); + printf(" Medium id number (in hex):\n%s", b); + } + printf(" Timestamp origin: 0x%x\n", bp[64] & 0xf); + // Check Timestamp for all zeros + if (sg_all_zeros(bp + 66, 6)) + printf(" Timestamp is all zeros:\n"); + else { + hex2str(bp + 66, 6, NULL, op->hex2str_oformat, sizeof(b), b); + printf(" Timestamp: %s", b); + } + if (pl > 72) { + n = pl - 72; + k = hex2str(bp + 72, n, " ", op->hex2str_oformat, + sizeof(b), b); + printf(" Vendor specific:\n"); + printf("%s", b); + if (k >= (int)sizeof(b) - 1) + printf(" <truncated>\n"); + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Media changer diagnostic data [0x16] <mcdd> for media changer */ +static bool +show_mchanger_diag_data_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc; + unsigned int v; + const uint8_t * bp; + char str[PCB_STR_LEN]; + char b[512]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Media changer diagnostics data page (smc-3) [0x16]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + printf(" %s: %d\n", param_c, pc); + printf(" Repeat: %d\n", !!(bp[5] & 0x80)); + v = bp[5] & 0xf; + printf(" Sense key: 0x%x [%s]\n", v, + sg_get_sense_key_str(v, sizeof(b), b)); + printf(" Additional sense code: 0x%x\n", bp[6]); + printf(" Additional sense code qualifier: 0x%x\n", bp[7]); + if (bp[6] || bp[7]) + printf(" [%s]\n", sg_get_asc_ascq_str(bp[6], bp[7], + sizeof(b), b)); + v = sg_get_unaligned_be32(bp + 8); + printf(" Vendor specific code qualifier: 0x%x\n", v); + v = sg_get_unaligned_be32(bp + 12); + printf(" Product revision level: %u\n", v); + v = sg_get_unaligned_be32(bp + 16); + printf(" Number of moves: %u\n", v); + v = sg_get_unaligned_be32(bp + 20); + printf(" Number of pick: %u\n", v); + v = sg_get_unaligned_be32(bp + 24); + printf(" Number of pick retries: %u\n", v); + v = sg_get_unaligned_be32(bp + 28); + printf(" Number of places: %u\n", v); + v = sg_get_unaligned_be32(bp + 32); + printf(" Number of place retries: %u\n", v); + v = sg_get_unaligned_be32(bp + 36); + printf(" Number of determined volume identifiers: %u\n", v); + v = sg_get_unaligned_be32(bp + 40); + printf(" Number of unreadable volume identifiers: %u\n", v); + printf(" Operation code: 0x%x\n", bp[44]); + printf(" Service action: 0x%x\n", bp[45] & 0xf); + printf(" Media changer error type: 0x%x\n", bp[46]); + printf(" MTAV: %d\n", !!(bp[47] & 0x8)); + printf(" IAV: %d\n", !!(bp[47] & 0x4)); + printf(" LSAV: %d\n", !!(bp[47] & 0x2)); + printf(" DAV: %d\n", !!(bp[47] & 0x1)); + v = sg_get_unaligned_be16(bp + 48); + printf(" Medium transport address: 0x%x\n", v); + v = sg_get_unaligned_be16(bp + 50); + printf(" Initial address: 0x%x\n", v); + v = sg_get_unaligned_be16(bp + 52); + printf(" Last successful address: 0x%x\n", v); + v = sg_get_unaligned_be16(bp + 54); + printf(" Destination address: 0x%x\n", v); + if (pl > 91) { + printf(" Volume tag information:\n"); + hex2fp(bp + 56, 36, " ", op->hex2str_oformat, stdout); + } + if (pl > 99) { + printf(" Timestamp origin: 0x%x\n", bp[92] & 0xf); + printf(" Timestamp:\n"); + hex2fp(bp + 94, 6, " ", op->hex2str_oformat, stdout); + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* Helper for show_volume_stats_pages() */ +static void +volume_stats_partition(const uint8_t * xp, int len, bool pr_in_hex) +{ + uint64_t ull; + + while (len > 3) { + bool all_ffs, ffs_last_fe; + int dl, pn; + + dl = xp[0] + 1; + if (dl < 3) + return; + pn = sg_get_unaligned_be16(xp + 2); + ffs_last_fe = false; + all_ffs = false; + if (sg_all_ffs(xp + 4, dl - 3)) { + switch (xp[4 + dl - 3]) { + case 0xff: + all_ffs = true; + break; + case 0xfe: + ffs_last_fe = true; + break; + default: + break; + } + } + if (! (all_ffs || ffs_last_fe)) { + ull = sg_get_unaligned_be(dl - 4, xp + 4); + if (pr_in_hex) + printf(" partition number: %d, partition record data " + "counter: 0x%" PRIx64 "\n", pn, ull); + else + printf(" partition number: %d, partition record data " + "counter: %" PRIu64 "\n", pn, ull); + } else if (all_ffs) + printf(" partition number: %d, partition record data " + "counter is all 0xFFs\n", pn); + else /* ffs_last_fe is true */ + printf(" partition number: %d, partition record data " + "counter is all 0xFFs apart\n from a trailing " + "0xFE\n", pn); + xp += dl; + len -= dl; + } +} + +/* Helper for show_volume_stats_pages() */ +static void +volume_stats_history(const uint8_t * xp, int len) +{ + while (len > 3) { + int dl, mhi; + + dl = xp[0] + 1; + if (dl < 4) + return; + mhi = sg_get_unaligned_be16(xp + 2); + if (dl < 12) + printf(" index: %d\n", mhi); + else if (12 == dl) + printf(" index: %d, vendor: %.8s\n", mhi, xp + 4); + else + printf(" index: %d, vendor: %.8s, unit serial number: %.*s\n", + mhi, xp + 4, dl - 12, xp + 12); + xp += dl; + len -= dl; + } +} + +/* Volume Statistics log page and subpages (ssc-4) [0x17, 0x0-0xf] <vs> */ +static bool +show_volume_stats_pages(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool skip_out = false; + bool evsm_output = false; + int num, pl, pc, subpg_code; + bool spf; + const uint8_t * bp; + char str[PCB_STR_LEN]; + char b[512]; + +if (jop) { }; + spf = !!(resp[0] & 0x40); + subpg_code = spf ? resp[1] : NOT_SPG_SUBPG; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (subpg_code < 0x10) + printf("Volume statistics page (ssc-4), subpage=%d\n", + subpg_code); + else { + printf("Volume statistics page (ssc-4), subpage=%d; Reserved, " + "skip\n", subpg_code); + return false; + } + } + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + + switch (pc) { + case 0: + printf(" Page valid: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 1: + printf(" Thread count: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 2: + printf(" Total data sets written: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 3: + printf(" Total write retries: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 4: + printf(" Total unrecovered write errors: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 5: + printf(" Total suspended writes: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 6: + printf(" Total fatal suspended writes: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 7: + printf(" Total data sets read: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 8: + printf(" Total read retries: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 9: + printf(" Total unrecovered read errors: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0xa: + printf(" Total suspended reads: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0xb: + printf(" Total fatal suspended reads: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0xc: + printf(" Last mount unrecovered write errors: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0xd: + printf(" Last mount unrecovered read errors: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0xe: + printf(" Last mount megabytes written: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0xf: + printf(" Last mount megabytes read: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x10: + printf(" Lifetime megabytes written: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x11: + printf(" Lifetime megabytes read: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x12: + printf(" Last load write compression ratio: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x13: + printf(" Last load read compression ratio: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x14: + printf(" Medium mount time: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x15: + printf(" Medium ready time: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x16: + printf(" Total native capacity [MB]: %s\n", + num_or_unknown(bp + 4, pl - 4, false, b, sizeof(b))); + break; + case 0x17: + printf(" Total used native capacity [MB]: %s\n", + num_or_unknown(bp + 4, pl - 4, false, b, sizeof(b))); + break; + case 0x1a: + printf(" Volume stop writes of forward wraps: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x1b: + printf(" Volume stop writes of backward wraps: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x40: + printf(" Volume serial number: %.*s\n", pl - 4, bp + 4); + break; + case 0x41: + printf(" Tape lot identifier: %.*s\n", pl - 4, bp + 4); + break; + case 0x42: + printf(" Volume barcode: %.*s\n", pl - 4, bp + 4); + break; + case 0x43: + printf(" Volume manufacturer: %.*s\n", pl - 4, bp + 4); + break; + case 0x44: + printf(" Volume license code: %.*s\n", pl - 4, bp + 4); + break; + case 0x45: + printf(" Volume personality: %.*s\n", pl - 4, bp + 4); + break; + case 0x80: + printf(" Write protect: %s\n", + num_or_unknown(bp + 4, pl - 4, false, b, sizeof(b))); + break; + case 0x81: + printf(" WORM: %s\n", + num_or_unknown(bp + 4, pl - 4, false, b, sizeof(b))); + break; + case 0x82: + printf(" Maximum recommended tape path temperature exceeded: " + "%s\n", num_or_unknown(bp + 4, pl - 4, false, b, + sizeof(b))); + break; + case 0x100: + printf(" Volume write mounts: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x101: + printf(" Beginning of medium passes: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x102: + printf(" Middle of medium passes: %" PRIu64 "\n", + sg_get_unaligned_be(pl - 4, bp + 4)); + break; + case 0x200: + printf(" Logical position of first encrypted logical object:\n"); + volume_stats_partition(bp + 4, pl - 4, true); + break; + case 0x201: + printf(" Logical position of first unencrypted logical object " + "after first\n encrypted logical object:\n"); + volume_stats_partition(bp + 4, pl - 4, true); + break; + case 0x202: + printf(" Native capacity partition(s) [MB]:\n"); + volume_stats_partition(bp + 4, pl - 4, false); + break; + case 0x203: + printf(" Used native capacity partition(s) [MB]:\n"); + volume_stats_partition(bp + 4, pl - 4, false); + break; + case 0x204: + printf(" Remaining native capacity partition(s) [MB]:\n"); + volume_stats_partition(bp + 4, pl - 4, false); + break; + case 0x300: + printf(" Mount history:\n"); + volume_stats_history(bp + 4, pl - 4); + break; + + default: + if (pc >= 0xf000) { + if (op->exclude_vendor) { + skip_out = true; + if ((op->verbose > 0) && (0 == op->do_brief) && + (! evsm_output)) { + evsm_output = true; + printf(" Vendor specific parameter(s) being " + "ignored\n"); + } + } else + printf(" Vendor specific %s (0x%x), payload in hex\n", + param_c, pc); + } else + printf(" Reserved %s (0x%x), payload in hex\n", param_c, pc); + if (skip_out) + skip_out = false; + else + hex2fp(bp + 4, pl - 4, " ", op->hex2str_oformat, stdout); + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* TAPE_ALERT_LPAGE [0x2e] <ta> */ +static bool +show_tape_alert_ssc_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + int num, pl, pc, flag; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + /* N.B. the Tape alert log page for smc-3 is different */ + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Tape alert page (ssc-3) [0x2e]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + flag = bp[4] & 1; + if (op->verbose && (0 == op->do_brief) && flag) + printf(" >>>> "); + if ((0 == op->do_brief) || op->verbose || flag) { + if (NULL == sg_lib_tapealert_strs[0]) + printf(" No string available for code 0x%x, flag: %d\n", + pc, flag); + else if (pc <= 0x40) + printf(" %s: %d\n", sg_lib_tapealert_strs[pc], flag); + else + printf(" Reserved %s 0x%x, flag: %d\n", param_c, pc, flag); + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* 0x37 */ +static bool +show_seagate_cache_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool skip = false; + int num, pl, pc; + int bsti = 0; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) { + if (resp[1] > 0) { + printf("Suspicious page 0x37, SPF=0 but subpage=0x%x\n", resp[1]); + if (op->verbose) + printf("... try vendor=wdc\n"); + if (op->do_brief > 0) + return true; + } else + printf("Seagate cache page [0x37]\n"); + } + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0: + ++bsti; + if (bsti < 2) + printf(" Blocks sent to initiator"); + else + skip = true; + break; + case 1: + printf(" Blocks received from initiator"); + break; + case 2: + printf(" Blocks read from cache and sent to initiator"); + break; + case 3: + printf(" Number of read and write commands whose size " + "<= segment size"); + break; + case 4: + printf(" Number of read and write commands whose size " + "> segment size"); + break; + default: + printf(" Unknown Seagate %s = 0x%x", param_c, pc); + break; + } + if (skip) + skip = false; + else { + printf(" = %" PRIu64 "", sg_get_unaligned_be(pl - 4, bp + 4)); + printf("\n"); + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, + sizeof(str))); + } +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +/* 0x37 */ +static bool +show_hgst_misc_page(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + bool valid = false; + int num, pl, pc; + const uint8_t * bp; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("HGST/WDC miscellaneous page [0x37, 0x%x]\n", + op->decod_subpg_code); + num = len - 4; + if (num < 0x30) { + printf("HGST/WDC miscellaneous page too short (%d) < 48\n", num); + return valid; + } + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + switch (pc) { + case 0: + valid = true; + printf(" Power on hours = %u\n", sg_get_unaligned_be32(bp + 4)); + printf(" Total Bytes Read = %" PRIu64 "\n", + sg_get_unaligned_be64(bp + 8)); + printf(" Total Bytes Written = %" PRIu64 "\n", + sg_get_unaligned_be64(bp + 16)); + printf(" Max Drive Temp (Celsius) = %u\n", bp[24]); + printf(" GList Size = %u\n", sg_get_unaligned_be16(bp + 25)); + printf(" Number of Information Exceptions = %u\n", bp[27]); + printf(" MED EXC = %u\n", !! (0x80 & bp[28])); + printf(" HDW EXC = %u\n", !! (0x40 & bp[28])); + printf(" Total Read Commands = %" PRIu64 "\n", + sg_get_unaligned_be64(bp + 29)); + printf(" Total Write Commands = %" PRIu64 "\n", + sg_get_unaligned_be64(bp + 37)); + printf(" Flash Correction Count = %u\n", + sg_get_unaligned_be16(bp + 46)); + break; + default: + valid = false; + printf(" Unknown HGST/WDC %s = 0x%x", param_c, pc); + break; + } + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return valid; +} + +/* 0x3e */ +static bool +show_seagate_factory_page(const uint8_t * resp, int len, + struct opts_t * op, sgj_opaque_p jop) +{ + bool valid = false; + int num, pl, pc; + const uint8_t * bp; + uint64_t ull; + char str[PCB_STR_LEN]; + +if (jop) { }; + if (op->verbose || ((! op->do_raw) && (0 == op->do_hex))) + printf("Seagate/Hitachi factory page [0x3e]\n"); + num = len - 4; + bp = &resp[0] + 4; + while (num > 3) { + pc = sg_get_unaligned_be16(bp + 0); + pl = bp[3] + 4; + if (op->filter_given) { + if (pc != op->filter) + goto skip; + } + if (op->do_raw) { + dStrRaw(bp, pl); + goto filter_chk; + } else if (op->do_hex) { + hex2stdout(bp, pl, op->dstrhex_no_ascii); + goto filter_chk; + } + valid = true; + switch (pc) { + case 0: + printf(" number of hours powered up"); + break; + case 8: + printf(" number of minutes until next internal SMART test"); + break; + default: + valid = false; + printf(" Unknown Seagate/Hitachi %s = 0x%x", param_c, pc); + break; + } + if (valid) { + ull = sg_get_unaligned_be(pl - 4, bp + 4); + if (0 == pc) + printf(" = %.2f", ((double)ull) / 60.0 ); + else + printf(" = %" PRIu64 "", ull); + } + printf("\n"); + if (op->do_pcb) + printf(" <%s>\n", get_pcb_str(bp[2], str, sizeof(str))); +filter_chk: + if (op->filter_given) + break; +skip: + num -= pl; + bp += pl; + } + return true; +} + +static void +decode_page_contents(const uint8_t * resp, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int pg_code, subpg_code, vpn; + bool spf; + bool done = false; + const struct log_elem * lep; + + if (len < 3) { + pr2serr("%s: response has bad length: %d\n", __func__, len); + return; + } + spf = !!(resp[0] & 0x40); + pg_code = resp[0] & 0x3f; + if ((VP_HITA == op->vend_prod_num) && (pg_code >= 0x30)) + subpg_code = resp[1]; /* Hitachi don't set SPF on VS pages */ + else + subpg_code = spf ? resp[1] : NOT_SPG_SUBPG; + op->decod_subpg_code = subpg_code; + if ((SUPP_SPGS_SUBPG == subpg_code) && (SUPP_PAGES_LPAGE != pg_code)) { + done = show_supported_pgs_sub_page(resp, len, op, jop); + if (done) + return; + } + vpn = (op->vend_prod_num >= 0) ? op->vend_prod_num : op->deduced_vpn; + lep = pg_subpg_pdt_search(pg_code, subpg_code, op->dev_pdt, vpn); + + /* Below is the indirect function call to all the show_* functions */ + if (lep && lep->show_pagep) + done = (*lep->show_pagep)(resp, len, op, jop); + + if (! done) { + if (0 == op->do_hex) { + static const char * unable_s = "Unable to decode page = 0x"; + + if (subpg_code > 0) + printf("%s%x, subpage = 0x%x, here is hex:\n", unable_s, + pg_code, subpg_code); + else + printf("%s%x, here is hex:\n", unable_s, pg_code); + } + if ((len > 128) && (0 == op->do_hex)) { + hex2fp(resp, 64, " ", op->hex2str_oformat, stdout); + printf(" ..... [truncated after 64 of %d bytes (use '-H' to " + "see the rest)]\n", len); + } else { + if (0 == op->do_hex) + hex2fp(resp, len, " ", op->hex2str_oformat, stdout); + else + hex2stdout(resp, len, op->dstrhex_no_ascii); + } + } +} + +/* Tries to fetch the TEMPERATURE_LPAGE [0xd] page first. If that fails + * tries to get the Informational Exceptions (IE_LPAGE) page. */ +static int +fetchTemperature(int sg_fd, uint8_t * resp, int max_len, struct opts_t * op, + sgj_opaque_p jop) +{ + int len; + int res = 0; + + op->pg_code = TEMPERATURE_LPAGE; + op->subpg_code = NOT_SPG_SUBPG; + res = do_logs(sg_fd, resp, max_len, op); + if (0 == res) { + len = sg_get_unaligned_be16(resp + 2) + 4; + if (op->do_raw) + dStrRaw(resp, len); + else if (op->do_hex) + hex2stdout(resp, len, op->dstrhex_no_ascii); + else + show_temperature_page(resp, len, op, jop); + } else if (SG_LIB_CAT_NOT_READY == res) + pr2serr("Device not ready\n"); + else { + op->pg_code = IE_LPAGE; + res = do_logs(sg_fd, resp, max_len, op); + if (0 == res) { + len = sg_get_unaligned_be16(resp + 2) + 4; + if (op->do_raw) + dStrRaw(resp, len); + else if (op->do_hex) + hex2stdout(resp, len, op->dstrhex_no_ascii); + else + show_ie_page(resp, len, op, jop); + } else + pr2serr("Unable to find temperature in either Temperature or " + "IE log page\n"); + } + sg_cmds_close_device(sg_fd); + return (res >= 0) ? res : SG_LIB_CAT_OTHER; +} + +/* Returns 0 if successful else SG_LIB_SYNTAX_ERROR. */ +static int +decode_pg_arg(struct opts_t * op) +{ + int nn; + const struct log_elem * lep; + char * cp; + + if (isalpha((uint8_t)op->pg_arg[0])) { + char b[80]; + + if (strlen(op->pg_arg) >= (sizeof(b) - 1)) { + pr2serr("argument to '--page=' is too long\n"); + return SG_LIB_SYNTAX_ERROR; + } + strcpy(b, op->pg_arg); + cp = (char *)strchr(b, ','); + if (cp) + *cp = '\0'; + lep = acron_search(b); + if (NULL == lep) { + pr2serr("bad argument to '--page=' no acronyn match to " + "'%s'\n", b); + pr2serr(" Try using '-e' or'-ee' to see available " + "acronyns\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->lep = lep; + op->pg_code = lep->pg_code; + if (cp) { + nn = sg_get_num_nomult(cp + 1); + if ((nn < 0) || (nn > 255)) { + pr2serr("Bad second value in argument to " + "'--page='\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->subpg_code = nn; + } else + op->subpg_code = lep->subpg_code; + } else { /* numeric arg: either 'pg_num' or 'pg_num,subpg_num' */ + int n; + + cp = (char *)strchr(op->pg_arg, ','); + n = sg_get_num_nomult(op->pg_arg); + if ((n < 0) || (n > 63)) { + pr2serr("Bad argument to '--page='\n"); + usage(1); + return SG_LIB_SYNTAX_ERROR; + } + if (cp) { + nn = sg_get_num_nomult(cp + 1); + if ((nn < 0) || (nn > 255)) { + pr2serr("Bad second value in argument to " + "'--page='\n"); + usage(1); + return SG_LIB_SYNTAX_ERROR; + } + } else + nn = 0; + op->pg_code = n; + op->subpg_code = nn; + } + return 0; +} + +/* Since the Supported subpages page is sitting in the rsp_buff which is + * MX_ALLOC_LEN bytes long (~ 64 KB) then move it (from rsp_buff+0 to + * rsp_buff+pg_len-1) to the top end of that buffer. Then there is room + * to merge supp_pgs_rsp with the supported subpages with the result back + * at the bottom of rsp_buff. The new length of the merged subpages page + * (excluding its 4 byte header) is returned. + * Assumes both pages are in ascending order (as required by SPC-4). */ +static int +merge_both_supported(const uint8_t * supp_pgs_p, int su_p_pg_len, int pg_len) +{ + uint8_t pg; + int k, kp, ks; + int max_blen = (2 * su_p_pg_len) + pg_len; + uint8_t * m_buff = rsp_buff + (rsp_buff_sz - pg_len); + uint8_t * r_buff = rsp_buff + 4; + + if (pg_len > 0) + memmove(m_buff, rsp_buff + 4, pg_len); + for (k = 0, kp = 0, ks = 0; k < max_blen; k += 2) { + if (kp < su_p_pg_len) + pg = supp_pgs_p[kp]; + else + pg = 0xff; + if (ks < pg_len) { + if (m_buff[ks] < pg) { + r_buff[k] = m_buff[ks]; + r_buff[k + 1] = m_buff[ks + 1]; + ks += 2; + } else if ((m_buff[ks] == pg) && (m_buff[ks + 1] == 0)) { + r_buff[k] = m_buff[ks]; + r_buff[k + 1] = m_buff[ks + 1]; + ks += 2; + ++kp; + } else { + r_buff[k] = pg; + r_buff[k + 1] = 0; + ++kp; + } + } else { + if (0xff == pg) + break; + r_buff[k] = pg; + r_buff[k + 1] = 0; + ++kp; + } + } + sg_put_unaligned_be16(k, rsp_buff + 2); + return k; +} + + +int +main(int argc, char * argv[]) +{ + bool as_json; + int k, nn, pg_len, res, vb; + int resp_len = 0; + int su_p_pg_len = 0; + int in_len = -1; + int sg_fd = -1; + int ret = 0; + uint8_t * parr; + uint8_t * free_parr = NULL; + struct opts_t * op; + sgj_state * jsp; + sgj_opaque_p jop = NULL; + struct sg_simple_inquiry_resp inq_out; + struct opts_t opts SG_C_CPP_ZERO_INIT; + uint8_t supp_pgs_rsp[256]; + char b[128]; + static const int blen = sizeof(b); + + op = &opts; + /* N.B. some disks only give data for current cumulative */ + op->page_control = 1; + op->dev_pdt = -1; + op->vend_prod_num = VP_NONE; + op->deduced_vpn = VP_NONE; + res = parse_cmd_line(op, argc, argv); + if (res) + return SG_LIB_SYNTAX_ERROR; + if (op->do_help) { + usage_for(op->do_help, op); + return 0; + } + jsp = &op->json_st; + as_json = jsp->pr_as_json; + if (as_json) { + if (op->do_name) { + pr2serr(">>> The --json option is superior to the --name " + "option.\n"); + pr2serr(">>> Ignoring the --name option.\n"); + op->do_name = false; + } + jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp); + } +#ifdef DEBUG + pr2serr("In DEBUG mode, "); + if (op->verbose_given && op->version_given) { + pr2serr("but override: '-vV' given, zero verbose and continue\n"); + op->verbose_given = false; + op->version_given = false; + op->verbose = 0; + } else if (! op->verbose_given) { + pr2serr("set '-vv'\n"); + op->verbose = 2; + } else + pr2serr("keep verbose=%d\n", op->verbose); +#else + if (op->verbose_given && op->version_given) + pr2serr("Not in DEBUG mode, so '-vV' has no special action\n"); +#endif + if (op->version_given) { + pr2serr("Version string: %s\n", version_str); + return 0; + } + if (op->do_hex > 0) { + if (op->do_hex > 2) { + op->dstrhex_no_ascii = -1; + op->hex2str_oformat = 1; + } else { + op->dstrhex_no_ascii = (1 == op->do_hex); + op->hex2str_oformat = (1 == op->do_hex); + } + } else { + if (op->undefined_hex > 0) { + if (op->undefined_hex > 2) { + op->dstrhex_no_ascii = -1; + op->hex2str_oformat = 1; + } else { + op->dstrhex_no_ascii = (1 == op->undefined_hex); + op->hex2str_oformat = (1 == op->undefined_hex); + } + } else { /* default when no --hex nor --undefined */ + op->dstrhex_no_ascii = -1; + op->hex2str_oformat = 1; + } + } + vb = op->verbose; + if (op->vend_prod) { + if (0 == memcmp("-1", op->vend_prod,3)) + k = VP_NONE; + else if (isdigit((uint8_t)op->vend_prod[0])) + k = sg_get_num_nomult(op->vend_prod); + else + k = find_vpn_by_acron(op->vend_prod); + op->vend_prod_num = k; + if (VP_ALL == k) + ; + else if ((k < 0) || (k > (32 - MVP_OFFSET))) { + pr2serr("Bad vendor/product acronym after '--vendor=' " + " ('-M ') option\n"); + enumerate_vp(); + return SG_LIB_SYNTAX_ERROR; + } + } + if (op->do_enumerate > 0) { + if (op->device_name && vb) + pr2serr("Warning: device: %s is being ignored\n", + op->device_name); + enumerate_pages(op); + return 0; + } + if (op->in_fn) { + if (op->maxlen_given) { + if (op->maxlen > MX_INLEN_ALLOC_LEN) { + pr2serr("bad argument to '--maxlen=' when --in= given, from " + "2 to %d (inclusive) expected\n", MX_INLEN_ALLOC_LEN); + return SG_LIB_SYNTAX_ERROR; + } + rsp_buff_sz = op->maxlen; + } else + rsp_buff_sz = DEF_INLEN_ALLOC_LEN; + } else { + if (op->maxlen_given) { + if (op->maxlen > MX_ALLOC_LEN) { + pr2serr("bad argument to '--maxlen=', from 2 to 65535 " + "(inclusive) expected\n"); + return SG_LIB_SYNTAX_ERROR; + } + rsp_buff_sz = op->maxlen; + } + } + rsp_buff = sg_memalign(rsp_buff_sz, 0 /* page aligned */, &free_rsp_buff, + false); + if (NULL == rsp_buff) { + pr2serr("Unable to allocate %d bytes on the heap\n", rsp_buff_sz); + ret = sg_convert_errno(ENOMEM); + goto err_out; + } + if (NULL == op->device_name) { + if (op->in_fn) { + bool found = false; + bool r_spf = false; + uint16_t u; + int pg_code, subpg_code, pdt, n; + const struct log_elem * lep; + const uint8_t * bp; + + if ((ret = sg_f2hex_arr(op->in_fn, op->do_raw, false, rsp_buff, + &in_len, rsp_buff_sz))) + goto err_out; + if (vb > 2) + pr2serr("Read %d [0x%x] bytes of user supplied data\n", + in_len, in_len); + if (op->do_raw) + op->do_raw = false; /* can interfere on decode */ + if (in_len < 4) { + pr2serr("--in=%s only decoded %d bytes (needs 4 at least)\n", + op->in_fn, in_len); + ret = SG_LIB_SYNTAX_ERROR; + goto err_out; + } + if (op->pg_arg) { + char b[144]; + char * cp; + + strcpy(b, op->pg_arg); + cp = (char *)strchr(b, ','); + if (cp) + *cp = '\0'; + lep = acron_search(b); + if (NULL == lep) { + pr2serr("bad argument to '--page=' no acronyn match to " + "'%s'\n", b); + pr2serr(" Try using '-e' or'-ee' to see available " + "acronyns\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->lep = lep; + op->pg_code = lep->pg_code; + op->subpg_code = lep->subpg_code; + if (op->subpg_code > 0) + r_spf = true; + } + + for (bp = rsp_buff, k = 0; k < in_len; bp += n, k += n) { + bool spf = !! (bp[0] & 0x40); + + pg_code = bp[0] & 0x3f; + subpg_code = spf ? bp[1] : NOT_SPG_SUBPG; + u = sg_get_unaligned_be16(bp + 2); + n = u + 4; + if (n > (in_len - k)) { + pr2serr("bytes decoded remaining (%d) less than lpage " + "length (%d), try decoding anyway\n", in_len - k, + n); + n = in_len - k; + } + if (op->pg_arg) { + if ((NOT_SPG_SUBPG == op->subpg_code) && spf) { + continue; + } else if ((! spf) && (! r_spf)) { + if (pg_code != op->pg_code) + continue; + } else if ((SUPP_SPGS_SUBPG == op->subpg_code) && + (SUPP_PAGES_LPAGE != op->pg_code)) { + if (pg_code != op->pg_code) + continue; + } else if ((SUPP_SPGS_SUBPG != op->subpg_code) && + (SUPP_PAGES_LPAGE == op->pg_code)) { + if (subpg_code != op->subpg_code) + continue; + } else if ((SUPP_SPGS_SUBPG != op->subpg_code) && + (SUPP_PAGES_LPAGE != op->pg_code)) { + if ((pg_code != op->pg_code) || + (subpg_code != op->subpg_code)) + continue; + } + } + if (op->exclude_vendor && (pg_code >= 0x30)) + continue; + found = true; + if (op->do_hex > 2) { + hex2fp(bp, n, NULL, op->hex2str_oformat, stdout); + continue; + } + pdt = op->dev_pdt; + lep = pg_subpg_pdt_search(pg_code, subpg_code, pdt, + op->vend_prod_num); + if (lep) { + /* Below is the indirect function call to all the + * show_* functions */ + if (lep->show_pagep) + (*lep->show_pagep)(bp, n, op, jop); + else + sgj_pr_hr(jsp, "Unable to decode %s [%s]\n", + lep->name, lep->acron); + } else { + nn = sg_scnpr(b, blen, "Unable to decode page=0x%x", + pg_code); + if (subpg_code > 0) + sg_scnpr(b + nn, blen - nn, ", subpage=0x%x", + subpg_code); + if (pdt >= 0) + sg_scnpr(b + nn, blen - nn, ", pdt=0x%x\n", pdt); + sgj_pr_hr(jsp, "%s\n", b); + } + } /* end of page/subpage search loop */ + if (op->pg_arg && (! found)) { + nn = sg_scnpr(b, blen, "Unable to find page=0x%x", + op->pg_code); + if (op->subpg_code > 0) + sg_scnpr(b + nn, blen - nn, ", subpage=0x%x", + op->subpg_code); + sgj_pr_hr(jsp, "%s\n", b); + if (jsp->pr_as_json) + sgj_js_nv_i(jsp, jop, "page_not_found", 1); + } + ret = 0; + goto err_out; + } + if (op->pg_arg) { /* do this for 'sg_logs -p xxx' */ + ret = decode_pg_arg(op); + if (ret) + goto err_out; + } + pr2serr("No DEVICE argument given\n\n"); + usage_for(1, op); + ret = SG_LIB_FILE_ERROR; + goto err_out; + } + if (op->do_select) { + if (op->do_temperature) { + pr2serr("--select cannot be used with --temperature\n"); + ret = SG_LIB_CONTRADICT; + goto err_out; + } + if (op->do_transport) { + pr2serr("--select cannot be used with --transport\n"); + ret = SG_LIB_CONTRADICT; + goto err_out; + } + } else if (op->do_raw) { + if (sg_set_binary_mode(STDOUT_FILENO) < 0) { + perror("sg_set_binary_mode"); + ret = SG_LIB_FILE_ERROR; + goto err_out; + } + } + if (op->do_all) { + if (op->do_select) { + pr2serr("--all conflicts with --select\n"); + ret = SG_LIB_CONTRADICT; + goto err_out; + } + } + if (op->in_fn) { + if (! op->do_select) { + pr2serr("--in=FN can only be used with --select when DEVICE " + "given\n"); + ret = SG_LIB_CONTRADICT; + goto err_out; + } + if ((ret = sg_f2hex_arr(op->in_fn, op->do_raw, false, rsp_buff, + &in_len, rsp_buff_sz))) + goto err_out; + if (vb > 2) + pr2serr("Read %d [0x%x] bytes of user supplied data\n", in_len, + in_len); + } + if (op->pg_arg) { + if (op->do_all) { + if (0 == op->do_brief) + pr2serr(">>> warning: --page=%s ignored when --all given\n", + op->pg_arg); + } else { + ret = decode_pg_arg(op); + if (ret) + goto err_out; + } + } + +#ifdef SG_LIB_WIN32 +#ifdef SG_LIB_WIN32_DIRECT + win32_spt_init_state = !! scsi_pt_win32_spt_state(); + if (vb > 4) + pr2serr("Initial win32 SPT interface state: %s\n", + win32_spt_init_state ? "direct" : "indirect"); +#endif +#endif + sg_fd = sg_cmds_open_device(op->device_name, op->o_readonly, vb); + if ((sg_fd < 0) && (! op->o_readonly)) + sg_fd = sg_cmds_open_device(op->device_name, true /* ro */, vb); + if (sg_fd < 0) { + pr2serr("error opening file: %s: %s \n", op->device_name, + safe_strerror(-sg_fd)); + ret = sg_convert_errno(-sg_fd); + goto err_out; + } + if (op->do_list || op->do_all) { + op->pg_code = SUPP_PAGES_LPAGE; + if ((op->do_list > 1) || (op->do_all > 1)) + op->subpg_code = SUPP_SPGS_SUBPG; + } + if (op->do_transport) { + if ((op->pg_code > 0) || (op->subpg_code > 0) || + op->do_temperature) { + pr2serr("'-T' should not be mixed with options implying other " + "pages\n"); + ret = SG_LIB_FILE_ERROR; + goto err_out; + } + op->pg_code = PROTO_SPECIFIC_LPAGE; + } + + memset(&inq_out, 0, sizeof(inq_out)); + if (op->no_inq < 2) { + if (sg_simple_inquiry(sg_fd, &inq_out, true, vb)) { + pr2serr("%s doesn't respond to a SCSI INQUIRY\n", + op->device_name); + ret = SG_LIB_CAT_OTHER; + goto err_out; + } + op->dev_pdt = inq_out.peripheral_type; + if ((! op->do_raw) && (0 == op->do_hex) && (! op->do_name) && + (0 == op->no_inq) && (0 == op->do_brief)) + sgj_pr_hr(jsp, " %.8s %.16s %.4s\n", inq_out.vendor, + inq_out.product, inq_out.revision); + memcpy(t10_vendor_str, inq_out.vendor, 8); + memcpy(t10_product_str, inq_out.product, 16); + if (VP_NONE == op->vend_prod_num) + op->deduced_vpn = find_vpn_by_inquiry(); + } + + if (op->do_temperature) { + ret = fetchTemperature(sg_fd, rsp_buff, SHORT_RESP_LEN, op, jop); + goto err_out; + } + if (op->do_select) { + k = sg_ll_log_select(sg_fd, op->do_pcreset, op->do_sp, + op->page_control, op->pg_code, op->subpg_code, + rsp_buff, ((in_len > 0) ? in_len : 0), true, vb); + if (k) { + if (SG_LIB_CAT_NOT_READY == k) + pr2serr("log_select: device not ready\n"); + else if (SG_LIB_CAT_ILLEGAL_REQ == k) + pr2serr("log_select: field in cdb illegal\n"); + else if (SG_LIB_CAT_INVALID_OP == k) + pr2serr("log_select: not supported\n"); + else if (SG_LIB_CAT_UNIT_ATTENTION == k) + pr2serr("log_select: unit attention\n"); + else if (SG_LIB_CAT_ABORTED_COMMAND == k) + pr2serr("log_select: aborted command\n"); + else + pr2serr("log_select: failed (%d), try '-v' for more " + "information\n", k); + } + ret = (k >= 0) ? k : SG_LIB_CAT_OTHER; + goto err_out; + } + if (op->do_list > 2) { + const int supp_pgs_blen = sizeof(supp_pgs_rsp); + + op->subpg_code = NOT_SPG_SUBPG; + res = do_logs(sg_fd, supp_pgs_rsp, supp_pgs_blen, op); + if (res != 0) + goto bad; + su_p_pg_len = sg_get_unaligned_be16(supp_pgs_rsp + 2); + if ((su_p_pg_len + 4) > supp_pgs_blen) { + pr2serr("Supported log pages log page is too long [%d], exit\n", + su_p_pg_len); + res = SG_LIB_CAT_OTHER; + goto bad; + } + op->subpg_code = SUPP_SPGS_SUBPG; + } + resp_len = (op->maxlen > 0) ? op->maxlen : MX_ALLOC_LEN; + res = do_logs(sg_fd, rsp_buff, resp_len, op); + if (0 == res) { + pg_len = sg_get_unaligned_be16(rsp_buff + 2); + if ((pg_len + 4) > resp_len) { + pr2serr("Only fetched %d bytes of response (available: %d " + "bytes)\n truncate output\n", + resp_len, pg_len + 4); + pg_len = resp_len - 4; + } + goto good; + } +bad: + if (SG_LIB_CAT_INVALID_OP == res) + pr2serr("%snot supported\n", ls_s); + else if (SG_LIB_CAT_NOT_READY == res) + pr2serr("%sdevice not ready\n", ls_s); + else if (SG_LIB_CAT_ILLEGAL_REQ == res) { + if ((op->do_list > 2) && (SUPP_SPGS_SUBPG == op->subpg_code)) { + rsp_buff[0] = 0x40; + rsp_buff[1] = SUPP_SPGS_SUBPG; + pg_len = 0; + res = 0; + if (op->verbose) + pr2serr("%sfield in cdb illegal in [0,0xff], " + "continue with merge\n", ls_s); + goto good; + } else + pr2serr("%sfield in cdb illegal\n", ls_s); + } else if (SG_LIB_CAT_UNIT_ATTENTION == res) + pr2serr("%sunit attention\n", ls_s); + else if (SG_LIB_CAT_ABORTED_COMMAND == res) + pr2serr("%saborted command\n", ls_s); + else if (SG_LIB_TRANSPORT_ERROR == res) + pr2serr("%stransport error\n", ls_s); + else + pr2serr("%sother error [%d]\n", ls_s, res); + ret = res; + goto err_out; + +good: + if (op->do_list > 2) + pg_len = merge_both_supported(supp_pgs_rsp + 4, su_p_pg_len, pg_len); + + if (0 == op->do_all) { + if (op->filter_given) { + if (op->do_hex > 2) + hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii); + else + decode_page_contents(rsp_buff, pg_len + 4, op, jop); + } else if (op->do_raw) + dStrRaw(rsp_buff, pg_len + 4); + else if (op->do_hex > 1) + hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii); + else if (pg_len > 1) { + if (op->do_hex) { + if (rsp_buff[0] & 0x40) + printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, " + "page_len=0x%x\n", rsp_buff[0] & 0x3f, rsp_buff[1], + !!(rsp_buff[0] & 0x80), pg_len); + else + printf("Log page code=0x%x, DS=%d, SPF=0, page_len=0x%x\n", + rsp_buff[0] & 0x3f, !!(rsp_buff[0] & 0x80), pg_len); + hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii); + } + else + decode_page_contents(rsp_buff, pg_len + 4, op, jop); + } + } + ret = res; + + if (op->do_all && (pg_len > 1)) { + int my_len = pg_len; + bool spf; + + parr = sg_memalign(parr_sz, 0, &free_parr, false); + if (NULL == parr) { + pr2serr("Unable to allocate heap for parr\n"); + ret = sg_convert_errno(ENOMEM); + goto err_out; + } + spf = !!(rsp_buff[0] & 0x40); + if (my_len > parr_sz) { + pr2serr("Unexpectedly large page_len=%d, trim to %d\n", my_len, + parr_sz); + my_len = parr_sz; + } + memcpy(parr, rsp_buff + 4, my_len); + for (k = 0; k < my_len; ++k) { + op->pg_code = parr[k] & 0x3f; + if (spf) + op->subpg_code = parr[++k]; + else + op->subpg_code = NOT_SPG_SUBPG; + + /* Some devices include [pg_code, 0xff] for all pg_code > 0 */ + if ((op->pg_code > 0) && (SUPP_SPGS_SUBPG == op->subpg_code)) + continue; /* skip since no new information */ + if ((op->pg_code >= 0x30) && op->exclude_vendor) + continue; + if (! op->do_raw) + sgj_pr_hr(jsp, "\n"); + res = do_logs(sg_fd, rsp_buff, resp_len, op); + if (0 == res) { + pg_len = sg_get_unaligned_be16(rsp_buff + 2); + if ((pg_len + 4) > resp_len) { + pr2serr("Only fetched %d bytes of response, truncate " + "output\n", resp_len); + pg_len = resp_len - 4; + } + if (op->do_raw && (! op->filter_given)) + dStrRaw(rsp_buff, pg_len + 4); + else if (op->do_hex > 4) + decode_page_contents(rsp_buff, pg_len + 4, op, jop); + else if (op->do_hex > 1) + hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii); + else if (1 == op->do_hex) { + if (0 == op->do_brief) { + if (rsp_buff[0] & 0x40) + printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, " + "page_len=0x%x\n", rsp_buff[0] & 0x3f, + rsp_buff[1], !!(rsp_buff[0] & 0x80), + pg_len); + else + printf("Log page code=0x%x, DS=%d, SPF=0, " + "page_len=0x%x\n", rsp_buff[0] & 0x3f, + !!(rsp_buff[0] & 0x80), pg_len); + } + hex2stdout(rsp_buff, pg_len + 4, op->dstrhex_no_ascii); + } + else + decode_page_contents(rsp_buff, pg_len + 4, op, jop); + } else if (SG_LIB_CAT_INVALID_OP == res) + pr2serr("%spage=0x%x,0x%x not supported\n", ls_s, + op->pg_code, op->subpg_code); + else if (SG_LIB_CAT_NOT_READY == res) + pr2serr("%sdevice not ready\n", ls_s); + else if (SG_LIB_CAT_ILLEGAL_REQ == res) + pr2serr("%sfield in cdb illegal [page=0x%x,0x%x]\n", ls_s, + op->pg_code, op->subpg_code); + else if (SG_LIB_CAT_UNIT_ATTENTION == res) + pr2serr("%sunit attention\n", ls_s); + else if (SG_LIB_CAT_ABORTED_COMMAND == res) + pr2serr("%saborted command\n", ls_s); + else + pr2serr("%sfailed, try '-v' for more information\n", ls_s); + } + } +err_out: + if (free_rsp_buff) + free(free_rsp_buff); + if (free_parr) + free(free_parr); + if (sg_fd >= 0) + sg_cmds_close_device(sg_fd); + if (0 == vb) { + if (! sg_if_can2stderr("sg_logs failed: ", ret)) + pr2serr("Some error occurred, try again with '-v' or '-vv' for " + "more information\n"); + } + if (as_json) { + if (0 == op->do_hex) + sgj_js2file(jsp, NULL, ret, stdout); + sgj_finish(jsp); + } + return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; +} |