diff options
Diffstat (limited to 'sbin/camcontrol')
-rw-r--r-- | sbin/camcontrol/Makefile | 4 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.8 | 52 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.c | 232 |
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); |