summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/camcontrol/camcontrol.823
-rw-r--r--sbin/camcontrol/camcontrol.c170
2 files changed, 141 insertions, 52 deletions
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index 0c994e6..b987cad 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 29, 2009
+.Dd September 4, 2009
.Dt CAMCONTROL 8
.Os
.Sh NAME
@@ -120,10 +120,12 @@
.Ic cmd
.Op device id
.Op generic args
+.Aq Fl a Ar cmd Op args
.Aq Fl c Ar cmd Op args
.Op Fl i Ar len Ar fmt
.Bk -words
.Op Fl o Ar len Ar fmt Op args
+.Op Fl r Ar fmt
.Ek
.Nm
.Ic debug
@@ -486,12 +488,14 @@ Saved values
.El
.El
.It Ic cmd
-Allows the user to send an arbitrary SCSI CDB to any device.
+Allows the user to send an arbitrary ATA or SCSI CDB to any device.
The
.Ic cmd
function requires the
.Fl c
-argument to specify the CDB.
+argument to specify SCSI CDB or the
+.Fl a
+argument to specify ATA Command Block registers values.
Other arguments are optional, depending on
the command type.
The command and data specification syntax is documented
@@ -503,9 +507,13 @@ SCSI device in question, you MUST specify either
or
.Fl o .
.Bl -tag -width 17n
+.It Fl a Ar cmd Op args
+This specifies the content of 12 ATA Command Block registers (command,
+features, lba_low, lba_mid, lba_high, device, lba_low_exp, lba_mid_exp.
+lba_high_exp, features_exp, sector_count, sector_count_exp).
.It Fl c Ar cmd Op args
This specifies the SCSI CDB.
-CDBs may be 6, 10, 12 or 16 bytes.
+SCSI CDBs may be 6, 10, 12 or 16 bytes.
.It Fl i Ar len Ar fmt
This specifies the amount of data to read, and how it should be displayed.
If the format is
@@ -519,6 +527,13 @@ If the format is
.Sq - ,
.Ar len
bytes of data will be read from standard input and written to the device.
+.It Fl r Ar fmt
+This specifies that 11 result ATA Command Block registers should be displayed
+(status, error, lba_low, lba_mid, lba_high, device, lba_low_exp, lba_mid_exp,
+lba_high_exp, sector_count, sector_count_exp), and how.
+If the format is
+.Sq - ,
+11 result registers will be written to standard output in hex.
.El
.It Ic debug
Turn on CAM debugging printfs in the kernel.
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index d3450e0..1bba49c 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -120,7 +120,7 @@ struct camcontrol_opts {
};
#ifndef MINIMALISTIC
-static const char scsicmd_opts[] = "c:i:o:";
+static const char scsicmd_opts[] = "a:c:i:o:r";
static const char readdefect_opts[] = "f:GP";
static const char negotiate_opts[] = "acD:O:qR:T:UW:";
#endif
@@ -2078,12 +2078,15 @@ scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
u_int32_t flags = CAM_DIR_NONE;
u_int8_t *data_ptr = NULL;
u_int8_t cdb[20];
+ u_int8_t atacmd[12];
struct get_hook hook;
int c, data_bytes = 0;
int cdb_len = 0;
- char *datastr = NULL, *tstr;
+ int atacmd_len = 0;
+ int need_res = 0;
+ char *datastr = NULL, *tstr, *resstr = NULL;
int error = 0;
- int fd_data = 0;
+ int fd_data = 0, fd_res = 0;
int retval;
ccb = cam_getccb(device);
@@ -2094,10 +2097,32 @@ scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
}
bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ sizeof(union ccb) - sizeof(struct ccb_hdr));
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c) {
+ case 'a':
+ tstr = optarg;
+ while (isspace(*tstr) && (*tstr != '\0'))
+ tstr++;
+ hook.argc = argc - optind;
+ hook.argv = argv + optind;
+ hook.got = 0;
+ atacmd_len = buff_encode_visit(atacmd, sizeof(atacmd), tstr,
+ iget, &hook);
+ /*
+ * Increment optind by the number of arguments the
+ * encoding routine processed. After each call to
+ * getopt(3), optind points to the argument that
+ * getopt should process _next_. In this case,
+ * that means it points to the first command string
+ * argument, if there is one. Once we increment
+ * this, it should point to either the next command
+ * line argument, or it should be past the end of
+ * the list.
+ */
+ optind += hook.got;
+ break;
case 'c':
tstr = optarg;
while (isspace(*tstr) && (*tstr != '\0'))
@@ -2194,6 +2219,16 @@ scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
iget, &hook);
optind += hook.got;
break;
+ case 'r':
+ need_res = 1;
+ hook.argc = argc - optind;
+ hook.argv = argv + optind;
+ hook.got = 0;
+ resstr = cget(&hook, NULL);
+ if ((resstr != NULL) && (resstr[0] == '-'))
+ fd_res = 1;
+ optind += hook.got;
+ break;
default:
break;
}
@@ -2226,50 +2261,51 @@ scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
/* Disable freezing the device queue */
flags |= CAM_DEV_QFRZDIS;
- /*
- * This is taken from the SCSI-3 draft spec.
- * (T10/1157D revision 0.3)
- * The top 3 bits of an opcode are the group code. The next 5 bits
- * are the command code.
- * Group 0: six byte commands
- * Group 1: ten byte commands
- * Group 2: ten byte commands
- * Group 3: reserved
- * Group 4: sixteen byte commands
- * Group 5: twelve byte commands
- * Group 6: vendor specific
- * Group 7: vendor specific
- */
- switch((cdb[0] >> 5) & 0x7) {
- case 0:
- cdb_len = 6;
- break;
- case 1:
- case 2:
- cdb_len = 10;
- break;
- case 3:
- case 6:
- case 7:
- /* computed by buff_encode_visit */
- break;
- case 4:
- cdb_len = 16;
- break;
- case 5:
- cdb_len = 12;
- break;
- }
+ if (cdb_len) {
+ /*
+ * This is taken from the SCSI-3 draft spec.
+ * (T10/1157D revision 0.3)
+ * The top 3 bits of an opcode are the group code.
+ * The next 5 bits are the command code.
+ * Group 0: six byte commands
+ * Group 1: ten byte commands
+ * Group 2: ten byte commands
+ * Group 3: reserved
+ * Group 4: sixteen byte commands
+ * Group 5: twelve byte commands
+ * Group 6: vendor specific
+ * Group 7: vendor specific
+ */
+ switch((cdb[0] >> 5) & 0x7) {
+ case 0:
+ cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ cdb_len = 10;
+ break;
+ case 3:
+ case 6:
+ case 7:
+ /* computed by buff_encode_visit */
+ break;
+ case 4:
+ cdb_len = 16;
+ break;
+ case 5:
+ cdb_len = 12;
+ break;
+ }
- /*
- * We should probably use csio_build_visit or something like that
- * here, but it's easier to encode arguments as you go. The
- * alternative would be skipping the CDB argument and then encoding
- * it here, since we've got the data buffer argument by now.
- */
- bcopy(cdb, &ccb->csio.cdb_io.cdb_bytes, cdb_len);
+ /*
+ * We should probably use csio_build_visit or something like that
+ * here, but it's easier to encode arguments as you go. The
+ * alternative would be skipping the CDB argument and then encoding
+ * it here, since we've got the data buffer argument by now.
+ */
+ bcopy(cdb, &ccb->csio.cdb_io.cdb_bytes, cdb_len);
- cam_fill_csio(&ccb->csio,
+ cam_fill_csio(&ccb->csio,
/*retries*/ retry_count,
/*cbfcnp*/ NULL,
/*flags*/ flags,
@@ -2279,6 +2315,21 @@ scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
/*sense_len*/ SSD_FULL_SIZE,
/*cdb_len*/ cdb_len,
/*timeout*/ timeout ? timeout : 5000);
+ } else {
+ atacmd_len = 12;
+ bcopy(atacmd, &ccb->ataio.cmd.command, atacmd_len);
+ if (need_res)
+ ccb->ataio.cmd.flags |= CAM_ATAIO_NEEDRESULT;
+
+ cam_fill_ataio(&ccb->ataio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*flags*/ flags,
+ /*tag_action*/ 0,
+ /*data_ptr*/ data_ptr,
+ /*dxfer_len*/ data_bytes,
+ /*timeout*/ timeout ? timeout : 5000);
+ }
if (((retval = cam_send_ccb(device, ccb)) < 0)
|| ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
@@ -2296,6 +2347,28 @@ scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
goto scsicmd_bailout;
}
+ if (atacmd_len && need_res) {
+ if (fd_res == 0) {
+ buff_decode_visit(&ccb->ataio.res.status, 11, resstr,
+ arg_put, NULL);
+ fprintf(stdout, "\n");
+ } else {
+ fprintf(stdout,
+ "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ ccb->ataio.res.status,
+ ccb->ataio.res.error,
+ ccb->ataio.res.lba_low,
+ ccb->ataio.res.lba_mid,
+ ccb->ataio.res.lba_high,
+ ccb->ataio.res.device,
+ ccb->ataio.res.lba_low_exp,
+ ccb->ataio.res.lba_mid_exp,
+ ccb->ataio.res.lba_high_exp,
+ ccb->ataio.res.sector_count,
+ ccb->ataio.res.sector_count_exp);
+ fflush(stdout);
+ }
+ }
if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
&& (arglist & CAM_ARG_CMD_IN)
@@ -4029,8 +4102,9 @@ usage(int verbose)
" camcontrol defects [dev_id][generic args] <-f format> [-P][-G]\n"
" camcontrol modepage [dev_id][generic args] <-m page | -l>\n"
" [-P pagectl][-e | -b][-d]\n"
-" camcontrol cmd [dev_id][generic args] <-c cmd [args]>\n"
-" [-i len fmt|-o len fmt [args]]\n"
+" camcontrol cmd [dev_id][generic args]\n"
+" <-a cmd [args] | -c cmd [args]>\n"
+" [-i len fmt|-o len fmt [args]] [-r fmt]\n"
" camcontrol debug [-I][-P][-T][-S][-X][-c]\n"
" <all|bus[:target[:lun]]|off>\n"
" camcontrol tags [dev_id][generic args] [-N tags] [-q] [-v]\n"
OpenPOWER on IntegriCloud