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.c232
1 files changed, 230 insertions, 2 deletions
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