aboutsummaryrefslogtreecommitdiff
path: root/sbin/camcontrol/camcontrol.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/camcontrol/camcontrol.c')
-rw-r--r--sbin/camcontrol/camcontrol.c189
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);