diff options
Diffstat (limited to 'sbin/camcontrol/camcontrol.c')
-rw-r--r-- | sbin/camcontrol/camcontrol.c | 189 |
1 files changed, 163 insertions, 26 deletions
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 9417943afcb3..15a5d42a2ba5 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -44,6 +44,7 @@ #include <fcntl.h> #include <ctype.h> #include <err.h> +#include <libnvmf.h> #include <libutil.h> #include <limits.h> #include <inttypes.h> @@ -60,9 +61,7 @@ #include <cam/mmc/mmc_all.h> #include <camlib.h> #include "camcontrol.h" -#ifdef WITH_NVME #include "nvmecontrol_ext.h" -#endif typedef enum { CAM_CMD_NONE, @@ -111,6 +110,7 @@ typedef enum { CAM_CMD_DEVTYPE, CAM_CMD_AMA, CAM_CMD_DEPOP, + CAM_CMD_REQSENSE } cam_cmd; typedef enum { @@ -233,6 +233,7 @@ static struct camcontrol_opts option_table[] = { {"epc", CAM_CMD_EPC, CAM_ARG_NONE, "c:dDeHp:Pr:sS:T:"}, {"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"}, {"depop", CAM_CMD_DEPOP, CAM_ARG_NONE, "ac:de:ls"}, + {"sense", CAM_CMD_REQSENSE, CAM_ARG_NONE, "Dx"}, {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, @@ -276,9 +277,10 @@ static int print_dev_ata(struct device_match_result *dev_result, char *tmpstr); static int print_dev_semb(struct device_match_result *dev_result, char *tmpstr); static int print_dev_mmcsd(struct device_match_result *dev_result, char *tmpstr); -#ifdef WITH_NVME static int print_dev_nvme(struct device_match_result *dev_result, char *tmpstr); -#endif +static int requestsense(struct cam_device *device, int argc, char **argv, + char *combinedopt, int task_attr, int retry_count, + int timeout); static int testunitready(struct cam_device *device, int task_attr, int retry_count, int timeout, int quiet); static int scsistart(struct cam_device *device, int startstop, int loadeject, @@ -595,14 +597,12 @@ getdevtree(int argc, char **argv, char *combinedopt) skip_device = 1; break; } -#ifdef WITH_NVME } else if (dev_result->protocol == PROTO_NVME) { if (print_dev_nvme(dev_result, &tmpstr[0]) != 0) { skip_device = 1; break; } -#endif } else { sprintf(tmpstr, "<>"); } @@ -653,6 +653,7 @@ getdevtree(int argc, char **argv, char *combinedopt) if (need_close) fprintf(stdout, ")\n"); + free(ccb.cdm.matches); close(fd); return (error); @@ -776,7 +777,6 @@ print_dev_mmcsd(struct device_match_result *dev_result, char *tmpstr) return (0); } -#ifdef WITH_NVME static int nvme_get_cdata(struct cam_device *dev, struct nvme_controller_data *cdata) { @@ -838,7 +838,114 @@ print_dev_nvme(struct device_match_result *dev_result, char *tmpstr) cam_close_device(dev); return (0); } -#endif + +static int +requestsense(struct cam_device *device, int argc, char **argv, + char *combinedopt, int task_attr, int retry_count, int timeout) +{ + int c; + int descriptor_sense = 0; + int do_hexdump = 0; + struct scsi_sense_data sense; + union ccb *ccb = NULL; + int error = 0; + size_t returned_bytes; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'D': + descriptor_sense = 1; + break; + case 'x': + do_hexdump = 1; + break; + default: + break; + } + } + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("couldn't allocate CCB"); + return (1); + } + + /* cam_getccb cleans up the header, caller has to zero the payload */ + CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); + + bzero(&sense, sizeof(sense)); + + scsi_request_sense(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*data_ptr*/ (void *)&sense, + /*dxfer_len*/ sizeof(sense), + /*tag_action*/ task_attr, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 60000); + + if (descriptor_sense != 0) { + struct scsi_request_sense *cdb; + + cdb = (struct scsi_request_sense *)&ccb->csio.cdb_io.cdb_bytes; + cdb->byte2 |= SRS_DESC; + } + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (arglist & CAM_ARG_ERR_RECOVER) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + if (cam_send_ccb(device, ccb) < 0) { + warn("error sending REQUEST SENSE command"); + cam_freeccb(ccb); + error = 1; + goto bailout; + } + + /* + * REQUEST SENSE is not generally supposed to fail. But there can + * be transport or other errors that might cause it to fail. It + * may also fail if the user asks for descriptor sense and the + * device doesn't support it. So we check the CCB status here to see. + */ + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + warnx("REQUEST SENSE failed"); + cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + error = 1; + goto bailout; + } + + returned_bytes = ccb->csio.dxfer_len - ccb->csio.resid; + + if (do_hexdump != 0) { + hexdump(&sense, returned_bytes, NULL, 0); + } else { + char path_str[80]; + struct sbuf *sb; + + cam_path_string(device, path_str, sizeof(path_str)); + sb = sbuf_new_auto(); + if (sb == NULL) { + warnx("%s: cannot allocate sbuf", __func__); + error = 1; + goto bailout; + } + + scsi_sense_only_sbuf(&sense, returned_bytes, sb, path_str, + &device->inq_data, scsiio_cdb_ptr(&ccb->csio), + ccb->csio.cdb_len); + + sbuf_finish(sb); + printf("%s", sbuf_data(sb)); + sbuf_delete(sb); + } +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + return (error); +} static int testunitready(struct cam_device *device, int task_attr, int retry_count, @@ -1995,7 +2102,7 @@ ata_read_native_max(struct cam_device *device, int retry_count, /*sector_count*/0, /*data_ptr*/NULL, /*dxfer_len*/0, - timeout ? timeout : 5000, + timeout ? timeout : 10 * 1000, is48bit); if (error) @@ -2376,7 +2483,6 @@ ataidentify(struct cam_device *device, int retry_count, int timeout) return (0); } -#ifdef WITH_NVME static int nvmeidentify(struct cam_device *device, int retry_count __unused, int timeout __unused) { @@ -2388,12 +2494,10 @@ nvmeidentify(struct cam_device *device, int retry_count __unused, int timeout __ return (0); } -#endif static int identify(struct cam_device *device, int retry_count, int timeout) { -#ifdef WITH_NVME struct ccb_pathinq cpi; if (get_cpi(device, &cpi) != 0) { @@ -2404,7 +2508,6 @@ identify(struct cam_device *device, int retry_count, int timeout) if (cpi.protocol == PROTO_NVME) { return (nvmeidentify(device, retry_count, timeout)); } -#endif return (ataidentify(device, retry_count, timeout)); } @@ -5277,6 +5380,39 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts) sata->caps); } } + if (cts->transport == XPORT_NVME) { + struct ccb_trans_settings_nvme *nvme = + &cts->xport_specific.nvme; + + if (nvme->valid & CTS_NVME_VALID_LINK) { + fprintf(stdout, "%sPCIe lanes: %d (%d max)\n", pathstr, + nvme->lanes, nvme->max_lanes); + fprintf(stdout, "%sPCIe Generation: %d (%d max)\n", pathstr, + nvme->speed, nvme->max_speed); + } + } + if (cts->transport == XPORT_NVMF) { + struct ccb_trans_settings_nvmf *nvmf = + &cts->xport_specific.nvmf; + + if (nvmf->valid & CTS_NVMF_VALID_TRTYPE) { + fprintf(stdout, "%sTransport: %s\n", pathstr, + nvmf_transport_type(nvmf->trtype)); + } + } + if (cts->transport == XPORT_UFSHCI) { + struct ccb_trans_settings_ufshci *ufshci = + &cts->xport_specific.ufshci; + + if (ufshci->valid & CTS_UFSHCI_VALID_LINK) { + fprintf(stdout, "%sHigh Speed Gear: %d (%d max)\n", + pathstr, ufshci->hs_gear, ufshci->max_hs_gear); + fprintf(stdout, "%sUnipro TX lanes: %d (%d max)\n", pathstr, + ufshci->tx_lanes, ufshci->max_tx_lanes); + fprintf(stdout, "%sUnipro RX lanes: %d (%d max)\n", pathstr, + ufshci->rx_lanes, ufshci->max_rx_lanes); + } + } if (cts->protocol == PROTO_ATA) { struct ccb_trans_settings_ata *ata= &cts->proto_specific.ata; @@ -5297,24 +5433,16 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts) "enabled" : "disabled"); } } -#ifdef WITH_NVME if (cts->protocol == PROTO_NVME) { - struct ccb_trans_settings_nvme *nvmex = - &cts->xport_specific.nvme; + struct ccb_trans_settings_nvme *nvme = + &cts->proto_specific.nvme; - if (nvmex->valid & CTS_NVME_VALID_SPEC) { + if (nvme->valid & CTS_NVME_VALID_SPEC) { fprintf(stdout, "%sNVMe Spec: %d.%d\n", pathstr, - NVME_MAJOR(nvmex->spec), - NVME_MINOR(nvmex->spec)); - } - if (nvmex->valid & CTS_NVME_VALID_LINK) { - fprintf(stdout, "%sPCIe lanes: %d (%d max)\n", pathstr, - nvmex->lanes, nvmex->max_lanes); - fprintf(stdout, "%sPCIe Generation: %d (%d max)\n", pathstr, - nvmex->speed, nvmex->max_speed); + NVME_MAJOR(nvme->spec), + NVME_MINOR(nvme->spec)); } } -#endif } /* @@ -9869,6 +9997,7 @@ usage(int printlong) " camcontrol devlist [-b] [-v]\n" " camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n" " camcontrol tur [dev_id][generic args]\n" +" camcontrol sense [dev_id][generic args][-D][-x]\n" " camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n" " camcontrol identify [dev_id][generic args] [-v]\n" " camcontrol reportluns [dev_id][generic args] [-c] [-l] [-r report]\n" @@ -9957,6 +10086,7 @@ usage(int printlong) "Specify one of the following options:\n" "devlist list all CAM devices\n" "periphlist list all CAM peripheral drivers attached to a device\n" +"sense send a request sense command to the named device\n" "tur send a test unit ready to the named device\n" "inquiry send a SCSI inquiry command to the named device\n" "identify send a ATA identify command to the named device\n" @@ -10021,6 +10151,9 @@ usage(int printlong) "-f format specify defect list format (block, bfi or phys)\n" "-G get the grown defect list\n" "-P get the permanent defect list\n" +"sense arguments:\n" +"-D request descriptor sense data\n" +"-x do a hexdump of the sense data\n" "inquiry arguments:\n" "-D get the standard inquiry data\n" "-S get the serial number\n" @@ -10491,6 +10624,10 @@ main(int argc, char **argv) case CAM_CMD_DEVTYPE: error = getdevtype(cam_dev); break; + case CAM_CMD_REQSENSE: + error = requestsense(cam_dev, argc, argv, combinedopt, + task_attr, retry_count, timeout); + break; case CAM_CMD_TUR: error = testunitready(cam_dev, task_attr, retry_count, timeout, 0); |