summaryrefslogtreecommitdiffstats
path: root/sbin/camcontrol/camcontrol.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/camcontrol/camcontrol.c')
-rw-r--r--sbin/camcontrol/camcontrol.c262
1 files changed, 261 insertions, 1 deletions
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