svn commit: r196831 - head/sbin/camcontrol

Alexander Motin mav at FreeBSD.org
Fri Sep 4 18:21:41 UTC 2009


Author: mav
Date: Fri Sep  4 18:21:40 2009
New Revision: 196831
URL: http://svn.freebsd.org/changeset/base/196831

Log:
  Add to `camcontrol cmd` support for sending arbitrary ATA commands.
  
  It could be used for broad range of tasks, such as configuring drive
  power management modes, caching, security and any other features and tasks,
  not supported by existing drivers.

Modified:
  head/sbin/camcontrol/camcontrol.8
  head/sbin/camcontrol/camcontrol.c

Modified: head/sbin/camcontrol/camcontrol.8
==============================================================================
--- head/sbin/camcontrol/camcontrol.8	Fri Sep  4 16:41:17 2009	(r196830)
+++ head/sbin/camcontrol/camcontrol.8	Fri Sep  4 18:21:40 2009	(r196831)
@@ -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 specif
 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.

Modified: head/sbin/camcontrol/camcontrol.c
==============================================================================
--- head/sbin/camcontrol/camcontrol.c	Fri Sep  4 16:41:17 2009	(r196830)
+++ head/sbin/camcontrol/camcontrol.c	Fri Sep  4 18:21:40 2009	(r196831)
@@ -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 a
 	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 a
 	}
 
 	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 a
 						  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 a
 	/* 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 a
 		      /*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 a
 		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"


More information about the svn-src-head mailing list