summaryrefslogtreecommitdiffstats
path: root/sbin/camcontrol
diff options
context:
space:
mode:
authorken <ken@FreeBSD.org>2006-08-21 13:24:50 +0000
committerken <ken@FreeBSD.org>2006-08-21 13:24:50 +0000
commitc31cf4b01516585cb63d0b90a8cecf736e3453a3 (patch)
tree16fa26af6dcf7ca80318714e8854e79e7946e6e6 /sbin/camcontrol
parent4bb293498f03b924b64e0987241f63cc81102adc (diff)
downloadFreeBSD-src-c31cf4b01516585cb63d0b90a8cecf736e3453a3.zip
FreeBSD-src-c31cf4b01516585cb63d0b90a8cecf736e3453a3.tar.gz
Implement 'camcontrol reportluns'. This allows users to send the SCSI
REPORT LUNS command to a device. camcontrol.[c8]: Implement reportluns. This tries to print the LUNs out in a reasonable format. Only the periph addressing method has been tested, since very little hardware that I know of supports the other methods. scsi_all.[ch]: Revamp the report luns CDB structure and helper functions. This constitutes a little bit of an API change, but since the old CDB length was 10 bytes, and the REPORT LUNS CDB length is actually 12 bytes, it's clear that no one was using this API in the first place. MFC After: 1 week
Diffstat (limited to 'sbin/camcontrol')
-rw-r--r--sbin/camcontrol/camcontrol.840
-rw-r--r--sbin/camcontrol/camcontrol.c262
2 files changed, 300 insertions, 2 deletions
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index 3923d5a..dcae091 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -1,5 +1,5 @@
.\"
-.\" Copyright (c) 1998, 1999, 2000, 2002, 2005 Kenneth D. Merry.
+.\" Copyright (c) 1998, 1999, 2000, 2002, 2005, 2006 Kenneth D. Merry.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -59,6 +59,13 @@
.Op Fl S
.Op Fl R
.Nm
+.Ic reportluns
+.Op device id
+.Op generic args
+.Op Fl c
+.Op Fl l
+.Op Fl r Ar reporttype
+.Nm
.Ic start
.Op device id
.Op generic args
@@ -266,6 +273,37 @@ This is to aid in script writing.
.It Fl R
Print out transfer rate information.
.El
+.It Ic reportluns
+Send the SCSI REPORT LUNS (0xA0) command to the given device.
+By default,
+.Nm
+will print out the list of logical units (LUNs) supported by the target device.
+There are a couple of options to modify the output:
+.Bl -tag -width 01234567890123
+.It Fl c
+Just print out a count of LUNs, not the actual LUN numbers.
+.It Fl l
+Just print out the LUNs, and don't print out the count.
+.It Fl r Ar reporttype
+Specify the type of report to request from the target:
+.Bl -tag -width 012345678
+.It default
+Return the default report.
+This is the
+.Nm
+default.
+Most targets will support this report if they support the REPORT LUNS
+command.
+.It wellknown
+Return only well known LUNs.
+.It all
+Return all available LUNs.
+.El
+.El
+.Pp
+.Nm
+will try to print out LUN numbers in a reasonable format.
+It can understand the peripheral, flat, LUN and extended LUN formats.
.It Ic start
Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
start bit set.
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index a8447ee..510a33b 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005 Kenneth D. Merry
+ * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2006 Kenneth D. Merry
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -69,6 +69,7 @@ typedef enum {
CAM_CMD_TAG = 0x0000000e,
CAM_CMD_RATE = 0x0000000f,
CAM_CMD_DETACH = 0x00000010,
+ CAM_CMD_REPORTLUNS = 0x00000011
} cam_cmdmask;
typedef enum {
@@ -127,6 +128,7 @@ struct camcontrol_opts option_table[] = {
{"stop", CAM_CMD_STARTSTOP, CAM_ARG_NONE, NULL},
{"load", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT | CAM_ARG_EJECT, NULL},
{"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL},
+ {"reportluns", CAM_CMD_REPORTLUNS, CAM_ARG_NONE, "clr:"},
#endif /* MINIMALISTIC */
{"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL},
{"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL},
@@ -203,6 +205,8 @@ static int ratecontrol(struct cam_device *device, int retry_count,
int timeout, int argc, char **argv, char *combinedopt);
static int scsiformat(struct cam_device *device, int argc, char **argv,
char *combinedopt, int retry_count, int timeout);
+static int scsireportluns(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout);
#endif /* MINIMALISTIC */
camcontrol_optret
@@ -3152,6 +3156,251 @@ scsiformat_bailout:
return(error);
}
+
+static int
+scsireportluns(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ int c, countonly, lunsonly;
+ struct scsi_report_luns_data *lundata;
+ int alloc_len;
+ uint8_t report_type;
+ uint32_t list_len, i, j;
+ int retval;
+
+ retval = 0;
+ lundata = NULL;
+ report_type = RPL_REPORT_DEFAULT;
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("%s: error allocating ccb", __func__);
+ return (1);
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ countonly = 0;
+ lunsonly = 0;
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch (c) {
+ case 'c':
+ countonly++;
+ break;
+ case 'l':
+ lunsonly++;
+ break;
+ case 'r':
+ if (strcasecmp(optarg, "default") == 0)
+ report_type = RPL_REPORT_DEFAULT;
+ else if (strcasecmp(optarg, "wellknown") == 0)
+ report_type = RPL_REPORT_WELLKNOWN;
+ else if (strcasecmp(optarg, "all") == 0)
+ report_type = RPL_REPORT_ALL;
+ else {
+ warnx("%s: invalid report type \"%s\"",
+ __func__, optarg);
+ retval = 1;
+ goto bailout;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((countonly != 0)
+ && (lunsonly != 0)) {
+ warnx("%s: you can only specify one of -c or -l", __func__);
+ retval = 1;
+ goto bailout;
+ }
+ /*
+ * According to SPC-4, the allocation length must be at least 16
+ * bytes -- enough for the header and one LUN.
+ */
+ alloc_len = sizeof(*lundata) + 8;
+
+retry:
+
+ lundata = malloc(alloc_len);
+
+ if (lundata == NULL) {
+ warn("%s: error mallocing %d bytes", __func__, alloc_len);
+ retval = 1;
+ goto bailout;
+ }
+
+ scsi_report_luns(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*select_report*/ report_type,
+ /*rpl_buf*/ lundata,
+ /*alloc_len*/ alloc_len,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ timeout ? timeout : 5000);
+
+ /* Disable freezing the device queue */
+ 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 REPORT LUNS command");
+
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+
+ retval = 1;
+ goto bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
+ retval = 1;
+ goto bailout;
+ }
+
+
+ list_len = scsi_4btoul(lundata->length);
+
+ /*
+ * If we need to list the LUNs, and our allocation
+ * length was too short, reallocate and retry.
+ */
+ if ((countonly == 0)
+ && (list_len > (alloc_len - sizeof(*lundata)))) {
+ alloc_len = list_len + sizeof(*lundata);
+ free(lundata);
+ goto retry;
+ }
+
+ if (lunsonly == 0)
+ fprintf(stdout, "%u LUN%s found\n", list_len / 8,
+ ((list_len / 8) > 1) ? "s" : "");
+
+ if (countonly != 0)
+ goto bailout;
+
+ for (i = 0; i < (list_len / 8); i++) {
+ int no_more;
+
+ no_more = 0;
+ for (j = 0; j < sizeof(lundata->luns[i].lundata); j += 2) {
+ if (j != 0)
+ fprintf(stdout, ",");
+ switch (lundata->luns[i].lundata[j] &
+ RPL_LUNDATA_ATYP_MASK) {
+ case RPL_LUNDATA_ATYP_PERIPH:
+ if ((lundata->luns[i].lundata[j] &
+ RPL_LUNDATA_PERIPH_BUS_MASK) != 0)
+ fprintf(stdout, "%d:",
+ lundata->luns[i].lundata[j] &
+ RPL_LUNDATA_PERIPH_BUS_MASK);
+ else if ((j == 0)
+ && ((lundata->luns[i].lundata[j+2] &
+ RPL_LUNDATA_PERIPH_BUS_MASK) == 0))
+ no_more = 1;
+
+ fprintf(stdout, "%d",
+ lundata->luns[i].lundata[j+1]);
+ break;
+ case RPL_LUNDATA_ATYP_FLAT: {
+ uint8_t tmplun[2];
+ tmplun[0] = lundata->luns[i].lundata[j] &
+ RPL_LUNDATA_FLAT_LUN_MASK;
+ tmplun[1] = lundata->luns[i].lundata[j+1];
+
+ fprintf(stdout, "%d", scsi_2btoul(tmplun));
+ no_more = 1;
+ break;
+ }
+ case RPL_LUNDATA_ATYP_LUN:
+ fprintf(stdout, "%d:%d:%d",
+ (lundata->luns[i].lundata[j+1] &
+ RPL_LUNDATA_LUN_BUS_MASK) >> 5,
+ lundata->luns[i].lundata[j] &
+ RPL_LUNDATA_LUN_TARG_MASK,
+ lundata->luns[i].lundata[j+1] &
+ RPL_LUNDATA_LUN_LUN_MASK);
+ break;
+ case RPL_LUNDATA_ATYP_EXTLUN: {
+ int field_len, field_len_code, eam_code;
+
+ eam_code = lundata->luns[i].lundata[j] &
+ RPL_LUNDATA_EXT_EAM_MASK;
+ field_len_code = (lundata->luns[i].lundata[j] &
+ RPL_LUNDATA_EXT_LEN_MASK) >> 4;
+ field_len = field_len_code * 2;
+
+ if ((eam_code == RPL_LUNDATA_EXT_EAM_WK)
+ && (field_len_code == 0x00)) {
+ fprintf(stdout, "%d",
+ lundata->luns[i].lundata[j+1]);
+ } else if ((eam_code ==
+ RPL_LUNDATA_EXT_EAM_NOT_SPEC)
+ && (field_len_code == 0x03)) {
+ uint8_t tmp_lun[8];
+
+ /*
+ * This format takes up all 8 bytes.
+ * If we aren't starting at offset 0,
+ * that's a bug.
+ */
+ if (j != 0) {
+ fprintf(stdout, "Invalid "
+ "offset %d for "
+ "Extended LUN not "
+ "specified format", j);
+ no_more = 1;
+ break;
+ }
+ bzero(tmp_lun, sizeof(tmp_lun));
+ bcopy(&lundata->luns[i].lundata[j+1],
+ &tmp_lun[1], sizeof(tmp_lun) - 1);
+ fprintf(stdout, "%#jx",
+ (intmax_t)scsi_8btou64(tmp_lun));
+ no_more = 1;
+ } else {
+ fprintf(stderr, "Unknown Extended LUN"
+ "Address method %#x, length "
+ "code %#x", eam_code,
+ field_len_code);
+ no_more = 1;
+ }
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown LUN address method "
+ "%#x\n", lundata->luns[i].lundata[0] &
+ RPL_LUNDATA_ATYP_MASK);
+ break;
+ }
+ /*
+ * For the flat addressing method, there are no
+ * other levels after it.
+ */
+ if (no_more != 0)
+ break;
+ }
+ fprintf(stdout, "\n");
+ }
+
+bailout:
+
+ cam_freeccb(ccb);
+
+ free(lundata);
+
+ return (retval);
+}
+
#endif /* MINIMALISTIC */
void
@@ -3164,6 +3413,7 @@ usage(int verbose)
" camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n"
" camcontrol tur [dev_id][generic args]\n"
" camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n"
+" camcontrol reportluns [dev_id][generic args] [-c] [-l] [-r report]\n"
" camcontrol start [dev_id][generic args]\n"
" camcontrol stop [dev_id][generic args]\n"
" camcontrol load [dev_id][generic args]\n"
@@ -3196,6 +3446,7 @@ usage(int verbose)
"periphlist list all CAM peripheral drivers attached to a device\n"
"tur send a test unit ready to the named device\n"
"inquiry send a SCSI inquiry command to the named device\n"
+"reportluns send a SCSI report luns command to the device\n"
"start send a Start Unit command to the device\n"
"stop send a Stop Unit command to the device\n"
"load send a Start Unit command to the device with the load bit set\n"
@@ -3236,6 +3487,10 @@ usage(int verbose)
"-D get the standard inquiry data\n"
"-S get the serial number\n"
"-R get the transfer rate, etc.\n"
+"reportluns arguments:\n"
+"-c only report a count of available LUNs\n"
+"-l only print out luns, and not a count\n"
+"-r <reporttype> specify \"default\", \"wellknown\" or \"all\"\n"
"cmd arguments:\n"
"-c cdb [args] specify the SCSI CDB\n"
"-i len fmt specify input data and input data format\n"
@@ -3547,6 +3802,11 @@ main(int argc, char **argv)
error = scsiformat(cam_dev, argc, argv,
combinedopt, retry_count, timeout);
break;
+ case CAM_CMD_REPORTLUNS:
+ error = scsireportluns(cam_dev, argc, argv,
+ combinedopt, retry_count,
+ timeout);
+ break;
#endif /* MINIMALISTIC */
case CAM_CMD_USAGE:
usage(1);
OpenPOWER on IntegriCloud