/* Decodes given user data referral segment descriptor * the number of blocks and returns the number of bytes processed, * -1 for error. */ static int decode_referral_desc(const uint8_t * bp, int bytes) { int j, n; uint64_t first, last; if (NULL == bp) return -1; if (bytes < 20) return -1; first = sg_get_unaligned_be64(bp + 4); last = sg_get_unaligned_be64(bp + 12); printf(" target port descriptors: %d\n", bp[3]); printf(" user data segment: first lba %" PRIu64 ", last lba %" PRIu64 "\n", first, last); n = 20; bytes -= n; for (j = 0; j < bp[3]; j++) { if (bytes < 4) return -1; printf(" target port descriptor %d:\n", j); printf(" port group %x state (%s)\n", sg_get_unaligned_be16(bp + n + 2), decode_tpgs_state(bp[n] & 0xf)); n += 4; bytes -= 4; } return n; }
/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */ static int scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) { int res; unsigned int ui; unsigned char rcBuff[RCAP16_REPLY_LEN]; int verb; verb = (verbose ? verbose - 1: 0); res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, 0, verb); if (0 != res) return res; if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) && (0xff == rcBuff[3])) { res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, 0, verb); if (0 != res) return res; *num_sect = sg_get_unaligned_be64(rcBuff + 0) + 1; *sect_sz = sg_get_unaligned_be32(rcBuff + 8); } else { ui = sg_get_unaligned_be32(rcBuff + 0); /* take care not to sign extend values > 0x7fffffff */ *num_sect = (int64_t)ui + 1; *sect_sz = sg_get_unaligned_be32(rcBuff + 4); } if (verbose) pr2serr(" number of blocks=%" PRId64 " [0x%" PRIx64 "], block " "size=%d\n", *num_sect, *num_sect, *sect_sz); return 0; }
int main(int argc, char * argv[]) { int res, c, k, len, act_resplen; int aff_context = 0; int do_hex = 0; int phy_id = 0; int phy_id_given = 0; int do_raw = 0; int verbose = 0; int do_zero = 0; int64_t sa_ll; uint64_t sa = 0; char i_params[256]; char device_name[512]; char b[256]; unsigned char smp_req[] = {SMP_FRAME_TYPE_REQ, SMP_FN_REPORT_PHY_SATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char smp_resp[SMP_FN_REPORT_PHY_SATA_RESP_LEN]; struct smp_req_resp smp_rr; struct smp_target_obj tobj; int subvalue = 0; char * cp; int ret = 0; memset(device_name, 0, sizeof device_name); while (1) { int option_index = 0; c = getopt_long(argc, argv, "ahHI:p:rs:vVz", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': aff_context = smp_get_num(optarg); if ((aff_context < 0) || (aff_context > 255)) { pr2serr("bad argument to '--affiliation'\n"); return SMP_LIB_SYNTAX_ERROR; } break; case 'h': case '?': usage(); return 0; case 'H': ++do_hex; break; case 'I': strncpy(i_params, optarg, sizeof(i_params)); i_params[sizeof(i_params) - 1] = '\0'; break; case 'p': phy_id = smp_get_num(optarg); if ((phy_id < 0) || (phy_id > 254)) { pr2serr("bad argument to '--phy', expect value from 0 to " "254\n"); return SMP_LIB_SYNTAX_ERROR; } ++phy_id_given; break; case 'r': ++do_raw; break; case 's': sa_ll = smp_get_llnum(optarg); if (-1LL == sa_ll) { pr2serr("bad argument to '--sa'\n"); return SMP_LIB_SYNTAX_ERROR; } sa = (uint64_t)sa_ll; break; case 'v': ++verbose; break; case 'V': pr2serr("version: %s\n", version_str); return 0; case 'z': ++do_zero; break; default: pr2serr("unrecognised switch code 0x%x ??\n", c); usage(); return SMP_LIB_SYNTAX_ERROR; } } if (optind < argc) { if ('\0' == device_name[0]) { strncpy(device_name, argv[optind], sizeof(device_name) - 1); device_name[sizeof(device_name) - 1] = '\0'; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); usage(); return SMP_LIB_SYNTAX_ERROR; } } if (0 == device_name[0]) { cp = getenv("SMP_UTILS_DEVICE"); if (cp) strncpy(device_name, cp, sizeof(device_name) - 1); else { pr2serr("missing device name on command line\n [Could use " "environment variable SMP_UTILS_DEVICE instead]\n"); usage(); return SMP_LIB_SYNTAX_ERROR; } } if ((cp = strchr(device_name, SMP_SUBVALUE_SEPARATOR))) { *cp = '\0'; if (1 != sscanf(cp + 1, "%d", &subvalue)) { pr2serr("expected number after separator in SMP_DEVICE name\n"); return SMP_LIB_SYNTAX_ERROR; } } if (0 == sa) { cp = getenv("SMP_UTILS_SAS_ADDR"); if (cp) { sa_ll = smp_get_llnum(cp); if (-1LL == sa_ll) { pr2serr("bad value in environment variable " "SMP_UTILS_SAS_ADDR\n use 0\n"); sa_ll = 0; } sa = (uint64_t)sa_ll; } } if (sa > 0) { if (! smp_is_naa5(sa)) { pr2serr("SAS (target) address not in naa-5 format (may need " "leading '0x')\n"); if ('\0' == i_params[0]) { pr2serr(" use '--interface=' to override\n"); return SMP_LIB_SYNTAX_ERROR; } } } res = smp_initiator_open(device_name, subvalue, i_params, sa, &tobj, verbose); if (res < 0) return SMP_LIB_FILE_ERROR; if (! do_zero) { /* SAS-2 or later */ len = (sizeof(smp_resp) - 8) / 4; smp_req[2] = (len < 0x100) ? len : 0xff; /* Allocated Response Len */ smp_req[3] = 2; /* Request Length: in dwords */ } smp_req[9] = phy_id; smp_req[10] = aff_context; if (verbose) { pr2serr(" Report phy SATA request: "); for (k = 0; k < (int)sizeof(smp_req); ++k) pr2serr("%02x ", smp_req[k]); pr2serr("\n"); } memset(&smp_rr, 0, sizeof(smp_rr)); smp_rr.request_len = sizeof(smp_req); smp_rr.request = smp_req; smp_rr.max_response_len = sizeof(smp_resp); smp_rr.response = smp_resp; res = smp_send_req(&tobj, &smp_rr, verbose); if (res) { pr2serr("smp_send_req failed, res=%d\n", res); if (0 == verbose) pr2serr(" try adding '-v' option for more debug\n"); ret = -1; goto err_out; } if (smp_rr.transport_err) { pr2serr("smp_send_req transport_error=%d\n", smp_rr.transport_err); ret = -1; goto err_out; } act_resplen = smp_rr.act_response_len; if ((act_resplen >= 0) && (act_resplen < 4)) { pr2serr("response too short, len=%d\n", act_resplen); ret = SMP_LIB_CAT_MALFORMED; goto err_out; } len = smp_resp[3]; if ((0 == len) && (0 == smp_resp[2])) { len = smp_get_func_def_resp_len(smp_resp[1]); if (len < 0) { len = 0; if (verbose > 0) pr2serr("unable to determine response length\n"); } } len = 4 + (len * 4); /* length in bytes, excluding 4 byte CRC */ if ((act_resplen >= 0) && (len > act_resplen)) { if (verbose) pr2serr("actual response length [%d] less than deduced length " "[%d]\n", act_resplen, len); len = act_resplen; } if (do_hex || do_raw) { if (do_hex) dStrHex((const char *)smp_resp, len, 1); else dStrRaw((const char *)smp_resp, len); if (SMP_FRAME_TYPE_RESP != smp_resp[0]) ret = SMP_LIB_CAT_MALFORMED; else if (smp_resp[1] != smp_req[1]) ret = SMP_LIB_CAT_MALFORMED; else if (smp_resp[2]) { if (verbose) pr2serr("Report phy SATA result: %s\n", smp_get_func_res_str(smp_resp[2], sizeof(b), b)); ret = smp_resp[2]; } goto err_out; } if (SMP_FRAME_TYPE_RESP != smp_resp[0]) { pr2serr("expected SMP frame response type, got=0x%x\n", smp_resp[0]); ret = SMP_LIB_CAT_MALFORMED; goto err_out; } if (smp_resp[1] != smp_req[1]) { pr2serr("Expected function code=0x%x, got=0x%x\n", smp_req[1], smp_resp[1]); ret = SMP_LIB_CAT_MALFORMED; goto err_out; } if (smp_resp[2]) { cp = smp_get_func_res_str(smp_resp[2], sizeof(b), b); pr2serr("Report phy SATA result%s: %s\n", (phy_id_given ? "" : " (for phy_id=0)"), cp); ret = smp_resp[2]; goto err_out; } printf("Report phy SATA response:\n"); res = sg_get_unaligned_be16(smp_resp + 4); if (verbose || (res > 0)) printf(" expander change count: %d\n", res); printf(" phy identifier: %d\n", smp_resp[9]); printf(" STP I_T nexus loss occurred: %d\n", !!(smp_resp[11] & 0x4)); printf(" affiliations supported: %d\n", !!(smp_resp[11] & 0x2)); printf(" affiliation valid: %d\n", !!(smp_resp[11] & 0x1)); printf(" STP SAS address: 0x%" PRIx64 "\n", sg_get_unaligned_be64(smp_resp + 16)); printf(" register device to host FIS:\n "); for (k = 0; k < 20; ++k) printf("%02x ", smp_resp[24 + k]); printf("\n"); printf(" affiliated STP initiator SAS address: 0x%" PRIx64 "\n", sg_get_unaligned_be64(smp_resp + 48)); if (len > 63) printf(" STP I_T nexus loss SAS address: 0x%" PRIx64 "\n", sg_get_unaligned_be64(smp_resp + 56)); if (len > 67) { printf(" affiliation context: %d\n", smp_resp[65]); printf(" current affiliation contexts: %d\n", smp_resp[66]); printf(" maximum affiliation contexts: %d\n", smp_resp[67]); } err_out: res = smp_initiator_close(&tobj); if (res < 0) { pr2serr("close error: %s\n", safe_strerror(errno)); if (0 == ret) return SMP_LIB_FILE_ERROR; } if (ret < 0) ret = SMP_LIB_CAT_OTHER; if (verbose && ret) pr2serr("Exit status %d indicates error detected\n", ret); return ret; }