summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/camcontrol/camcontrol.840
-rw-r--r--sbin/camcontrol/camcontrol.c262
-rw-r--r--sys/cam/scsi/scsi_all.c8
-rw-r--r--sys/cam/scsi/scsi_all.h42
4 files changed, 335 insertions, 17 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);
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c
index 4c51148..adaef35 100644
--- a/sys/cam/scsi/scsi_all.c
+++ b/sys/cam/scsi/scsi_all.c
@@ -2749,8 +2749,9 @@ scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
void
scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
- u_int8_t tag_action, struct scsi_report_luns_data *rpl_buf,
- u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout)
+ u_int8_t tag_action, u_int8_t select_report,
+ struct scsi_report_luns_data *rpl_buf, u_int32_t alloc_len,
+ u_int8_t sense_len, u_int32_t timeout)
{
struct scsi_report_luns *scsi_cmd;
@@ -2767,7 +2768,8 @@ scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries,
scsi_cmd = (struct scsi_report_luns *)&csio->cdb_io.cdb_bytes;
bzero(scsi_cmd, sizeof(*scsi_cmd));
scsi_cmd->opcode = REPORT_LUNS;
- scsi_ulto4b(alloc_len, scsi_cmd->addr);
+ scsi_cmd->select_report = select_report;
+ scsi_ulto4b(alloc_len, scsi_cmd->length);
}
/*
diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h
index 33b26de..8d320d9 100644
--- a/sys/cam/scsi/scsi_all.h
+++ b/sys/cam/scsi/scsi_all.h
@@ -708,11 +708,16 @@ struct scsi_read_capacity_data_long
struct scsi_report_luns
{
- u_int8_t opcode;
- u_int8_t byte2;
- u_int8_t unused[3];
- u_int8_t addr[4];
- u_int8_t control;
+ uint8_t opcode;
+ uint8_t reserved1;
+#define RPL_REPORT_DEFAULT 0x00
+#define RPL_REPORT_WELLKNOWN 0x01
+#define RPL_REPORT_ALL 0x02
+ uint8_t select_report;
+ uint8_t reserved2[3];
+ uint8_t length[4];
+ uint8_t reserved3;
+ uint8_t control;
};
struct scsi_report_luns_data {
@@ -723,10 +728,22 @@ struct scsi_report_luns_data {
*/
struct {
u_int8_t lundata[8];
- } luns[1];
+ } luns[0];
};
+#define RPL_LUNDATA_PERIPH_BUS_MASK 0x3f
+#define RPL_LUNDATA_FLAT_LUN_MASK 0x3f
+#define RPL_LUNDATA_LUN_TARG_MASK 0x3f
+#define RPL_LUNDATA_LUN_BUS_MASK 0xe0
+#define RPL_LUNDATA_LUN_LUN_MASK 0x1f
+#define RPL_LUNDATA_EXT_LEN_MASK 0x30
+#define RPL_LUNDATA_EXT_EAM_MASK 0x0f
+#define RPL_LUNDATA_EXT_EAM_WK 0x01
+#define RPL_LUNDATA_EXT_EAM_NOT_SPEC 0x0f
#define RPL_LUNDATA_ATYP_MASK 0xc0 /* MBZ for type 0 lun */
-#define RPL_LUNDATA_T0LUN 1 /* @ lundata[1] */
+#define RPL_LUNDATA_ATYP_PERIPH 0x00
+#define RPL_LUNDATA_ATYP_FLAT 0x40
+#define RPL_LUNDATA_ATYP_LUN 0x80
+#define RPL_LUNDATA_ATYP_EXTLUN 0xc0
struct scsi_sense_data
@@ -1035,11 +1052,12 @@ void scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
uint32_t timeout);
void scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries,
- void (*cbfcnp)(struct cam_periph *,
- union ccb *), u_int8_t tag_action,
- struct scsi_report_luns_data *,
- u_int32_t alloc_len, u_int8_t sense_len,
- u_int32_t timeout);
+ void (*cbfcnp)(struct cam_periph *,
+ union ccb *), u_int8_t tag_action,
+ u_int8_t select_report,
+ struct scsi_report_luns_data *rpl_buf,
+ u_int32_t alloc_len, u_int8_t sense_len,
+ u_int32_t timeout);
void scsi_synchronize_cache(struct ccb_scsiio *csio,
u_int32_t retries,
OpenPOWER on IntegriCloud