summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/camcontrol/Makefile4
-rw-r--r--sbin/camcontrol/camcontrol.852
-rw-r--r--sbin/camcontrol/camcontrol.c232
3 files changed, 283 insertions, 5 deletions
diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile
index bf2ba4a..7a3d421 100644
--- a/sbin/camcontrol/Makefile
+++ b/sbin/camcontrol/Makefile
@@ -12,8 +12,8 @@ WARNS?= 3
.else
WARNS?= 6
.endif
-DPADD= ${LIBCAM} ${LIBSBUF}
-LDADD= -lcam -lsbuf
+DPADD= ${LIBCAM} ${LIBSBUF} ${LIBUTIL}
+LDADD= -lcam -lsbuf -lutil
MAN= camcontrol.8
.include <bsd.prog.mk>
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index c6691fc..7925e94 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -1,5 +1,5 @@
.\"
-.\" Copyright (c) 1998, 1999, 2000, 2002, 2005, 2006 Kenneth D. Merry.
+.\" Copyright (c) 1998, 1999, 2000, 2002, 2005, 2006, 2007 Kenneth D. Merry.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -66,6 +66,16 @@
.Op Fl l
.Op Fl r Ar reporttype
.Nm
+.Ic readcap
+.Op device id
+.Op generic args
+.Op Fl b
+.Op Fl h
+.Op Fl H
+.Op Fl N
+.Op Fl q
+.Op Fl s
+.Nm
.Ic start
.Op device id
.Op generic args
@@ -304,6 +314,46 @@ Return all available LUNs.
.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 readcap
+Send the SCSI READ CAPACITY command to the given device and display
+the results.
+If the device is larger than 2TB, the SCSI READ CAPACITY (16) service
+action will be sent to obtain the full size of the device.
+By default,
+.Nm
+will print out the last logical block of the device, and the blocksize of
+the device in bytes.
+To modify the output format, use the following options:
+.Bl -tag -width 5n
+.It Fl b
+Just print out the blocksize, not the last block or device size.
+This cannot be used with
+.Fl N
+or
+.Fl s .
+.It Fl h
+Print out the device size in human readable (base 2, 1K == 1024) format.
+This implies
+.Fl N
+and cannot be used with
+.Fl q
+or
+.Fl b .
+.It Fl H
+Print out the device size in human readable (base 10, 1K == 1000) format.
+.It Fl N
+Print out the number of blocks in the device instead of the last logical
+block.
+.It Fl q
+Quiet, print out the numbers only (separated by a comma if
+.Fl b
+or
+.Fl s
+are not specified).
+.It Fl s
+Print out the last logical block or the size of the device only, and omit
+the blocksize.
+.El
.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 87dc030..9eab41c 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2006 Kenneth D. Merry
+ * Copyright (c) 1997-2007 Kenneth D. Merry
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <fcntl.h>
#include <ctype.h>
#include <err.h>
+#include <libutil.h>
#include <cam/cam.h>
#include <cam/cam_debug.h>
@@ -69,7 +70,8 @@ typedef enum {
CAM_CMD_TAG = 0x0000000e,
CAM_CMD_RATE = 0x0000000f,
CAM_CMD_DETACH = 0x00000010,
- CAM_CMD_REPORTLUNS = 0x00000011
+ CAM_CMD_REPORTLUNS = 0x00000011,
+ CAM_CMD_READCAP = 0x00000012
} cam_cmdmask;
typedef enum {
@@ -129,6 +131,7 @@ struct camcontrol_opts option_table[] = {
{"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:"},
+ {"readcapacity", CAM_CMD_READCAP, CAM_ARG_NONE, "bhHNqs"},
#endif /* MINIMALISTIC */
{"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL},
{"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL},
@@ -207,6 +210,8 @@ 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);
+static int scsireadcapacity(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout);
#endif /* MINIMALISTIC */
camcontrol_optret
@@ -3450,6 +3455,214 @@ bailout:
return (retval);
}
+static int
+scsireadcapacity(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ int blocksizeonly, humanize, numblocks, quiet, sizeonly, baseten;
+ struct scsi_read_capacity_data rcap;
+ struct scsi_read_capacity_data_long rcaplong;
+ uint64_t maxsector;
+ uint32_t block_len;
+ int retval;
+ int c;
+
+ blocksizeonly = 0;
+ humanize = 0;
+ numblocks = 0;
+ quiet = 0;
+ sizeonly = 0;
+ baseten = 0;
+ retval = 0;
+
+ 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));
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch (c) {
+ case 'b':
+ blocksizeonly++;
+ break;
+ case 'h':
+ humanize++;
+ baseten = 0;
+ break;
+ case 'H':
+ humanize++;
+ baseten++;
+ break;
+ case 'N':
+ numblocks++;
+ break;
+ case 'q':
+ quiet++;
+ break;
+ case 's':
+ sizeonly++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((blocksizeonly != 0)
+ && (numblocks != 0)) {
+ warnx("%s: you can only specify one of -b or -N", __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ if ((blocksizeonly != 0)
+ && (sizeonly != 0)) {
+ warnx("%s: you can only specify one of -b or -s", __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ if ((humanize != 0)
+ && (quiet != 0)) {
+ warnx("%s: you can only specify one of -h/-H or -q", __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ if ((humanize != 0)
+ && (blocksizeonly != 0)) {
+ warnx("%s: you can only specify one of -h/-H or -b", __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ scsi_read_capacity(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ &rcap,
+ 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 READ CAPACITY 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;
+ }
+
+ maxsector = scsi_4btoul(rcap.addr);
+ block_len = scsi_4btoul(rcap.length);
+
+ /*
+ * A last block of 2^32-1 means that the true capacity is over 2TB,
+ * and we need to issue the long READ CAPACITY to get the real
+ * capacity. Otherwise, we're all set.
+ */
+ if (maxsector != 0xffffffff)
+ goto do_print;
+
+ scsi_read_capacity_16(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*lba*/ 0,
+ /*reladdr*/ 0,
+ /*pmi*/ 0,
+ &rcaplong,
+ /*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 READ CAPACITY (16) 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;
+ }
+
+ maxsector = scsi_8btou64(rcaplong.addr);
+ block_len = scsi_4btoul(rcaplong.length);
+
+do_print:
+ if (blocksizeonly == 0) {
+ /*
+ * Humanize implies !quiet, and also implies numblocks.
+ */
+ if (humanize != 0) {
+ char tmpstr[6];
+ int64_t tmpbytes;
+ int ret;
+
+ tmpbytes = (maxsector + 1) * block_len;
+ ret = humanize_number(tmpstr, sizeof(tmpstr),
+ tmpbytes, "", HN_AUTOSCALE,
+ HN_B | HN_DECIMAL |
+ ((baseten != 0) ?
+ HN_DIVISOR_1000 : 0));
+ if (ret == -1) {
+ warnx("%s: humanize_number failed!", __func__);
+ retval = 1;
+ goto bailout;
+ }
+ fprintf(stdout, "Device Size: %s%s", tmpstr,
+ (sizeonly == 0) ? ", " : "\n");
+ } else if (numblocks != 0) {
+ fprintf(stdout, "%s%ju%s", (quiet == 0) ?
+ "Blocks: " : "", (uintmax_t)maxsector + 1,
+ (sizeonly == 0) ? ", " : "\n");
+ } else {
+ fprintf(stdout, "%s%ju%s", (quiet == 0) ?
+ "Last Block: " : "", (uintmax_t)maxsector,
+ (sizeonly == 0) ? ", " : "\n");
+ }
+ }
+ if (sizeonly == 0)
+ fprintf(stdout, "%s%u%s\n", (quiet == 0) ?
+ "Block Length: " : "", block_len, (quiet == 0) ?
+ " bytes" : "");
+bailout:
+ cam_freeccb(ccb);
+
+ return (retval);
+}
+
#endif /* MINIMALISTIC */
void
@@ -3463,6 +3676,8 @@ usage(int verbose)
" 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 readcap [dev_id][generic args] [-b] [-h] [-H] [-N]\n"
+" [-q] [-s]\n"
" camcontrol start [dev_id][generic args]\n"
" camcontrol stop [dev_id][generic args]\n"
" camcontrol load [dev_id][generic args]\n"
@@ -3496,6 +3711,7 @@ usage(int verbose)
"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"
+"readcap send a SCSI read capacity 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"
@@ -3540,6 +3756,13 @@ usage(int verbose)
"-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"
+"readcap arguments\n"
+"-b only report the blocksize\n"
+"-h human readable device size, base 2\n"
+"-H human readable device size, base 10\n"
+"-N print the number of blocks instead of last block\n"
+"-q quiet, print numbers only\n"
+"-s only report the last block/device size\n"
"cmd arguments:\n"
"-c cdb [args] specify the SCSI CDB\n"
"-i len fmt specify input data and input data format\n"
@@ -3856,6 +4079,11 @@ main(int argc, char **argv)
combinedopt, retry_count,
timeout);
break;
+ case CAM_CMD_READCAP:
+ error = scsireadcapacity(cam_dev, argc, argv,
+ combinedopt, retry_count,
+ timeout);
+ break;
#endif /* MINIMALISTIC */
case CAM_CMD_USAGE:
usage(1);
OpenPOWER on IntegriCloud