SCSI descriptor sense changes, testing needed

Kenneth D. Merry ken at freebsd.org
Sat Oct 1 05:19:44 UTC 2011


I have attached a new version of the patches, with a number of changes.

One issue that has cropped up is that the previous sense code and my new
descriptor sense changes never paid any attention to the actual length of
the sense data returned by the controller.

I have changed all of the error recovery code and sense printing code to
honor the sense data length in the CAM CCB.

One other problem related to that is that many controller drivers don't set
the sense residual field in struct ccb_scsiio properly, or don't set it at
all.  This patch includes changes to the isp, mps, mpt, umass, and ciss
drivers to set the sense_resid field properly.

There are lots of other drivers in the system, however, that haven't been
audited, and may or may not set the sense residual correctly.

I also fixed an issue reported by Fabian Keil that showed up with the ahci
driver.  In reverting a change I have in my local tree to switch to a 2
byte length field in the SCSI inquiry CDB, I accidently shortened the CDB
to 5 bytes.  Oops.

I'd really appreciate more feedback; Fabian is the only person to report
testing the previous patch.

Thanks,

Ken

On Thu, Sep 22, 2011 at 13:33:05 -0600, Kenneth D. Merry wrote:
> 
> I have attached a set of patches against head that implement SCSI
> descriptor sense support for CAM.
> 
> Descriptor sense is a new sense (SCSI error) format introduced in the SPC-3
> spec in 2006.  FreeBSD doesn't currently support it.
> 
> Seagate's new 3TB SAS drives come with descriptor sense enabled by default,
> and it's possible that other newer drives do as well.  Because all the
> sense key, additional sense code, and additional sense code qualifier
> fields are in different places, the CAM error recovery code will not do the
> right thing when it gets descriptor sense.
> 
> These patches do bump up the size of struct scsi_sense_data, and so I have
> incremented CAM_VERSION as well.  I have discussed this with re@, and it
> looks like we'll be putting the changes in before 9.0, so it ships with
> support for newer SCSI devices.
> 
> A number of things have changed in these patches, but in particular, it
> would be good to test the following:
> 
>  - The sa(4) (SCSI tape) driver.  The residual handling code, which looks
>    at the sense data, has changed.
>  - The Playstation 3 CDROM driver.
>  - Firewire target mode.
>  - umass devices with the NO_INQUIRY_EVPD quirk.
> 
> Also, please let me know if you see any anomalies with the sense printing
> code.  In the common cases the output should look identical to the old
> code, but in some cases it will be a little different.  e.g.:
> 
> # camcontrol inquiry da40 -v
> pass47: <SEAGATE ST33000650SS 0002> Fixed Direct Access SCSI-6 device
> pass47: Serial Number 9XK0GAJ70000S125XDNU
> pass47: 300.000MB/s transfers, Command Queueing Enabled
> 
> (Seagate 3TB drive)
> 
> # camcontrol modepage da40 -m 10 |grep D_SENSE
> D_SENSE:  1
> 
> (Descriptor sense is enabled)
> 
> # camcontrol modepage da40 -m 15 -v
> (pass47:mps1:0:47:0): MODE SENSE(6). CDB: 1a 0 4f 0 ff 0 
> (pass47:mps1:0:47:0): CAM status: SCSI Status Error
> (pass47:mps1:0:47:0): SCSI status: Check Condition
> (pass47:mps1:0:47:0): SCSI sense: ILLEGAL REQUEST asc:24,0 (Invalid field in CDB)
> (pass47:mps1:0:47:0): Field Replaceable Unit: 1
> (pass47:mps1:0:47:0): Command byte 2 bit 5 is invalid
> (pass47:mps1:0:47:0): Descriptor 0x80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> camcontrol: error sending mode sense command
> 
> (The FRU and Sense Key Specific entries are on separate lines, and a
> vendor-specific sense descriptor is printed out in hex format.)
> 
> Anyway, I'd appreciate any testing and feedback on these changes.  As I
> said, they will probably be in 9.0, so if there are any issues it would be
> better to find them now. :)
> 
> Thanks,
> 
> Ken
> -- 
> Kenneth Merry
> ken at FreeBSD.ORG
-- 
Kenneth Merry
ken at FreeBSD.ORG
-------------- next part --------------
Index: sbin/camcontrol/camcontrol.c
===================================================================
--- sbin/camcontrol/camcontrol.c	(revision 225891)
+++ sbin/camcontrol/camcontrol.c	(working copy)
@@ -1907,7 +1907,9 @@ readdefects(struct cam_device *device, int argc, c
 		int error_code, sense_key, asc, ascq;
 
 		sense = &ccb->csio.sense_data;
-		scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);
+		scsi_extract_sense_len(sense, ccb->csio.sense_len -
+		    ccb->csio.sense_resid, &error_code, &sense_key, &asc,
+		    &ascq, /*show_errors*/ 1);
 
 		/*
 		 * According to the SCSI spec, if the disk doesn't support
@@ -3798,8 +3800,9 @@ doreport:
 			int error_code, sense_key, asc, ascq;
 
 			sense = &ccb->csio.sense_data;
-			scsi_extract_sense(sense, &error_code, &sense_key,
-					   &asc, &ascq);
+			scsi_extract_sense_len(sense, ccb->csio.sense_len -
+			    ccb->csio.sense_resid, &error_code, &sense_key,
+			    &asc, &ascq, /*show_errors*/ 1);
 
 			/*
 			 * According to the SCSI-2 and SCSI-3 specs, a
@@ -3810,15 +3813,15 @@ doreport:
 			 */
 			if ((sense_key == SSD_KEY_NOT_READY)
 			 && (asc == 0x04) && (ascq == 0x04)) {
-				if ((sense->extra_len >= 10)
-				 && ((sense->sense_key_spec[0] &
-				      SSD_SCS_VALID) != 0)
+				uint8_t sks[3];
+
+				if ((scsi_get_sks(sense, ccb->csio.sense_len -
+				     ccb->csio.sense_resid, sks) == 0)
 				 && (quiet == 0)) {
 					int val;
 					u_int64_t percentage;
 
-					val = scsi_2btoul(
-						&sense->sense_key_spec[1]);
+					val = scsi_2btoul(&sks[1]);
 					percentage = 10000 * val;
 
 					fprintf(stdout,
Index: share/misc/scsi_modes
===================================================================
--- share/misc/scsi_modes	(revision 225891)
+++ share/misc/scsi_modes	(working copy)
@@ -50,19 +50,32 @@
 # ALL DEVICE TYPES
 
 0x0a "Control Mode Page" {
-	{Reserved} *t7
+	{TST} t3
+	{TMF_ONLY} t1
+	{DPICZ} t1
+	{D_SENSE} t1
+	{GLTSD} t1
 	{RLEC} t1
 	{Queue Algorithm Modifier} t4
-	{Reserved} *t2
-	{QErr} t1
+	{NUAR} t1
+	{QErr} t2
 	{DQue} t1
 	{EECA} t1
-	{Reserved} *t4
+	{RAC} t1
+	{UA_INTLCK_CTRL} t2
+	{SWP} t1
 	{RAENP} t1
 	{UAAENP} t1
 	{EAENP} t1
-	{Reserved} *i1
+	{ATO} t1
+	{TAS} t1
+	{ATMPE} t1
+	{RWWP} t1
+	{Reserved} *t1
+	{Autoload Mode} t3
 	{Ready AEN Holdoff Period} i2
+	{Busy Timeout Period} i2
+	{Extended Self-Test Completion Time} i2
 }
 
 0x02 "Disconnect-Reconnect Page" {
Index: share/examples/scsi_target/scsi_cmds.c
===================================================================
--- share/examples/scsi_target/scsi_cmds.c	(revision 225891)
+++ share/examples/scsi_target/scsi_cmds.c	(working copy)
@@ -242,22 +242,22 @@ tcmd_sense(u_int init_id, struct ccb_scsiio *ctio,
 	       u_int8_t asc, u_int8_t ascq)
 {
 	struct initiator_state *istate;
-	struct scsi_sense_data *sense;
+	struct scsi_sense_data_fixed *sense;
 
 	/* Set our initiator's istate */
 	istate = tcmd_get_istate(init_id);
 	if (istate == NULL)
 		return;
 	istate->pending_ca |= CA_CMD_SENSE; /* XXX set instead of or? */
-	sense = &istate->sense_data;
+	sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
 	bzero(sense, sizeof(*sense));
 	sense->error_code = SSD_CURRENT_ERROR;
 	sense->flags = flags;
 	sense->add_sense_code = asc;
 	sense->add_sense_code_qual = ascq;
 	sense->extra_len =
-		offsetof(struct scsi_sense_data, sense_key_spec[2]) -
-		offsetof(struct scsi_sense_data, extra_len);
+		offsetof(struct scsi_sense_data_fixed, sense_key_spec[2]) -
+		offsetof(struct scsi_sense_data_fixed, extra_len);
 
 	/* Fill out the supplied CTIO */
 	if (ctio != NULL) {
@@ -298,7 +298,7 @@ tcmd_inquiry(struct ccb_accept_tio *atio, struct c
 	struct scsi_inquiry *inq;
 	struct atio_descr *a_descr;
 	struct initiator_state *istate;
-	struct scsi_sense_data *sense;
+	struct scsi_sense_data_fixed *sense;
 
 	a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;
 	inq = (struct scsi_inquiry *)a_descr->cdb;
@@ -310,7 +310,7 @@ tcmd_inquiry(struct ccb_accept_tio *atio, struct c
 	 * complain if EVPD or CMDDT is set.
 	 */
 	istate = tcmd_get_istate(ctio->init_id);
-	sense = &istate->sense_data;
+	sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
 	if ((inq->byte2 & SI_EVPD) != 0) {
 		tcmd_illegal_req(atio, ctio);
 		sense->sense_key_spec[0] = SSD_SCS_VALID | SSD_FIELDPTR_CMD |
@@ -376,7 +376,7 @@ static int
 tcmd_req_sense(struct ccb_accept_tio *atio, struct ccb_scsiio *ctio)
 {
 	struct scsi_request_sense *rsense;
-	struct scsi_sense_data *sense;
+	struct scsi_sense_data_fixed *sense;
 	struct initiator_state *istate;
 	size_t dlen;
 	struct atio_descr *a_descr;
@@ -385,7 +385,7 @@ tcmd_req_sense(struct ccb_accept_tio *atio, struct
 	rsense = (struct scsi_request_sense *)a_descr->cdb;
 	
 	istate = tcmd_get_istate(ctio->init_id);
-	sense = &istate->sense_data;
+	sense = (struct scsi_sense_data_fixed *)&istate->sense_data;
 
 	if (debug) {
 		cdb_debug(a_descr->cdb, "REQ SENSE from %u: ", atio->init_id);
@@ -400,7 +400,7 @@ tcmd_req_sense(struct ccb_accept_tio *atio, struct
 	}
 
 	bcopy(sense, ctio->data_ptr, sizeof(struct scsi_sense_data));
-	dlen = offsetof(struct scsi_sense_data, extra_len) +
+	dlen = offsetof(struct scsi_sense_data_fixed, extra_len) +
 			sense->extra_len + 1;
 	ctio->dxfer_len = min(dlen, SCSI_CDB6_LEN(rsense->length));
 	ctio->ccb_h.flags |= CAM_DIR_IN | CAM_SEND_STATUS;
@@ -482,7 +482,7 @@ tcmd_rdwr(struct ccb_accept_tio *atio, struct ccb_
 	c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;
 
 	/* Command needs to be decoded */
-	if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_RESV) {
+	if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_BOTH) {
 		if (debug)
 			warnx("Calling rdwr_decode");
 		ret = tcmd_rdwr_decode(atio, ctio);
Index: share/examples/scsi_target/scsi_target.c
===================================================================
--- share/examples/scsi_target/scsi_target.c	(revision 225891)
+++ share/examples/scsi_target/scsi_target.c	(working copy)
@@ -651,7 +651,7 @@ work_atio(struct ccb_accept_tio *atio)
 	 * receiving this ATIO.
 	 */
 	if (atio->sense_len != 0) {
-		struct scsi_sense_data *sense;
+		struct scsi_sense_data_fixed *sense;
 
 		if (debug) {
 			warnx("ATIO with %u bytes sense received",
@@ -825,9 +825,9 @@ work_inot(struct ccb_immed_notify *inot)
 
 	/* If there is sense data, use it */
 	if (sense != 0) {
-		struct scsi_sense_data *sense;
+		struct scsi_sense_data_fixed *sense;
 
-		sense = &inot->sense_data;
+		sense = (struct scsi_sense_data_fixed *)&inot->sense_data;
 		tcmd_sense(inot->initiator_id, NULL, sense->flags,
 			   sense->add_sense_code, sense->add_sense_code_qual);
 		if (debug)
Index: sys/powerpc/ps3/ps3cdrom.c
===================================================================
--- sys/powerpc/ps3/ps3cdrom.c	(revision 225891)
+++ sys/powerpc/ps3/ps3cdrom.c	(working copy)
@@ -506,21 +506,19 @@ ps3cdrom_intr(void *arg)
 
 			if (!ps3cdrom_decode_lv1_status(status, &sense_key,
 			    &asc, &ascq)) {
-				struct scsi_sense_data sense_data;
 
 				CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
 				   ("sense key 0x%02x asc 0x%02x ascq 0x%02x\n",
 				    sense_key, asc, ascq));
 
-				bzero(&sense_data, sizeof(sense_data));
-				sense_data.error_code = SSD_CURRENT_ERROR;
-				sense_data.flags |= sense_key;
-				sense_data.extra_len = 0xa;
-				sense_data.add_sense_code = asc;
-				sense_data.add_sense_code_qual = ascq;
-				ccb->csio.sense_len = sizeof(sense_data);
-				bcopy(&sense_data, &ccb->csio.sense_data,
-				    ccb->csio.sense_len);
+				scsi_set_sense_data(&ccb->csio.sense_data,
+				    /*sense_format*/ SSD_TYPE_NONE,
+				    /*current_error*/ 1,
+				    sense_key,
+				    asc,
+				    ascq,
+				    SSD_ELEM_NONE);
+				ccb->csio.sense_len = SSD_FULL_SIZE;
 				ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR |
 				    CAM_AUTOSNS_VALID;
 			}
@@ -643,8 +641,6 @@ ps3cdrom_transfer(void *arg, bus_dma_segment_t *se
 	}
 
 	if (err) {
-		struct scsi_sense_data sense_data;
-
 		device_printf(dev, "ATAPI command 0x%02x failed (%d)\n",
 		    cdb[0], err);
 
@@ -653,11 +649,18 @@ ps3cdrom_transfer(void *arg, bus_dma_segment_t *se
 		xp->x_ccb = NULL;
 		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
 
-		bzero(&sense_data, sizeof(sense_data));
-		sense_data.error_code = SSD_CURRENT_ERROR;
-		sense_data.flags |= SSD_KEY_ILLEGAL_REQUEST;
-		ccb->csio.sense_len = sizeof(sense_data);
-		bcopy(&sense_data, &ccb->csio.sense_data, ccb->csio.sense_len);
+		bzero(&ccb->csio.sense_data, sizeof(ccb->csio.sense_data));
+		/* Invalid field in parameter list */
+		scsi_set_sense_data(&ccb->csio.sense_data,
+				    /*sense_format*/ SSD_TYPE_NONE,
+				    /*current_error*/ 1,
+				    /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
+				    /*asc*/ 0x26,
+				    /*ascq*/ 0x00,
+				    SSD_ELEM_NONE);
+
+		ccb->csio.sense_len = SSD_FULL_SIZE;
+		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
 		ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
 		xpt_done(ccb);
 	} else {
Index: sys/cam/cam_periph.c
===================================================================
--- sys/cam/cam_periph.c	(revision 225891)
+++ sys/cam/cam_periph.c	(working copy)
@@ -1085,7 +1085,6 @@ camperiphsensedone(struct cam_periph *periph, unio
 	union ccb      *saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr;
 	cam_status	status;
 	int		frozen = 0;
-	u_int		sense_key;
 	int		depth = done_ccb->ccb_h.recovery_depth;
 
 	status = done_ccb->ccb_h.status;
@@ -1101,22 +1100,25 @@ camperiphsensedone(struct cam_periph *periph, unio
 	switch (status) {
 	case CAM_REQ_CMP:
 	{
+		int error_code, sense_key, asc, ascq;
+
+		scsi_extract_sense_len(&saved_ccb->csio.sense_data,
+				       saved_ccb->csio.sense_len -
+				       saved_ccb->csio.sense_resid,
+				       &error_code, &sense_key, &asc, &ascq,
+				       /*show_errors*/ 1);
 		/*
 		 * If we manually retrieved sense into a CCB and got
 		 * something other than "NO SENSE" send the updated CCB
 		 * back to the client via xpt_done() to be processed via
 		 * the error recovery code again.
 		 */
-		sense_key = saved_ccb->csio.sense_data.flags;
-		sense_key &= SSD_KEY;
-		if (sense_key != SSD_KEY_NO_SENSE) {
-			saved_ccb->ccb_h.status |=
-			    CAM_AUTOSNS_VALID;
+		if ((sense_key != -1)
+		 && (sense_key != SSD_KEY_NO_SENSE)) {
+			saved_ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
 		} else {
-			saved_ccb->ccb_h.status &=
-			    ~CAM_STATUS_MASK;
-			saved_ccb->ccb_h.status |=
-			    CAM_AUTOSENSE_FAIL;
+			saved_ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+			saved_ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL;
 		}
 		saved_ccb->csio.sense_resid = done_ccb->csio.resid;
 		bcopy(saved_ccb, done_ccb, sizeof(union ccb));
@@ -1198,12 +1200,15 @@ camperiphdone(struct cam_periph *periph, union ccb
 		if (status & CAM_AUTOSNS_VALID) {
 			struct ccb_getdev cgd;
 			struct scsi_sense_data *sense;
-			int    error_code, sense_key, asc, ascq;	
+			int    error_code, sense_key, asc, ascq, sense_len;
 			scsi_sense_action err_action;
 
 			sense = &done_ccb->csio.sense_data;
-			scsi_extract_sense(sense, &error_code, 
-					   &sense_key, &asc, &ascq);
+			sense_len = done_ccb->csio.sense_len -
+				    done_ccb->csio.sense_resid;
+			scsi_extract_sense_len(sense, sense_len, &error_code, 
+					       &sense_key, &asc, &ascq,
+					       /*show_errors*/ 1);
 			/*
 			 * Grab the inquiry data for this device.
 			 */
Index: sys/cam/cam_ccb.h
===================================================================
--- sys/cam/cam_ccb.h	(revision 225891)
+++ sys/cam/cam_ccb.h	(working copy)
@@ -539,7 +539,7 @@ struct ccb_dev_match {
 /*
  * Definitions for the path inquiry CCB fields.
  */
-#define CAM_VERSION	0x15	/* Hex value for current version */
+#define CAM_VERSION	0x16	/* Hex value for current version */
 
 typedef enum {
 	PI_MDP_ABLE	= 0x80,	/* Supports MDP message */
Index: sys/cam/scsi/scsi_all.h
===================================================================
--- sys/cam/scsi/scsi_all.h	(revision 225891)
+++ sys/cam/scsi/scsi_all.h	(working copy)
@@ -25,6 +25,7 @@
 #define	_SCSI_SCSI_ALL_H 1
 
 #include <sys/cdefs.h>
+#include <machine/stdarg.h>
 
 #ifdef _KERNEL
 /*
@@ -171,7 +172,8 @@ struct scsi_inquiry
 {
 	u_int8_t opcode;
 	u_int8_t byte2;
-#define	SI_EVPD 0x01
+#define	SI_EVPD 	0x01
+#define	SI_CMDDT	0x02
 	u_int8_t page_code;
 	u_int8_t reserved;
 	u_int8_t length;
@@ -200,7 +202,9 @@ struct scsi_mode_sense_6
 #define	SMS_PAGE_CTRL_CHANGEABLE 	0x40
 #define	SMS_PAGE_CTRL_DEFAULT 		0x80
 #define	SMS_PAGE_CTRL_SAVED 		0xC0
-	u_int8_t unused;
+	u_int8_t subpage;
+#define	SMS_SUBPAGE_PAGE_0		0x00
+#define	SMS_SUBPAGE_ALL			0xff
 	u_int8_t length;
 	u_int8_t control;
 };
@@ -209,8 +213,10 @@ struct scsi_mode_sense_10
 {
 	u_int8_t opcode;
 	u_int8_t byte2;		/* same bits as small version */
+#define	SMS10_LLBAA			0x10
 	u_int8_t page; 		/* same bits as small version */
-	u_int8_t unused[4];
+	u_int8_t subpage;
+	u_int8_t unused[3];
 	u_int8_t length[2];
 	u_int8_t control;
 };
@@ -263,6 +269,120 @@ struct scsi_mode_block_descr
 	u_int8_t block_len[3];
 };
 
+struct scsi_per_res_in
+{
+	u_int8_t opcode;
+	u_int8_t action;
+#define	SPRI_RK	0x00
+#define	SPRI_RR	0x01
+#define	SPRI_RC	0x02
+#define	SPRI_RS	0x03
+	u_int8_t reserved[5];
+	u_int8_t length[2];
+	u_int8_t control;
+};
+
+struct scsi_per_res_in_header
+{
+	u_int8_t generation[4];
+	u_int8_t length[4];
+};
+
+struct scsi_per_res_key
+{
+	u_int8_t key[8];
+};
+
+struct scsi_per_res_in_keys
+{
+	struct scsi_per_res_in_header header;
+	struct scsi_per_res_key keys[0];
+};
+
+struct scsi_per_res_cap
+{
+	uint8_t length[2];
+	uint8_t flags1;
+#define	SPRI_CRH	0x10
+#define	SPRI_SIP_C	0x08
+#define	SPRI_ATP_C	0x04
+#define	SPRI_PTPL_C	0x01
+	uint8_t flags2;
+#define	SPRI_TMV	0x80
+#define	SPRI_PTPL_A	0x01
+	uint8_t type_mask[2];
+#define	SPRI_TM_WR_EX_AR	0x8000
+#define	SPRI_TM_EX_AC_RO	0x4000
+#define	SPRI_TM_WR_EX_RO	0x2000
+#define	SPRI_TM_EX_AC		0x0800
+#define	SPRI_TM_WR_EX		0x0200
+#define	SPRI_TM_EX_AC_AR	0x0001
+	uint8_t reserved[2];
+};
+
+struct scsi_per_res_in_rsrv_data
+{
+	uint8_t reservation[8];
+	uint8_t obsolete1[4];
+	uint8_t reserved;
+	uint8_t scopetype;
+#define	SPRT_WE    0x01
+#define	SPRT_EA    0x03
+#define	SPRT_WERO  0x05
+#define	SPRT_EARO  0x06
+#define	SPRT_WEAR  0x07
+#define	SPRT_EAAR  0x08
+	uint8_t obsolete2[2];
+};
+
+struct scsi_per_res_in_rsrv
+{
+	struct scsi_per_res_in_header header;
+	struct scsi_per_res_in_rsrv_data data;
+};
+
+struct scsi_per_res_out
+{
+	u_int8_t opcode;
+	u_int8_t action;
+#define	SPRO_REGISTER		0x00
+#define	SPRO_RESERVE		0x01
+#define	SPRO_RELEASE		0x02
+#define	SPRO_CLEAR		0x03
+#define	SPRO_PREEMPT		0x04
+#define	SPRO_PRE_ABO		0x05
+#define	SPRO_REG_IGNO		0x06
+#define	SPRO_REG_MOVE		0x07
+#define	SPRO_ACTION_MASK	0x1f
+	u_int8_t scope_type;
+#define	SPR_SCOPE_MASK		0xf0
+#define	SPR_LU_SCOPE		0x00
+#define	SPR_TYPE_MASK		0x0f
+#define	SPR_TYPE_WR_EX		0x01
+#define	SPR_TYPE_EX_AC		0x03
+#define	SPR_TYPE_WR_EX_RO	0x05
+#define	SPR_TYPE_EX_AC_RO	0x06
+#define	SPR_TYPE_WR_EX_AR	0x07
+#define	SPR_TYPE_EX_AC_AR	0x08
+	u_int8_t reserved[2];
+	u_int8_t length[4];
+	u_int8_t control;
+};
+
+struct scsi_per_res_out_parms
+{
+	struct scsi_per_res_key res_key;
+	u_int8_t serv_act_res_key[8];
+	u_int8_t obsolete1[4];
+	u_int8_t flags;
+#define	SPR_SPEC_I_PT		0x08
+#define	SPR_ALL_TG_PT		0x04
+#define	SPR_APTPL		0x01
+	u_int8_t reserved1;
+	u_int8_t obsolete2[2];
+};
+
+
 struct scsi_log_sense
 {
 	u_int8_t opcode;
@@ -337,7 +457,16 @@ struct scsi_control_page {
 	u_int8_t page_code;
 	u_int8_t page_length;
 	u_int8_t rlec;
-#define	SCB_RLEC			0x01	/*Report Log Exception Cond*/
+#define	SCP_RLEC			0x01	/*Report Log Exception Cond*/
+#define	SCP_GLTSD			0x02	/*Global Logging target
+						  save disable */
+#define	SCP_DSENSE			0x04	/*Descriptor Sense */
+#define	SCP_DPICZ			0x08	/*Disable Prot. Info Check
+						  if Prot. Field is Zero */
+#define	SCP_TMF_ONLY			0x10	/*TM Functions Only*/
+#define	SCP_TST_MASK			0xE0	/*Task Set Type Mask*/
+#define	SCP_TST_ONE			0x00	/*One Task Set*/
+#define	SCP_TST_SEPARATE		0x20	/*Separate Task Sets*/
 	u_int8_t queue_flags;
 #define	SCP_QUEUE_ALG_MASK		0xF0
 #define	SCP_QUEUE_ALG_RESTRICTED	0x00
@@ -368,6 +497,41 @@ struct scsi_cache_page {
 	u_int8_t max_prefetch_ceil[2];
 };
 
+/*
+ * XXX KDM
+ * Updated version of the cache page, as of SBC.  Update this to SBC-3 and
+ * rationalize the two.
+ */
+struct scsi_caching_page {
+	uint8_t page_code;
+#define	SMS_CACHING_PAGE		0x08
+	uint8_t page_length;
+	uint8_t flags1;
+#define	SCP_IC		0x80
+#define	SCP_ABPF	0x40
+#define	SCP_CAP		0x20
+#define	SCP_DISC	0x10
+#define	SCP_SIZE	0x08
+#define	SCP_WCE		0x04
+#define	SCP_MF		0x02
+#define	SCP_RCD		0x01
+	uint8_t ret_priority;
+	uint8_t disable_pf_transfer_len[2];
+	uint8_t min_prefetch[2];
+	uint8_t max_prefetch[2];
+	uint8_t max_pf_ceiling[2];
+	uint8_t flags2;
+#define	SCP_FSW		0x80
+#define	SCP_LBCSS	0x40
+#define	SCP_DRA		0x20
+#define	SCP_VS1		0x10
+#define	SCP_VS2		0x08
+	uint8_t cache_segments;
+	uint8_t cache_seg_size[2];
+	uint8_t reserved;
+	uint8_t non_cache_seg_size[3];
+};
+
 struct scsi_info_exceptions_page {
 	u_int8_t page_code;
 #define	SIEP_PAGE_SAVABLE		0x80	/* Page is savable */
@@ -406,20 +570,49 @@ struct scsi_reserve
 {
 	u_int8_t opcode;
 	u_int8_t byte2;
-	u_int8_t unused[2];
-	u_int8_t length;
+#define	SR_EXTENT	0x01
+#define	SR_ID_MASK	0x0e
+#define	SR_3RDPTY	0x10
+#define	SR_LUN_MASK	0xe0
+	u_int8_t resv_id;
+	u_int8_t length[2];
 	u_int8_t control;
 };
 
+struct scsi_reserve_10 {
+	uint8_t	opcode;
+	uint8_t	byte2;
+#define	SR10_3RDPTY	0x10
+#define	SR10_LONGID	0x02
+#define	SR10_EXTENT	0x01
+	uint8_t resv_id;
+	uint8_t thirdparty_id;
+	uint8_t reserved[3];
+	uint8_t length[2];
+	uint8_t control;
+};
+
+
 struct scsi_release
 {
 	u_int8_t opcode;
 	u_int8_t byte2;
-	u_int8_t unused[2];
+	u_int8_t resv_id;
+	u_int8_t unused[1];
 	u_int8_t length;
 	u_int8_t control;
 };
 
+struct scsi_release_10 {
+	uint8_t opcode;
+	uint8_t byte2;
+	uint8_t resv_id;
+	uint8_t thirdparty_id;
+	uint8_t reserved[3];
+	uint8_t length[2];
+	uint8_t control;
+};
+
 struct scsi_prevent
 {
 	u_int8_t opcode;
@@ -435,13 +628,61 @@ struct scsi_sync_cache
 {
 	u_int8_t opcode;
 	u_int8_t byte2;
+#define	SSC_IMMED	0x02
+#define	SSC_RELADR	0x01
 	u_int8_t begin_lba[4];
 	u_int8_t reserved;
 	u_int8_t lb_count[2];
 	u_int8_t control;	
 };
 
+struct scsi_sync_cache_16
+{
+	uint8_t opcode;
+	uint8_t byte2;
+	uint8_t begin_lba[8];
+	uint8_t lb_count[4];
+	uint8_t reserved;
+	uint8_t control;
+};
 
+struct scsi_format {
+	uint8_t opcode;
+	uint8_t byte2;
+#define	SF_LONGLIST		0x20
+#define	SF_FMTDATA		0x10
+#define	SF_CMPLIST		0x08
+#define	SF_FORMAT_MASK		0x07
+#define	SF_FORMAT_BLOCK		0x00
+#define	SF_FORMAT_LONG_BLOCK	0x03
+#define	SF_FORMAT_BFI		0x04
+#define	SF_FORMAT_PHYS		0x05
+	uint8_t vendor;
+	uint8_t interleave[2];
+	uint8_t control;
+};
+
+struct scsi_format_header_short {
+	uint8_t reserved;
+#define	SF_DATA_FOV	0x80
+#define	SF_DATA_DPRY	0x40
+#define	SF_DATA_DCRT	0x20
+#define	SF_DATA_STPF	0x10
+#define	SF_DATA_IP	0x08
+#define	SF_DATA_DSP	0x04
+#define	SF_DATA_IMMED	0x02
+#define	SF_DATA_VS	0x01
+	uint8_t byte2;
+	uint8_t defect_list_len[2];
+};
+
+struct scsi_format_header_long {
+	uint8_t reserved;
+	uint8_t byte2;
+	uint8_t reserved2[2];
+	uint8_t defect_list_len[4];
+};
+
 struct scsi_changedef
 {
 	u_int8_t opcode;
@@ -459,6 +700,7 @@ struct scsi_read_buffer
 	u_int8_t byte2;
 #define	RWB_MODE		0x07
 #define	RWB_MODE_HDR_DATA	0x00
+#define	RWB_MODE_VENDOR		0x01
 #define	RWB_MODE_DATA		0x02
 #define	RWB_MODE_DOWNLOAD	0x04
 #define	RWB_MODE_DOWNLOAD_SAVE	0x05
@@ -529,6 +771,40 @@ struct scsi_rw_16
 	u_int8_t control;
 };
 
+struct scsi_write_verify_10
+{
+	uint8_t	opcode;
+	uint8_t	byte2;
+#define	SWV_BYTCHK		0x02
+#define	SWV_DPO			0x10
+#define	SWV_WRPROECT_MASK	0xe0
+	uint8_t	addr[4];
+	uint8_t	group;
+	uint8_t length[2];
+	uint8_t	control;
+};
+
+struct scsi_write_verify_12
+{
+	uint8_t	opcode;
+	uint8_t	byte2;
+	uint8_t	addr[4];
+	uint8_t	length[4];
+	uint8_t	group;
+	uint8_t	control;
+};
+
+struct scsi_write_verify_16
+{
+	uint8_t	opcode;
+	uint8_t	byte2;
+	uint8_t	addr[8];
+	uint8_t	length[4];
+	uint8_t	group;
+	uint8_t	control;
+};
+
+
 struct scsi_start_stop_unit
 {
 	u_int8_t opcode;
@@ -538,6 +814,14 @@ struct scsi_start_stop_unit
 	u_int8_t how;
 #define	SSS_START		0x01
 #define	SSS_LOEJ		0x02
+#define	SSS_PC_MASK		0xf0
+#define	SSS_PC_START_VALID	0x00
+#define	SSS_PC_ACTIVE		0x10
+#define	SSS_PC_IDLE		0x20
+#define	SSS_PC_STANDBY		0x30
+#define	SSS_PC_LU_CONTROL	0x70
+#define	SSS_PC_FORCE_IDLE_0	0xa0
+#define	SSS_PC_FORCE_STANDBY_0	0xb0
 	u_int8_t control;
 };
 
@@ -562,6 +846,18 @@ struct ata_pass_12 {
 	u_int8_t control;
 };
 
+struct scsi_maintenance_in
+{
+        uint8_t  opcode;
+        uint8_t  byte2;
+#define SERVICE_ACTION_MASK  0x1f
+#define SA_RPRT_TRGT_GRP     0x0a
+        uint8_t  reserved[4];
+	uint8_t  length[4];
+	uint8_t  reserved1;
+	uint8_t  control;
+};
+
 struct ata_pass_16 {
 	u_int8_t opcode;
 	u_int8_t protocol;
@@ -607,6 +903,7 @@ struct ata_pass_16 {
 #define	READ_10			0x28
 #define	WRITE_10		0x2A
 #define	POSITION_TO_ELEMENT	0x2B
+#define	WRITE_VERIFY_10		0x2E
 #define	SYNCHRONIZE_CACHE	0x35
 #define	READ_DEFECT_DATA_10	0x37
 #define	WRITE_BUFFER            0x3B
@@ -615,10 +912,16 @@ struct ata_pass_16 {
 #define	LOG_SELECT		0x4C
 #define	LOG_SENSE		0x4D
 #define	MODE_SELECT_10		0x55
+#define	RESERVE_10		0x56
+#define	RELEASE_10		0x57
 #define	MODE_SENSE_10		0x5A
+#define	PERSISTENT_RES_IN	0x5E
+#define	PERSISTENT_RES_OUT	0x5F
 #define	ATA_PASS_16		0x85
 #define	READ_16			0x88
 #define	WRITE_16		0x8A
+#define	WRITE_VERIFY_16		0x8E
+#define	SYNCHRONIZE_CACHE_16	0x91
 #define	SERVICE_ACTION_IN	0x9E
 #define	REPORT_LUNS		0xA0
 #define	ATA_PASS_12		0xA1
@@ -627,6 +930,7 @@ struct ata_pass_16 {
 #define	MOVE_MEDIUM     	0xA5
 #define	READ_12			0xA8
 #define	WRITE_12		0xAA
+#define	WRITE_VERIFY_12		0xAE
 #define	READ_ELEMENT_STATUS	0xB8
 
 /* Maintenance In Service Action Codes */
@@ -737,10 +1041,12 @@ struct scsi_inquiry_data
 	u_int8_t response_format;
 #define	SID_AENC	0x80
 #define	SID_TrmIOP	0x40
+#define	SID_NormACA	0x20
+#define	SID_HiSup	0x10
 	u_int8_t additional_length;
 #define	SID_ADDITIONAL_LENGTH(iqd)					\
 	((iqd)->additional_length +					\
-	offsetof(struct scsi_inquiry_data, additional_length) + 1)
+	__offsetof(struct scsi_inquiry_data, additional_length) + 1)
 	u_int8_t spc3_flags;
 #define	SPC3_SID_PROTECT	0x01
 #define	SPC3_SID_3PC		0x08
@@ -750,6 +1056,7 @@ struct scsi_inquiry_data
 #define	SPC3_SID_ACC		0x40
 #define	SPC3_SID_SCCS		0x80
 	u_int8_t spc2_flags;
+#define	SPC2_SID_ADDR16		0x01
 #define	SPC2_SID_MChngr 	0x08
 #define	SPC2_SID_MultiP 	0x10
 #define	SPC2_SID_EncServ	0x40
@@ -809,6 +1116,10 @@ struct scsi_inquiry_data
 	u_int8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE];
 };
 
+/*
+ * This structure is more suited to initiator operation, because the
+ * maximum number of supported pages is already allocated.
+ */
 struct scsi_vpd_supported_page_list
 {
 	u_int8_t device;
@@ -852,11 +1163,11 @@ struct scsi_vpd_device_id
 	u_int8_t device;
 	u_int8_t page_code;
 #define	SVPD_DEVICE_ID			0x83
-#define	SVPD_DEVICE_ID_MAX_SIZE		0xffff
-#define	SVPD_DEVICE_ID_HDR_LEN		4
-#define	SVPD_DEVICE_ID_DESC_HDR_LEN	4
+#define	SVPD_DEVICE_ID_MAX_SIZE		252
+#define	SVPD_DEVICE_ID_HDR_LEN \
+    __offsetof(struct scsi_vpd_device_id, desc_list)
 	u_int8_t length[2];
-	u_int8_t desc_list[0];
+	u_int8_t desc_list[];
 };
 
 struct scsi_vpd_id_descriptor
@@ -872,11 +1183,13 @@ struct scsi_vpd_id_descriptor
 #define	SVPD_ID_PROTO_SHIFT	4
 #define	SVPD_ID_CODESET_BINARY	0x01
 #define	SVPD_ID_CODESET_ASCII	0x02
+#define	SVPD_ID_CODESET_MASK	0x0f
 	u_int8_t	id_type;
 #define	SVPD_ID_PIV		0x80
 #define	SVPD_ID_ASSOC_LUN	0x00
 #define	SVPD_ID_ASSOC_PORT	0x10
 #define	SVPD_ID_ASSOC_TARGET	0x20
+#define	SVPD_ID_ASSOC_MASK	0x30
 #define	SVPD_ID_TYPE_VENDOR	0x00
 #define	SVPD_ID_TYPE_T10	0x01
 #define	SVPD_ID_TYPE_EUI64	0x02
@@ -889,7 +1202,9 @@ struct scsi_vpd_id_descriptor
 #define	SVPD_ID_TYPE_MASK	0x0f
 	u_int8_t	reserved;
 	u_int8_t	length;
-	u_int8_t	identifier[0];
+#define	SVPD_DEVICE_ID_DESC_HDR_LEN \
+    __offsetof(struct scsi_vpd_id_descriptor, identifier) 
+	u_int8_t	identifier[];
 };
 
 struct scsi_vpd_id_t10
@@ -990,12 +1305,23 @@ struct scsi_vpd_id_scsi_name
 	uint8_t name_string[256];
 };
 
+struct scsi_service_action_in
+{
+	uint8_t opcode;
+	uint8_t service_action;
+	uint8_t action_dependent[13];
+	uint8_t control;
+};
+
 struct scsi_read_capacity
 {
 	u_int8_t opcode;
 	u_int8_t byte2;
+#define	SRC_RELADR	0x01
 	u_int8_t addr[4];
-	u_int8_t unused[3];
+	u_int8_t unused[2];
+	u_int8_t pmi;
+#define	SRC_PMI		0x01
 	u_int8_t control;
 };
 
@@ -1038,18 +1364,11 @@ struct scsi_report_luns
 	uint8_t control;
 };
 
-struct scsi_report_luns_data {
-	u_int8_t length[4];	/* length of LUN inventory, in bytes */
-	u_int8_t reserved[4];	/* unused */
-	/*
-	 * LUN inventory- we only support the type zero form for now.
-	 */
-	struct {
-		u_int8_t lundata[8];
-	} luns[0];
-};
+struct scsi_report_luns_lundata {
+	uint8_t lundata[8];
 #define	RPL_LUNDATA_PERIPH_BUS_MASK	0x3f
 #define	RPL_LUNDATA_FLAT_LUN_MASK	0x3f
+#define	RPL_LUNDATA_FLAT_LUN_BITS	0x06
 #define	RPL_LUNDATA_LUN_TARG_MASK	0x3f
 #define	RPL_LUNDATA_LUN_BUS_MASK	0xe0
 #define	RPL_LUNDATA_LUN_LUN_MASK	0x1f
@@ -1062,7 +1381,17 @@ struct scsi_report_luns
 #define	RPL_LUNDATA_ATYP_FLAT	0x40
 #define	RPL_LUNDATA_ATYP_LUN	0x80
 #define	RPL_LUNDATA_ATYP_EXTLUN	0xc0
+};
 
+struct scsi_report_luns_data {
+	u_int8_t length[4];	/* length of LUN inventory, in bytes */
+	u_int8_t reserved[4];	/* unused */
+	/*
+	 * LUN inventory- we only support the type zero form for now.
+	 */
+	struct scsi_report_luns_lundata luns[0];
+};
+
 struct scsi_target_group
 {
 	uint8_t opcode;
@@ -1103,6 +1432,9 @@ struct scsi_target_port_group_descriptor {
 	uint8_t target_port_group[2];
 	uint8_t reserved;
 	uint8_t status;
+#define TPG_UNAVLBL      0
+#define TPG_SET_BY_STPG  0x01
+#define TPG_IMPLICIT     0x02
 	uint8_t vendor_specific;
 	uint8_t	target_port_count;
 	struct scsi_target_port_descriptor descriptors[];
@@ -1122,8 +1454,49 @@ struct scsi_target_group_data_extended {
 };
 
 
+typedef enum {
+	SSD_TYPE_NONE,
+	SSD_TYPE_FIXED,
+	SSD_TYPE_DESC
+} scsi_sense_data_type;
+
+typedef enum {
+	SSD_ELEM_NONE,
+	SSD_ELEM_SKIP,
+	SSD_ELEM_DESC,
+	SSD_ELEM_SKS,
+	SSD_ELEM_COMMAND,
+	SSD_ELEM_INFO,
+	SSD_ELEM_FRU,
+	SSD_ELEM_STREAM,
+	SSD_ELEM_MAX
+} scsi_sense_elem_type;
+
+
 struct scsi_sense_data
 {
+	uint8_t error_code;
+	/*
+	 * SPC-4 says that the maximum length of sense data is 252 bytes.
+	 * So this structure is exactly 252 bytes log.
+	 */
+#define	SSD_FULL_SIZE 252
+	uint8_t sense_buf[SSD_FULL_SIZE - 1];
+	/*
+	 * XXX KDM is this still a reasonable minimum size?
+	 */
+#define	SSD_MIN_SIZE 18
+	/*
+	 * Maximum value for the extra_len field in the sense data.
+	 */
+#define	SSD_EXTRA_MAX 244
+};
+
+/*
+ * Fixed format sense data.
+ */
+struct scsi_sense_data_fixed
+{
 	u_int8_t error_code;
 #define	SSD_ERRCODE			0x7F
 #define		SSD_CURRENT_ERROR	0x70
@@ -1147,7 +1520,7 @@ struct scsi_sense_data
 #define		SSD_KEY_EQUAL		0x0c
 #define		SSD_KEY_VOLUME_OVERFLOW	0x0d
 #define		SSD_KEY_MISCOMPARE	0x0e
-#define		SSD_KEY_RESERVED	0x0f			
+#define		SSD_KEY_COMPLETED	0x0f			
 #define	SSD_ILI		0x20
 #define	SSD_EOM		0x40
 #define	SSD_FILEMARK	0x80
@@ -1162,11 +1535,315 @@ struct scsi_sense_data
 #define	SSD_FIELDPTR_CMD	0x40
 #define	SSD_BITPTR_VALID	0x08
 #define	SSD_BITPTR_VALUE	0x07
-#define	SSD_MIN_SIZE 18
 	u_int8_t extra_bytes[14];
-#define	SSD_FULL_SIZE sizeof(struct scsi_sense_data)
+#define	SSD_FIXED_IS_PRESENT(sense, length, field) 			\
+	((length >= (offsetof(struct scsi_sense_data_fixed, field) +	\
+	sizeof(sense->field))) ? 1 :0)
+#define	SSD_FIXED_IS_FILLED(sense, field) 				\
+	((((offsetof(struct scsi_sense_data_fixed, field) +		\
+	sizeof(sense->field)) -						\
+	(offsetof(struct scsi_sense_data_fixed, extra_len) +		\
+	sizeof(sense->extra_len))) <= sense->extra_len) ? 1 : 0)
 };
 
+/*
+ * Descriptor format sense data definitions.
+ * Introduced in SPC-3.
+ */
+struct scsi_sense_data_desc 
+{
+	uint8_t	error_code;
+#define	SSD_DESC_CURRENT_ERROR	0x72
+#define	SSD_DESC_DEFERRED_ERROR	0x73
+	uint8_t sense_key;
+	uint8_t	add_sense_code;
+	uint8_t	add_sense_code_qual;
+	uint8_t	reserved[3];
+	/*
+	 * Note that SPC-4, section 4.5.2.1 says that the extra_len field
+	 * must be less than or equal to 244.
+	 */
+	uint8_t	extra_len;
+	uint8_t	sense_desc[0];
+#define	SSD_DESC_IS_PRESENT(sense, length, field) 			\
+	((length >= (offsetof(struct scsi_sense_data_desc, field) +	\
+	sizeof(sense->field))) ? 1 :0)
+};
+
+struct scsi_sense_desc_header
+{
+	uint8_t desc_type;
+	uint8_t length;
+};
+/*
+ * The information provide in the Information descriptor is device type or
+ * command specific information, and defined in a command standard.
+ *
+ * Note that any changes to the field names or positions in this structure,
+ * even reserved fields, should be accompanied by an examination of the
+ * code in ctl_set_sense() that uses them.
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+struct scsi_sense_info
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_INFO	0x00
+	uint8_t	length;
+	uint8_t	byte2;
+#define	SSD_INFO_VALID	0x80
+	uint8_t	reserved;
+	uint8_t	info[8];
+};
+
+/*
+ * Command-specific information depends on the command for which the
+ * reported condition occured.
+ *
+ * Note that any changes to the field names or positions in this structure,
+ * even reserved fields, should be accompanied by an examination of the
+ * code in ctl_set_sense() that uses them.
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+struct scsi_sense_command
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_COMMAND	0x01
+	uint8_t	length;
+	uint8_t	reserved[2];
+	uint8_t	command_info[8];
+};
+
+/*
+ * Sense key specific descriptor.  The sense key specific data format
+ * depends on the sense key in question.
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+struct scsi_sense_sks
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_SKS		0x02
+	uint8_t	length;
+	uint8_t reserved1[2];
+	uint8_t	sense_key_spec[3];
+#define	SSD_SKS_VALID		0x80
+	uint8_t reserved2;
+};
+
+/*
+ * This is used for the Illegal Request sense key (0x05) only.
+ */
+struct scsi_sense_sks_field
+{
+	uint8_t	byte0;
+#define	SSD_SKS_FIELD_VALID	0x80
+#define	SSD_SKS_FIELD_CMD	0x40
+#define	SSD_SKS_BPV		0x08
+#define	SSD_SKS_BIT_VALUE	0x07
+	uint8_t	field[2];
+};
+
+
+/* 
+ * This is used for the Hardware Error (0x04), Medium Error (0x03) and
+ * Recovered Error (0x01) sense keys.
+ */
+struct scsi_sense_sks_retry
+{
+	uint8_t byte0;
+#define	SSD_SKS_RETRY_VALID	0x80
+	uint8_t actual_retry_count[2];
+};
+
+/*
+ * Used with the NO Sense (0x00) or Not Ready (0x02) sense keys.
+ */
+struct scsi_sense_sks_progress
+{
+	uint8_t byte0;
+#define	SSD_SKS_PROGRESS_VALID	0x80
+	uint8_t progress[2];
+#define	SSD_SKS_PROGRESS_DENOM	0x10000
+};
+
+/*
+ * Used with the Copy Aborted (0x0a) sense key.
+ */
+struct scsi_sense_sks_segment
+{
+	uint8_t byte0;
+#define	SSD_SKS_SEGMENT_VALID	0x80
+#define	SSD_SKS_SEGMENT_SD	0x20
+#define	SSD_SKS_SEGMENT_BPV	0x08
+#define	SSD_SKS_SEGMENT_BITPTR	0x07
+	uint8_t field[2];
+};
+
+/*
+ * Used with the Unit Attention (0x06) sense key.
+ *
+ * This is currently used to indicate that the unit attention condition
+ * queue has overflowed (when the overflow bit is set).
+ */
+struct scsi_sense_sks_overflow
+{
+	uint8_t byte0;
+#define	SSD_SKS_OVERFLOW_VALID	0x80
+#define	SSD_SKS_OVERFLOW_SET	0x01
+	uint8_t	reserved[2];
+};
+
+/*
+ * This specifies which component is associated with the sense data.  There
+ * is no standard meaning for the fru value.
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+struct scsi_sense_fru
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_FRU		0x03
+	uint8_t	length;
+	uint8_t reserved;
+	uint8_t fru;
+};
+
+/*
+ * Used for Stream commands, defined in SSC-4.
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+ 
+struct scsi_sense_stream
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_STREAM		0x04
+	uint8_t	length;
+	uint8_t	reserved;
+	uint8_t	byte3;
+#define	SSD_DESC_STREAM_FM	0x80
+#define	SSD_DESC_STREAM_EOM	0x40
+#define	SSD_DESC_STREAM_ILI	0x20
+};
+
+/*
+ * Used for Block commands, defined in SBC-3.
+ *
+ * This is currently (as of SBC-3) only used for the Incorrect Length
+ * Indication (ILI) bit, which says that the data length requested in the
+ * READ LONG or WRITE LONG command did not match the length of the logical
+ * block.
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+struct scsi_sense_block
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_BLOCK		0x05
+	uint8_t	length;
+	uint8_t	reserved;
+	uint8_t	byte3;
+#define	SSD_DESC_BLOCK_ILI	0x20
+};
+
+/*
+ * Used for Object-Based Storage Devices (OSD-3).
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+struct scsi_sense_osd_objid
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_OSD_OBJID	0x06
+	uint8_t	length;
+	uint8_t	reserved[6];
+	/*
+	 * XXX KDM provide the bit definitions here?  There are a lot of
+	 * them, and we don't have an OSD driver yet.
+	 */
+	uint8_t	not_init_cmds[4];
+	uint8_t	completed_cmds[4];
+	uint8_t	partition_id[8];
+	uint8_t	object_id[8];
+};
+
+/*
+ * Used for Object-Based Storage Devices (OSD-3).
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+struct scsi_sense_osd_integrity
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_OSD_INTEGRITY	0x07
+	uint8_t	length;
+	uint8_t	integ_check_val[32];
+};
+
+/*
+ * Used for Object-Based Storage Devices (OSD-3).
+ *
+ * Maximum descriptors allowed: 1 (as of SPC-4)
+ */
+struct scsi_sense_osd_attr_id
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_OSD_ATTR_ID	0x08
+	uint8_t	length;
+	uint8_t	reserved[2];
+	uint8_t	attr_desc[0];
+};
+
+/*
+ * Used with Sense keys No Sense (0x00) and Not Ready (0x02).
+ *
+ * Maximum descriptors allowed: 32 (as of SPC-4)
+ */
+struct scsi_sense_progress
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_PROGRESS	0x0a
+	uint8_t	length;
+	uint8_t	sense_key;
+	uint8_t	add_sense_code;
+	uint8_t	add_sense_code_qual;
+	uint8_t reserved;
+	uint8_t	progress[2];
+};
+
+/*
+ * This is typically forwarded as the result of an EXTENDED COPY command.
+ *
+ * Maximum descriptors allowed: 2 (as of SPC-4)
+ */
+struct scsi_sense_forwarded
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_FORWARDED	0x0c
+	uint8_t	length;
+	uint8_t	byte2;
+#define	SSD_FORWARDED_FSDT	0x80
+#define	SSD_FORWARDED_SDS_MASK	0x0f
+#define	SSD_FORWARDED_SDS_UNK	0x00
+#define	SSD_FORWARDED_SDS_EXSRC	0x01
+#define	SSD_FORWARDED_SDS_EXDST	0x02
+};
+
+/*
+ * Vendor-specific sense descriptor.  The desc_type field will be in the
+ * range bewteen MIN and MAX inclusive.
+ */
+struct scsi_sense_vendor
+{
+	uint8_t	desc_type;
+#define	SSD_DESC_VENDOR_MIN	0x80
+#define	SSD_DESC_VENDOR_MAX	0xff
+	uint8_t length;
+	uint8_t	data[0];
+};
+
 struct scsi_mode_header_6
 {
 	u_int8_t data_length;	/* Sense data length */
@@ -1187,9 +1864,20 @@ struct scsi_mode_header_10
 struct scsi_mode_page_header
 {
 	u_int8_t page_code;
+#define	SMPH_PS		0x80
+#define	SMPH_SPF	0x40
+#define	SMPH_PC_MASK	0x3f
 	u_int8_t page_length;
 };
 
+struct scsi_mode_page_header_sp
+{
+	uint8_t page_code;
+	uint8_t subpage;
+	uint8_t page_length[2];
+};
+
+
 struct scsi_mode_blk_desc
 {
 	u_int8_t density;
@@ -1292,6 +1980,84 @@ scsi_sense_action scsi_error_action(struct ccb_scs
 				    struct scsi_inquiry_data *inq_data,
 				    u_int32_t sense_flags);
 const char *	scsi_status_string(struct ccb_scsiio *csio);
+
+void scsi_desc_iterate(struct scsi_sense_data_desc *sense, u_int sense_len,
+		       int (*iter_func)(struct scsi_sense_data_desc *sense,
+					u_int, struct scsi_sense_desc_header *,
+					void *), void *arg);
+uint8_t *scsi_find_desc(struct scsi_sense_data_desc *sense, u_int sense_len,
+			uint8_t desc_type);
+void scsi_set_sense_data(struct scsi_sense_data *sense_data, 
+			 scsi_sense_data_type sense_format, int current_error,
+			 int sense_key, int asc, int ascq, ...) ;
+void scsi_set_sense_data_va(struct scsi_sense_data *sense_data,
+			    scsi_sense_data_type sense_format,
+			    int current_error, int sense_key, int asc,
+			    int ascq, va_list ap);
+int scsi_get_sense_info(struct scsi_sense_data *sense_data, u_int sense_len,
+			uint8_t info_type, uint64_t *info,
+			int64_t *signed_info);
+int scsi_get_sks(struct scsi_sense_data *sense_data, u_int sense_len,
+		 uint8_t *sks);
+int scsi_get_block_info(struct scsi_sense_data *sense_data, u_int sense_len,
+			struct scsi_inquiry_data *inq_data,
+			uint8_t *block_bits);
+int scsi_get_stream_info(struct scsi_sense_data *sense_data, u_int sense_len,
+			 struct scsi_inquiry_data *inq_data,
+			 uint8_t *stream_bits);
+void scsi_info_sbuf(struct sbuf *sb, uint8_t *cdb, int cdb_len,
+		    struct scsi_inquiry_data *inq_data, uint64_t info);
+void scsi_command_sbuf(struct sbuf *sb, uint8_t *cdb, int cdb_len,
+		       struct scsi_inquiry_data *inq_data, uint64_t csi);
+void scsi_progress_sbuf(struct sbuf *sb, uint16_t progress);
+int scsi_sks_sbuf(struct sbuf *sb, int sense_key, uint8_t *sks);
+void scsi_fru_sbuf(struct sbuf *sb, uint64_t fru);
+void scsi_stream_sbuf(struct sbuf *sb, uint8_t stream_bits, uint64_t info);
+void scsi_block_sbuf(struct sbuf *sb, uint8_t block_bits, uint64_t info);
+void scsi_sense_info_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			  u_int sense_len, uint8_t *cdb, int cdb_len,
+			  struct scsi_inquiry_data *inq_data,
+			  struct scsi_sense_desc_header *header);
+
+void scsi_sense_command_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			     u_int sense_len, uint8_t *cdb, int cdb_len,
+			     struct scsi_inquiry_data *inq_data,
+			     struct scsi_sense_desc_header *header);
+void scsi_sense_sks_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			 u_int sense_len, uint8_t *cdb, int cdb_len,
+			 struct scsi_inquiry_data *inq_data,
+			 struct scsi_sense_desc_header *header);
+void scsi_sense_fru_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			 u_int sense_len, uint8_t *cdb, int cdb_len,
+			 struct scsi_inquiry_data *inq_data,
+			 struct scsi_sense_desc_header *header);
+void scsi_sense_stream_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			    u_int sense_len, uint8_t *cdb, int cdb_len,
+			    struct scsi_inquiry_data *inq_data,
+			    struct scsi_sense_desc_header *header);
+void scsi_sense_block_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			   u_int sense_len, uint8_t *cdb, int cdb_len,
+			   struct scsi_inquiry_data *inq_data,
+			   struct scsi_sense_desc_header *header);
+void scsi_sense_progress_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			      u_int sense_len, uint8_t *cdb, int cdb_len,
+			      struct scsi_inquiry_data *inq_data,
+			      struct scsi_sense_desc_header *header);
+void scsi_sense_generic_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			     u_int sense_len, uint8_t *cdb, int cdb_len,
+			     struct scsi_inquiry_data *inq_data,
+			     struct scsi_sense_desc_header *header);
+void scsi_sense_desc_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			  u_int sense_len, uint8_t *cdb, int cdb_len,
+			  struct scsi_inquiry_data *inq_data,
+			  struct scsi_sense_desc_header *header);
+scsi_sense_data_type scsi_sense_type(struct scsi_sense_data *sense_data);
+
+void scsi_sense_only_sbuf(struct scsi_sense_data *sense, u_int sense_len,
+			  struct sbuf *sb, char *path_str,
+			  struct scsi_inquiry_data *inq_data, uint8_t *cdb,
+			  int cdb_len);
+
 #ifdef _KERNEL
 int		scsi_command_string(struct ccb_scsiio *csio, struct sbuf *sb);
 int		scsi_sense_sbuf(struct ccb_scsiio *csio, struct sbuf *sb,
@@ -1497,31 +2263,29 @@ int		scsi_static_inquiry_match(caddr_t inqbuffer,
 int		scsi_devid_match(uint8_t *rhs, size_t rhs_len,
 				 uint8_t *lhs, size_t lhs_len);
 
-static __inline void scsi_extract_sense(struct scsi_sense_data *sense,
-					int *error_code, int *sense_key,
-					int *asc, int *ascq);
+void scsi_extract_sense(struct scsi_sense_data *sense, int *error_code,
+			int *sense_key, int *asc, int *ascq);
+void scsi_extract_sense_len(struct scsi_sense_data *sense,
+			    u_int sense_len, int *error_code, int *sense_key,
+			    int *asc, int *ascq, int show_errors);
+int scsi_get_sense_key(struct scsi_sense_data *sense, u_int sense_len,
+		       int show_errors);
+int scsi_get_asc(struct scsi_sense_data *sense, u_int sense_len,
+		 int show_errors);
+int scsi_get_ascq(struct scsi_sense_data *sense, u_int sense_len,
+		  int show_errors);
 static __inline void scsi_ulto2b(u_int32_t val, u_int8_t *bytes);
 static __inline void scsi_ulto3b(u_int32_t val, u_int8_t *bytes);
 static __inline void scsi_ulto4b(u_int32_t val, u_int8_t *bytes);
 static __inline void scsi_u64to8b(u_int64_t val, u_int8_t *bytes);
-static __inline u_int32_t scsi_2btoul(u_int8_t *bytes);
-static __inline u_int32_t scsi_3btoul(u_int8_t *bytes);
-static __inline int32_t scsi_3btol(u_int8_t *bytes);
-static __inline u_int32_t scsi_4btoul(u_int8_t *bytes);
-static __inline u_int64_t scsi_8btou64(u_int8_t *bytes);
+static __inline uint32_t scsi_2btoul(const uint8_t *bytes);
+static __inline uint32_t scsi_3btoul(const uint8_t *bytes);
+static __inline int32_t scsi_3btol(const uint8_t *bytes);
+static __inline uint32_t scsi_4btoul(const uint8_t *bytes);
+static __inline uint64_t scsi_8btou64(const uint8_t *bytes);
 static __inline void *find_mode_page_6(struct scsi_mode_header_6 *mode_header);
 static __inline void *find_mode_page_10(struct scsi_mode_header_10 *mode_header);
 
-static __inline void scsi_extract_sense(struct scsi_sense_data *sense,
-					int *error_code, int *sense_key,
-					int *asc, int *ascq)
-{
-	*error_code = sense->error_code & SSD_ERRCODE;
-	*sense_key = sense->flags & SSD_KEY;
-	*asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0;
-	*ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0;
-}
-
 static __inline void
 scsi_ulto2b(u_int32_t val, u_int8_t *bytes)
 {
@@ -1563,20 +2327,20 @@ scsi_u64to8b(u_int64_t val, u_int8_t *bytes)
 	bytes[7] = val & 0xff;
 }
 
-static __inline u_int32_t
-scsi_2btoul(u_int8_t *bytes)
+static __inline uint32_t
+scsi_2btoul(const uint8_t *bytes)
 {
-	u_int32_t rv;
+	uint32_t rv;
 
 	rv = (bytes[0] << 8) |
 	     bytes[1];
 	return (rv);
 }
 
-static __inline u_int32_t
-scsi_3btoul(u_int8_t *bytes)
+static __inline uint32_t
+scsi_3btoul(const uint8_t *bytes)
 {
-	u_int32_t rv;
+	uint32_t rv;
 
 	rv = (bytes[0] << 16) |
 	     (bytes[1] << 8) |
@@ -1585,9 +2349,9 @@ scsi_u64to8b(u_int64_t val, u_int8_t *bytes)
 }
 
 static __inline int32_t 
-scsi_3btol(u_int8_t *bytes)
+scsi_3btol(const uint8_t *bytes)
 {
-	u_int32_t rc = scsi_3btoul(bytes);
+	uint32_t rc = scsi_3btoul(bytes);
  
 	if (rc & 0x00800000)
 		rc |= 0xff000000;
@@ -1595,10 +2359,10 @@ static __inline int32_t
 	return (int32_t) rc;
 }
 
-static __inline u_int32_t
-scsi_4btoul(u_int8_t *bytes)
+static __inline uint32_t
+scsi_4btoul(const uint8_t *bytes)
 {
-	u_int32_t rv;
+	uint32_t rv;
 
 	rv = (bytes[0] << 24) |
 	     (bytes[1] << 16) |
@@ -1608,7 +2372,7 @@ static __inline int32_t
 }
 
 static __inline uint64_t
-scsi_8btou64(uint8_t *bytes)
+scsi_8btou64(const uint8_t *bytes)
 {
         uint64_t rv;
  
Index: sys/cam/scsi/scsi_cd.c
===================================================================
--- sys/cam/scsi/scsi_cd.c	(revision 225891)
+++ sys/cam/scsi/scsi_cd.c	(working copy)
@@ -1691,9 +1691,10 @@ cddone(struct cam_periph *periph, union ccb *done_
 
 				if (have_sense) {
 					sense = &csio->sense_data;
-					scsi_extract_sense(sense, &error_code,
-							   &sense_key, 
-							   &asc, &ascq);
+					scsi_extract_sense_len(sense,
+					    csio->sense_len - csio->sense_resid,
+					    &error_code, &sense_key, &asc,
+					    &ascq, /*show_errors*/ 1);
 				}
 				/*
 				 * Attach to anything that claims to be a
@@ -3126,8 +3127,9 @@ cderror(union ccb *ccb, u_int32_t cam_flags, u_int
 	 && ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) {
 		int sense_key, error_code, asc, ascq;
 
- 		scsi_extract_sense(&ccb->csio.sense_data,
-				   &error_code, &sense_key, &asc, &ascq);
+ 		scsi_extract_sense_len(&ccb->csio.sense_data,
+		    ccb->csio.sense_len - ccb->csio.sense_resid, &error_code,
+		    &sense_key, &asc, &ascq, /*show_errors*/ 1);
 		if (sense_key == SSD_KEY_ILLEGAL_REQUEST)
  			error = cd6byteworkaround(ccb);
 	}
Index: sys/cam/scsi/scsi_sa.c
===================================================================
--- sys/cam/scsi/scsi_sa.c	(revision 225891)
+++ sys/cam/scsi/scsi_sa.c	(working copy)
@@ -235,10 +235,10 @@ struct sa_softc {
 	 */
 	struct {
 		struct scsi_sense_data _last_io_sense;
-		u_int32_t _last_io_resid;
+		u_int64_t _last_io_resid;
 		u_int8_t _last_io_cdb[CAM_MAX_CDBLEN];
 		struct scsi_sense_data _last_ctl_sense;
-		u_int32_t _last_ctl_resid;
+		u_int64_t _last_ctl_resid;
 		u_int8_t _last_ctl_cdb[CAM_MAX_CDBLEN];
 #define	last_io_sense	errinfo._last_io_sense
 #define	last_io_resid	errinfo._last_io_resid
@@ -849,8 +849,10 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg,
 			 */
 			if ((periph->flags & CAM_PERIPH_LOCKED) == 0) {
 				error = cam_periph_hold(periph, PRIBIO|PCATCH);
-				 if (error != 0)
+				if (error != 0) {
+					cam_periph_unlock(periph);
 					return (error);
+				}
 				didlockperiph = 1;
 			}
 			break;
@@ -884,12 +886,15 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg,
 			 * access to data structures.
 			 */
 			error = cam_periph_hold(periph, PRIBIO|PCATCH);
-			if (error != 0)
+			if (error != 0) {
+				cam_periph_unlock(periph);
 				return (error);
+			}
 			didlockperiph = 1;
 			break;
 
 		default:
+			cam_periph_unlock(periph);
 			return (EINVAL);
 		}
 	}
@@ -2322,17 +2327,28 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t
 	struct	sa_softc *softc;
 	struct	ccb_scsiio *csio;
 	struct	scsi_sense_data *sense;
-	u_int32_t resid = 0;
-	int32_t	info = 0;
+	uint64_t resid = 0;
+	int64_t	info = 0;
 	cam_status status;
-	int error_code, sense_key, asc, ascq, error, aqvalid;
+	int error_code, sense_key, asc, ascq, error, aqvalid, stream_valid;
+	int sense_len;
+	uint8_t stream_bits;
 
 	periph = xpt_path_periph(ccb->ccb_h.path);
 	softc = (struct sa_softc *)periph->softc;
 	csio = &ccb->csio;
 	sense = &csio->sense_data;
-	scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);
-	aqvalid = sense->extra_len >= 6;
+	sense_len = csio->sense_len - csio->sense_resid;
+	scsi_extract_sense_len(sense, sense_len, &error_code, &sense_key,
+	    &asc, &ascq, /*show_errors*/ 1);
+	if (asc != -1 && ascq != -1)
+		aqvalid = 1;
+	else
+		aqvalid = 0;
+	if (scsi_get_stream_info(sense, sense_len, NULL, &stream_bits) == 0)
+		stream_valid = 1;
+	else
+		stream_valid = 0;
 	error = 0;
 
 	status = csio->ccb_h.status & CAM_STATUS_MASK;
@@ -2343,9 +2359,8 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t
 	 * unit.
 	 */
 	if (status == CAM_SCSI_STATUS_ERROR) {
-		if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
-			info = (int32_t) scsi_4btoul(sense->info);
-			resid = info;
+		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &resid,
+					&info) == 0) {
 			if ((softc->flags & SA_FLAG_FIXED) != 0)
 				resid *= softc->media_blksize;
 		} else {
@@ -2372,10 +2387,11 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t
 			softc->last_resid_was_io = 0;
 		}
 		CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("CDB[0]=0x%x Key 0x%x "
-		    "ASC/ASCQ 0x%x/0x%x CAM STATUS 0x%x flags 0x%x resid %d "
+		    "ASC/ASCQ 0x%x/0x%x CAM STATUS 0x%x flags 0x%x resid %jd "
 		    "dxfer_len %d\n", csio->cdb_io.cdb_bytes[0] & 0xff,
 		    sense_key, asc, ascq, status,
-		    sense->flags & ~SSD_KEY_RESERVED, resid, csio->dxfer_len));
+		    (stream_valid) ? stream_bits : 0, (intmax_t)resid,
+		    csio->dxfer_len));
 	} else {
 		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
 		    ("Cam Status 0x%x\n", status));
@@ -2431,7 +2447,7 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t
 		if (sense_key == SSD_KEY_VOLUME_OVERFLOW) {
 			csio->resid = resid;
 			error = ENOSPC;
-		} else if (sense->flags & SSD_EOM) {
+		} else if ((stream_valid != 0) && (stream_bits & SSD_EOM)) {
 			softc->flags |= SA_FLAG_EOM_PENDING;
 			/*
 			 * Grotesque as it seems, the few times
@@ -2450,7 +2466,7 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t
 			} else {
 				error = EIO;
 			}
-		} else if (sense->flags & SSD_FILEMARK) {
+		} else if ((stream_valid != 0) && (stream_bits & SSD_FILEMARK)){
 			if (softc->flags & SA_FLAG_FIXED) {
 				error = -1;
 				softc->flags |= SA_FLAG_EOF_PENDING;
@@ -2470,7 +2486,7 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t
 	/*
 	 * Incorrect Length usually applies to read, but can apply to writes.
 	 */
-	if (error == 0 && (sense->flags & SSD_ILI)) {
+	if (error == 0 && (stream_valid != 0) && (stream_bits & SSD_ILI)) {
 		if (info < 0) {
 			xpt_print(csio->ccb_h.path, toobig,
 			    csio->dxfer_len - info);
@@ -2485,7 +2501,8 @@ saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t
 			 * Bump the block number if we hadn't seen a filemark.
 			 * Do this independent of errors (we've moved anyway).
 			 */
-			if ((sense->flags & SSD_FILEMARK) == 0) {
+			if ((stream_valid == 0) ||
+			    (stream_bits & SSD_FILEMARK) == 0) {
 				if (softc->blkno != (daddr_t) -1) {
 					softc->blkno++;
 					csio->ccb_h.ccb_pflags |=
Index: sys/cam/scsi/scsi_targ_bh.c
===================================================================
--- sys/cam/scsi/scsi_targ_bh.c	(revision 225891)
+++ sys/cam/scsi/scsi_targ_bh.c	(working copy)
@@ -112,20 +112,20 @@ static struct scsi_inquiry_data no_lun_inq_data =
 	/* version */2, /* format version */2
 };
 
-static struct scsi_sense_data no_lun_sense_data =
+static struct scsi_sense_data_fixed no_lun_sense_data =
 {
 	SSD_CURRENT_ERROR|SSD_ERRCODE_VALID,
 	0,
 	SSD_KEY_NOT_READY, 
 	{ 0, 0, 0, 0 },
-	/*extra_len*/offsetof(struct scsi_sense_data, fru)
-                   - offsetof(struct scsi_sense_data, extra_len),
+	/*extra_len*/offsetof(struct scsi_sense_data_fixed, fru)
+                   - offsetof(struct scsi_sense_data_fixed, extra_len),
 	{ 0, 0, 0, 0 },
 	/* Logical Unit Not Supported */
 	/*ASC*/0x25, /*ASCQ*/0
 };
 
-static const int request_sense_size = offsetof(struct scsi_sense_data, fru);
+static const int request_sense_size = offsetof(struct scsi_sense_data_fixed, fru);
 
 static periph_init_t	targbhinit;
 static void		targbhasync(void *callback_arg, u_int32_t code,
@@ -587,7 +587,9 @@ targbhdone(struct cam_periph *periph, union ccb *d
 				 * This needs to have other than a
 				 * no_lun_sense_data response.
 				 */
-				atio->sense_data = no_lun_sense_data;
+				bcopy(&no_lun_sense_data, &atio->sense_data,
+				      min(sizeof(no_lun_sense_data),
+					  sizeof(atio->sense_data)));
 				atio->sense_len = sizeof(no_lun_sense_data);
 				descr->data_resid = 0;
 				descr->data_increment = 0;
@@ -630,7 +632,9 @@ targbhdone(struct cam_periph *periph, union ccb *d
 			/* Direction is always relative to the initator */
 			atio->ccb_h.flags &= ~CAM_DIR_MASK;
 			atio->ccb_h.flags |= CAM_DIR_NONE;
-			atio->sense_data = no_lun_sense_data;
+			bcopy(&no_lun_sense_data, &atio->sense_data,
+			      min(sizeof(no_lun_sense_data),
+				  sizeof(atio->sense_data)));
 			atio->sense_len = sizeof (no_lun_sense_data);
 			descr->data_resid = 0;
 			descr->data_increment = 0;
Index: sys/cam/scsi/scsi_da.c
===================================================================
--- sys/cam/scsi/scsi_da.c	(revision 225891)
+++ sys/cam/scsi/scsi_da.c	(working copy)
@@ -753,10 +753,10 @@ daclose(struct disk *dp)
 				int asc, ascq;
 				int sense_key, error_code;
 
-				scsi_extract_sense(&ccb->csio.sense_data,
-						   &error_code,
-						   &sense_key, 
-						   &asc, &ascq);
+				scsi_extract_sense_len(&ccb->csio.sense_data,
+				    ccb->csio.sense_len - ccb->csio.sense_resid,
+				    &error_code, &sense_key, &asc, &ascq,
+				    /*show_errors*/ 1);
 				if (sense_key != SSD_KEY_ILLEGAL_REQUEST)
 					scsi_sense_print(&ccb->csio);
 			} else {
@@ -916,10 +916,10 @@ dadump(void *arg, void *virtual, vm_offset_t physi
 				int asc, ascq;
 				int sense_key, error_code;
 
-				scsi_extract_sense(&csio.sense_data,
-						   &error_code,
-						   &sense_key, 
-						   &asc, &ascq);
+				scsi_extract_sense_len(&csio.sense_data,
+				    csio.sense_len - csio.sense_resid,
+				    &error_code, &sense_key, &asc, &ascq,
+				    /*show_errors*/ 1);
 				if (sense_key != SSD_KEY_ILLEGAL_REQUEST)
 					scsi_sense_print(&csio);
 			} else {
@@ -1802,9 +1802,10 @@ dadone(struct cam_periph *periph, union ccb *done_
 
 				if (have_sense) {
 					sense = &csio->sense_data;
-					scsi_extract_sense(sense, &error_code,
-							   &sense_key, 
-							   &asc, &ascq);
+					scsi_extract_sense_len(sense,
+					    csio->sense_len - csio->sense_resid,
+					    &error_code, &sense_key, &asc,
+					    &ascq, /*show_errors*/ 1);
 				}
 				/*
 				 * Attach to anything that claims to be a
Index: sys/cam/scsi/scsi_low.c
===================================================================
--- sys/cam/scsi/scsi_low.c	(revision 225891)
+++ sys/cam/scsi/scsi_low.c	(working copy)
@@ -2577,12 +2577,16 @@ resume:
 #ifdef	SCSI_LOW_DEBUG
 				if (scsi_low_debug & SCSI_LOW_DEBUG_SENSE)
 				{
-					printf("SENSE: [%x][%x][%x][%x][%x]\n",
-					(u_int) cb->ccb_sense.error_code,
-					(u_int) cb->ccb_sense.segment,
-					(u_int) cb->ccb_sense.flags,
-					(u_int) cb->ccb_sense.add_sense_code,
-					(u_int) cb->ccb_sense.add_sense_code_qual);
+					int error_code, sense_key, asc, ascq;
+
+					scsi_extract_sense(&cb->ccb_sense,
+							   &error_code,
+							   &sense_key,
+							   &asc,
+							   &ascq);
+					printf("SENSE: [%x][%x][%x][%x]\n",
+					       error_code, sense_key, asc,
+					       ascq);
 				}
 #endif	/* SCSI_LOW_DEBUG */
 			}
Index: sys/cam/scsi/scsi_all.c
===================================================================
--- sys/cam/scsi/scsi_all.c	(revision 225891)
+++ sys/cam/scsi/scsi_all.c	(working copy)
@@ -31,6 +31,8 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stdint.h>
 
 #ifdef _KERNEL
 #include <opt_scsi.h>
@@ -54,6 +56,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/sbuf.h>
 #ifndef _KERNEL
 #include <camlib.h>
+#include <stddef.h>
 
 #ifndef FALSE
 #define FALSE   0
@@ -608,14 +611,24 @@ scsi_op_desc(u_int16_t opcode, struct scsi_inquiry
 	struct op_table_entry *table[2];
 	int num_tables;
 
-	pd_type = SID_TYPE(inq_data);
+	/*
+	 * If we've got inquiry data, use it to determine what type of
+	 * device we're dealing with here.  Otherwise, assume direct
+	 * access.
+	 */
+	if (inq_data == NULL) {
+		pd_type = T_DIRECT;
+		match = NULL;
+	} else {
+		pd_type = SID_TYPE(inq_data);
 
-	match = cam_quirkmatch((caddr_t)inq_data,
-			       (caddr_t)scsi_op_quirk_table,
-			       sizeof(scsi_op_quirk_table)/
-			       sizeof(*scsi_op_quirk_table),
-			       sizeof(*scsi_op_quirk_table),
-			       scsi_inquiry_match);
+		match = cam_quirkmatch((caddr_t)inq_data,
+				       (caddr_t)scsi_op_quirk_table,
+				       sizeof(scsi_op_quirk_table)/
+				       sizeof(*scsi_op_quirk_table),
+				       sizeof(*scsi_op_quirk_table),
+				       scsi_inquiry_match);
+	}
 
 	if (match != NULL) {
 		table[0] = ((struct scsi_op_quirk_entry *)match)->op_table;
@@ -699,7 +712,7 @@ const struct sense_key_table_entry sense_key_table
 	{ SSD_KEY_EQUAL, SS_NOP, "EQUAL" },
 	{ SSD_KEY_VOLUME_OVERFLOW, SS_FATAL|EIO, "VOLUME OVERFLOW" },
 	{ SSD_KEY_MISCOMPARE, SS_NOP, "MISCOMPARE" },
-	{ SSD_KEY_RESERVED, SS_FATAL|EIO, "RESERVED" }
+	{ SSD_KEY_COMPLETED, SS_NOP, "COMPLETED" }
 };
 
 const int sense_key_table_size =
@@ -1062,25 +1075,25 @@ static struct asc_table_entry asc_table[] = {
 	{ SST(0x10, 0x03, SS_RDEF,	/* XXX TBD */
 	    "Logical block reference tag check failed") },
 	/* DT  WRO   BK   */
-	{ SST(0x11, 0x00, SS_RDEF,
+	{ SST(0x11, 0x00, SS_FATAL|EIO,
 	    "Unrecovered read error") },
 	/* DT  WRO   BK   */
-	{ SST(0x11, 0x01, SS_RDEF,
+	{ SST(0x11, 0x01, SS_FATAL|EIO,
 	    "Read retries exhausted") },
 	/* DT  WRO   BK   */
-	{ SST(0x11, 0x02, SS_RDEF,
+	{ SST(0x11, 0x02, SS_FATAL|EIO,
 	    "Error too long to correct") },
 	/* DT  W O   BK   */
-	{ SST(0x11, 0x03, SS_RDEF,
+	{ SST(0x11, 0x03, SS_FATAL|EIO,
 	    "Multiple read errors") },
 	/* D   W O   BK   */
-	{ SST(0x11, 0x04, SS_RDEF,
+	{ SST(0x11, 0x04, SS_FATAL|EIO,
 	    "Unrecovered read error - auto reallocate failed") },
 	/*     WRO   B    */
-	{ SST(0x11, 0x05, SS_RDEF,
+	{ SST(0x11, 0x05, SS_FATAL|EIO,
 	    "L-EC uncorrectable error") },
 	/*     WRO   B    */
-	{ SST(0x11, 0x06, SS_RDEF,
+	{ SST(0x11, 0x06, SS_FATAL|EIO,
 	    "CIRC unrecovered error") },
 	/*     W O   B    */
 	{ SST(0x11, 0x07, SS_RDEF,
@@ -1095,10 +1108,10 @@ static struct asc_table_entry asc_table[] = {
 	{ SST(0x11, 0x0A, SS_RDEF,
 	    "Miscorrected error") },
 	/* D   W O   BK   */
-	{ SST(0x11, 0x0B, SS_RDEF,
+	{ SST(0x11, 0x0B, SS_FATAL|EIO,
 	    "Unrecovered read error - recommend reassignment") },
 	/* D   W O   BK   */
-	{ SST(0x11, 0x0C, SS_RDEF,
+	{ SST(0x11, 0x0C, SS_FATAL|EIO,
 	    "Unrecovered read error - recommend rewrite the data") },
 	/* DT  WRO   B    */
 	{ SST(0x11, 0x0D, SS_RDEF,
@@ -2790,7 +2803,10 @@ scsi_sense_desc(int sense_key, int asc, int ascq,
 			  &sense_entry,
 			  &asc_entry);
 
-	*sense_key_desc = sense_entry->desc;
+	if (sense_entry != NULL)
+		*sense_key_desc = sense_entry->desc;
+	else
+		*sense_key_desc = "Invalid Sense Key";
 
 	if (asc_entry != NULL)
 		*asc_desc = asc_entry->desc;
@@ -2816,10 +2832,12 @@ scsi_error_action(struct ccb_scsiio *csio, struct
 	int error_code, sense_key, asc, ascq;
 	scsi_sense_action action;
 
-	scsi_extract_sense(&csio->sense_data, &error_code,
-			   &sense_key, &asc, &ascq);
+	scsi_extract_sense_len(&csio->sense_data, csio->sense_len -
+			       csio->sense_resid, &error_code,
+			       &sense_key, &asc, &ascq, /*show_errors*/ 1);
 
-	if (error_code == SSD_DEFERRED_ERROR) {
+	if ((error_code == SSD_DEFERRED_ERROR)
+	 || (error_code == SSD_DESC_DEFERRED_ERROR)) {
 		/*
 		 * XXX dufault at FreeBSD.org
 		 * This error doesn't relate to the command associated
@@ -2857,8 +2875,10 @@ scsi_error_action(struct ccb_scsiio *csio, struct
 		if (asc_entry != NULL
 		 && (asc != 0 || ascq != 0))
 			action = asc_entry->action;
+		else if (sense_entry != NULL)
+			action = sense_entry->action;
 		else
-			action = sense_entry->action;
+			action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE; 
 
 		if (sense_key == SSD_KEY_RECOVERED_ERROR) {
 			/*
@@ -3040,8 +3060,1348 @@ scsi_command_string(struct cam_device *device, str
 	return(0);
 }
 
+/*
+ * Iterate over sense descriptors.  Each descriptor is passed into iter_func(). 
+ * If iter_func() returns 0, list traversal continues.  If iter_func()
+ * returns non-zero, list traversal is stopped.
+ */
+void
+scsi_desc_iterate(struct scsi_sense_data_desc *sense, u_int sense_len,
+		  int (*iter_func)(struct scsi_sense_data_desc *sense,
+				   u_int, struct scsi_sense_desc_header *,
+				   void *), void *arg)
+{
+	int cur_pos;
+	int desc_len;
 
+	/*
+	 * First make sure the extra length field is present.
+	 */
+	if (SSD_DESC_IS_PRESENT(sense, sense_len, extra_len) == 0)
+		return;
+
+	/*
+	 * The length of data actually returned may be different than the
+	 * extra_len recorded in the sturcture.
+	 */
+	desc_len = sense_len -offsetof(struct scsi_sense_data_desc, sense_desc);
+
+	/*
+	 * Limit this further by the extra length reported, and the maximum
+	 * allowed extra length.
+	 */
+	desc_len = MIN(desc_len, MIN(sense->extra_len, SSD_EXTRA_MAX));
+
+	/*
+	 * Subtract the size of the header from the descriptor length.
+	 * This is to ensure that we have at least the header left, so we
+	 * don't have to check that inside the loop.  This can wind up
+	 * being a negative value.
+	 */
+	desc_len -= sizeof(struct scsi_sense_desc_header);
+
+	for (cur_pos = 0; cur_pos < desc_len;) {
+		struct scsi_sense_desc_header *header;
+
+		header = (struct scsi_sense_desc_header *)
+			&sense->sense_desc[cur_pos];
+
+		/*
+		 * Check to make sure we have the entire descriptor.  We
+		 * don't call iter_func() unless we do.
+		 *
+		 * Note that although cur_pos is at the beginning of the
+		 * descriptor, desc_len already has the header length
+		 * subtracted.  So the comparison of the length in the
+		 * header (which does not include the header itself) to
+		 * desc_len - cur_pos is correct.
+		 */
+		if (header->length > (desc_len - cur_pos)) 
+			break;
+
+		if (iter_func(sense, sense_len, header, arg) != 0)
+			break;
+
+		cur_pos += sizeof(*header) + header->length;
+	}
+}
+
+struct scsi_find_desc_info {
+	uint8_t desc_type;
+	struct scsi_sense_desc_header *header;
+};
+
+static int
+scsi_find_desc_func(struct scsi_sense_data_desc *sense, u_int sense_len,
+		    struct scsi_sense_desc_header *header, void *arg)
+{
+	struct scsi_find_desc_info *desc_info;
+
+	desc_info = (struct scsi_find_desc_info *)arg;
+
+	if (header->desc_type == desc_info->desc_type) {
+		desc_info->header = header;
+
+		/* We found the descriptor, tell the iterator to stop. */
+		return (1);
+	} else
+		return (0);
+}
+
 /*
+ * Given a descriptor type, return a pointer to it if it is in the sense
+ * data and not truncated.  Avoiding truncating sense data will simplify
+ * things significantly for the caller.
+ */
+uint8_t *
+scsi_find_desc(struct scsi_sense_data_desc *sense, u_int sense_len,
+	       uint8_t desc_type)
+{
+	struct scsi_find_desc_info desc_info;
+
+	desc_info.desc_type = desc_type;
+	desc_info.header = NULL;
+
+	scsi_desc_iterate(sense, sense_len, scsi_find_desc_func, &desc_info);
+
+	return ((uint8_t *)desc_info.header);
+}
+
+/*
+ * Fill in SCSI sense data with the specified parameters.  This routine can
+ * fill in either fixed or descriptor type sense data.
+ */
+void
+scsi_set_sense_data_va(struct scsi_sense_data *sense_data,
+		      scsi_sense_data_type sense_format, int current_error,
+		      int sense_key, int asc, int ascq, va_list ap) 
+{
+	int descriptor_sense;
+	scsi_sense_elem_type elem_type;
+
+	/*
+	 * Determine whether to return fixed or descriptor format sense
+	 * data.  If the user specifies SSD_TYPE_NONE for some reason,
+	 * they'll just get fixed sense data.
+	 */
+	if (sense_format == SSD_TYPE_DESC)
+		descriptor_sense = 1;
+	else
+		descriptor_sense = 0;
+
+	/*
+	 * Zero the sense data, so that we don't pass back any garbage data
+	 * to the user.
+	 */
+	memset(sense_data, 0, sizeof(*sense_data));
+
+	if (descriptor_sense != 0) {
+		struct scsi_sense_data_desc *sense;
+
+		sense = (struct scsi_sense_data_desc *)sense_data;
+		/*
+		 * The descriptor sense format eliminates the use of the
+		 * valid bit.
+		 */
+		if (current_error != 0)
+			sense->error_code = SSD_DESC_CURRENT_ERROR;
+		else
+			sense->error_code = SSD_DESC_DEFERRED_ERROR;
+		sense->sense_key = sense_key;
+		sense->add_sense_code = asc;
+		sense->add_sense_code_qual = ascq;
+		/*
+		 * Start off with no extra length, since the above data
+		 * fits in the standard descriptor sense information.
+		 */
+		sense->extra_len = 0;
+		while ((elem_type = (scsi_sense_elem_type)va_arg(ap,
+			scsi_sense_elem_type)) != SSD_ELEM_NONE) {
+			int sense_len, len_to_copy;
+			uint8_t *data;
+
+			if (elem_type >= SSD_ELEM_MAX) {
+				printf("%s: invalid sense type %d\n", __func__,
+				       elem_type);
+				break;
+			}
+
+			sense_len = (int)va_arg(ap, int);
+			len_to_copy = MIN(sense_len, SSD_EXTRA_MAX -
+					  sense->extra_len);
+			data = (uint8_t *)va_arg(ap, uint8_t *);
+
+			/*
+			 * We've already consumed the arguments for this one.
+			 */
+			if (elem_type == SSD_ELEM_SKIP)
+				continue;
+
+			switch (elem_type) {
+			case SSD_ELEM_DESC: {
+
+				/*
+				 * This is a straight descriptor.  All we
+				 * need to do is copy the data in.
+				 */
+				bcopy(data, &sense->sense_desc[
+				      sense->extra_len], len_to_copy);
+				sense->extra_len += len_to_copy;
+				break;
+			}
+			case SSD_ELEM_SKS: {
+				struct scsi_sense_sks sks;
+
+				bzero(&sks, sizeof(sks));
+
+				/*
+				 * This is already-formatted sense key
+				 * specific data.  We just need to fill out
+				 * the header and copy everything in.
+				 */
+				bcopy(data, &sks.sense_key_spec,
+				      MIN(len_to_copy,
+				          sizeof(sks.sense_key_spec)));
+
+				sks.desc_type = SSD_DESC_SKS;
+				sks.length = sizeof(sks) -
+				    offsetof(struct scsi_sense_sks, reserved1);
+				bcopy(&sks,&sense->sense_desc[sense->extra_len],
+				      sizeof(sks));
+				sense->extra_len += sizeof(sks);
+				break;
+			}
+			case SSD_ELEM_INFO:
+			case SSD_ELEM_COMMAND: {
+				struct scsi_sense_command cmd;
+				struct scsi_sense_info info;
+				uint8_t *data_dest;
+				uint8_t *descriptor;
+				int descriptor_size, i, copy_len;
+
+				bzero(&cmd, sizeof(cmd));
+				bzero(&info, sizeof(info));
+
+				/*
+				 * Command or information data.  The
+				 * operate in pretty much the same way.
+				 */
+				if (elem_type == SSD_ELEM_COMMAND) {
+					len_to_copy = MIN(len_to_copy,
+					    sizeof(cmd.command_info));
+					descriptor = (uint8_t *)&cmd;
+					descriptor_size  = sizeof(cmd);
+					data_dest =(uint8_t *)&cmd.command_info;
+					cmd.desc_type = SSD_DESC_COMMAND;
+					cmd.length = sizeof(cmd) -
+					    offsetof(struct scsi_sense_command,
+						     reserved);
+				} else {
+					len_to_copy = MIN(len_to_copy,
+					    sizeof(info.info));
+					descriptor = (uint8_t *)&info;
+					descriptor_size = sizeof(cmd);
+					data_dest = (uint8_t *)&info.info;
+					info.desc_type = SSD_DESC_INFO;
+					info.byte2 = SSD_INFO_VALID;
+					info.length = sizeof(info) -
+					    offsetof(struct scsi_sense_info,
+						     byte2);
+				}
+
+				/*
+				 * Copy this in reverse because the spec
+				 * (SPC-4) says that when 4 byte quantities
+				 * are stored in this 8 byte field, the
+				 * first four bytes shall be 0.
+				 *
+				 * So we fill the bytes in from the end, and
+				 * if we have less than 8 bytes to copy,
+				 * the initial, most significant bytes will
+				 * be 0.
+				 */
+				for (i = sense_len - 1; i >= 0 &&
+				     len_to_copy > 0; i--, len_to_copy--)
+					data_dest[len_to_copy - 1] = data[i];
+
+				/*
+				 * This calculation looks much like the
+				 * initial len_to_copy calculation, but
+				 * we have to do it again here, because
+				 * we're looking at a larger amount that
+				 * may or may not fit.  It's not only the
+				 * data the user passed in, but also the
+				 * rest of the descriptor.
+				 */
+				copy_len = MIN(descriptor_size,
+				    SSD_EXTRA_MAX - sense->extra_len);
+				bcopy(descriptor, &sense->sense_desc[
+				      sense->extra_len], copy_len);
+				sense->extra_len += copy_len;
+				break;
+			}
+			case SSD_ELEM_FRU: {
+				struct scsi_sense_fru fru;
+				int copy_len;
+
+				bzero(&fru, sizeof(fru));
+
+				fru.desc_type = SSD_DESC_FRU;
+				fru.length = sizeof(fru) -
+				    offsetof(struct scsi_sense_fru, reserved);
+				fru.fru = *data;
+
+				copy_len = MIN(sizeof(fru), SSD_EXTRA_MAX -
+					       sense->extra_len);
+				bcopy(&fru, &sense->sense_desc[
+				      sense->extra_len], copy_len);
+				sense->extra_len += copy_len;
+				break;
+			}
+			case SSD_ELEM_STREAM: {
+				struct scsi_sense_stream stream_sense;
+				int copy_len;
+
+				bzero(&stream_sense, sizeof(stream_sense));
+				stream_sense.desc_type = SSD_DESC_STREAM;
+				stream_sense.length = sizeof(stream_sense) -
+				   offsetof(struct scsi_sense_stream, reserved);
+				stream_sense.byte3 = *data;
+
+				copy_len = MIN(sizeof(stream_sense),
+				    SSD_EXTRA_MAX - sense->extra_len);
+				bcopy(&stream_sense, &sense->sense_desc[
+				      sense->extra_len], copy_len);
+				sense->extra_len += copy_len;
+				break;
+			}
+			default:
+				/*
+				 * We shouldn't get here, but if we do, do
+				 * nothing.  We've already consumed the
+				 * arguments above.
+				 */
+				break;
+			}
+		}
+	} else {
+		struct scsi_sense_data_fixed *sense;
+
+		sense = (struct scsi_sense_data_fixed *)sense_data;
+
+		if (current_error != 0)
+			sense->error_code = SSD_CURRENT_ERROR;
+		else
+			sense->error_code = SSD_DEFERRED_ERROR;
+
+		sense->flags = sense_key;
+		sense->add_sense_code = asc;
+		sense->add_sense_code_qual = ascq;
+		/*
+		 * We've set the ASC and ASCQ, so we have 6 more bytes of
+		 * valid data.  If we wind up setting any of the other
+		 * fields, we'll bump this to 10 extra bytes.
+		 */
+		sense->extra_len = 6;
+
+		while ((elem_type = (scsi_sense_elem_type)va_arg(ap,
+			scsi_sense_elem_type)) != SSD_ELEM_NONE) {
+			int sense_len, len_to_copy;
+			uint8_t *data;
+
+			if (elem_type >= SSD_ELEM_MAX) {
+				printf("%s: invalid sense type %d\n", __func__,
+				       elem_type);
+				break;
+			}
+			/*
+			 * If we get in here, just bump the extra length to
+			 * 10 bytes.  That will encompass anything we're
+			 * going to set here.
+			 */
+			sense->extra_len = 10;
+			sense_len = (int)va_arg(ap, int);
+			len_to_copy = MIN(sense_len, SSD_EXTRA_MAX -
+					  sense->extra_len);
+			data = (uint8_t *)va_arg(ap, uint8_t *);
+
+			switch (elem_type) {
+			case SSD_ELEM_SKS:
+				/*
+				 * The user passed in pre-formatted sense
+				 * key specific data.
+				 */
+				bcopy(data, &sense->sense_key_spec[0],
+				      MIN(sizeof(sense->sense_key_spec),
+				      sense_len));
+				break;
+			case SSD_ELEM_INFO:
+			case SSD_ELEM_COMMAND: {
+				uint8_t *data_dest;
+				int i;
+
+				if (elem_type == SSD_ELEM_COMMAND)
+					data_dest = &sense->cmd_spec_info[0];
+				else {
+					data_dest = &sense->info[0];
+					/*
+					 * We're setting the info field, so
+					 * set the valid bit.
+					 */
+					sense->error_code |= SSD_ERRCODE_VALID;
+				}
+
+				/*
+			 	 * Copy this in reverse so that if we have
+				 * less than 4 bytes to fill, the least
+				 * significant bytes will be at the end.
+				 * If we have more than 4 bytes, only the
+				 * least significant bytes will be included.
+				 */
+				for (i = sense_len - 1; i >= 0 &&
+				     len_to_copy > 0; i--, len_to_copy--)
+					data_dest[len_to_copy - 1] = data[i];
+
+				break;
+			}
+			case SSD_ELEM_FRU:
+				sense->fru = *data;
+				break;
+			case SSD_ELEM_STREAM:
+				sense->flags |= *data;
+				break;
+			case SSD_ELEM_DESC:
+			default:
+
+				/*
+				 * If the user passes in descriptor sense,
+				 * we can't handle that in fixed format.
+				 * So just skip it, and any unknown argument
+				 * types.
+				 */
+				break;
+			}
+		}
+	}
+}
+
+void
+scsi_set_sense_data(struct scsi_sense_data *sense_data, 
+		    scsi_sense_data_type sense_format, int current_error,
+		    int sense_key, int asc, int ascq, ...) 
+{
+	va_list ap;
+
+	va_start(ap, ascq);
+	scsi_set_sense_data_va(sense_data, sense_format, current_error,
+			       sense_key, asc, ascq, ap);
+	va_end(ap);
+}
+
+/*
+ * Get sense information for three similar sense data types.
+ */
+int
+scsi_get_sense_info(struct scsi_sense_data *sense_data, u_int sense_len,
+		    uint8_t info_type, uint64_t *info, int64_t *signed_info)
+{
+	scsi_sense_data_type sense_type;
+
+	if (sense_len == 0)
+		goto bailout;
+
+	sense_type = scsi_sense_type(sense_data);
+
+	switch (sense_type) {
+	case SSD_TYPE_DESC: {
+		struct scsi_sense_data_desc *sense;
+		uint8_t *desc;
+
+		sense = (struct scsi_sense_data_desc *)sense_data;
+
+		desc = scsi_find_desc(sense, sense_len, info_type);
+		if (desc == NULL)
+			goto bailout;
+
+		switch (info_type) {
+		case SSD_DESC_INFO: {
+			struct scsi_sense_info *info_desc;
+
+			info_desc = (struct scsi_sense_info *)desc;
+			*info = scsi_8btou64(info_desc->info);
+			if (signed_info != NULL)
+				*signed_info = *info;
+			break;
+		}
+		case SSD_DESC_COMMAND: {
+			struct scsi_sense_command *cmd_desc;
+
+			cmd_desc = (struct scsi_sense_command *)desc;
+
+			*info = scsi_8btou64(cmd_desc->command_info);
+			if (signed_info != NULL)
+				*signed_info = *info;
+			break;
+		}
+		case SSD_DESC_FRU: {
+			struct scsi_sense_fru *fru_desc;
+
+			fru_desc = (struct scsi_sense_fru *)desc;
+
+			*info = fru_desc->fru;
+			if (signed_info != NULL)
+				*signed_info = (int8_t)fru_desc->fru;
+			break;
+		}
+		default:
+			goto bailout;
+			break;
+		}
+		break;
+	}
+	case SSD_TYPE_FIXED: {
+		struct scsi_sense_data_fixed *sense;
+
+		sense = (struct scsi_sense_data_fixed *)sense_data;
+
+		switch (info_type) {
+		case SSD_DESC_INFO: {
+			uint32_t info_val;
+
+			if ((sense->error_code & SSD_ERRCODE_VALID) == 0)
+				goto bailout;
+
+			if (SSD_FIXED_IS_PRESENT(sense, sense_len, info) == 0)
+				goto bailout;
+
+			info_val = scsi_4btoul(sense->info);
+
+			*info = info_val;
+			if (signed_info != NULL)
+				*signed_info = (int32_t)info_val;
+			break;
+		}
+		case SSD_DESC_COMMAND: {
+			uint32_t cmd_val;
+
+			if ((SSD_FIXED_IS_PRESENT(sense, sense_len,
+			     cmd_spec_info) == 0)
+			 || (SSD_FIXED_IS_FILLED(sense, cmd_spec_info) == 0)) 
+				goto bailout;
+
+			cmd_val = scsi_4btoul(sense->cmd_spec_info);
+			if (cmd_val == 0)
+				goto bailout;
+
+			*info = cmd_val;
+			if (signed_info != NULL)
+				*signed_info = (int32_t)cmd_val;
+			break;
+		}
+		case SSD_DESC_FRU:
+			if ((SSD_FIXED_IS_PRESENT(sense, sense_len, fru) == 0)
+			 || (SSD_FIXED_IS_FILLED(sense, fru) == 0))
+				goto bailout;
+
+			if (sense->fru == 0)
+				goto bailout;
+
+			*info = sense->fru;
+			if (signed_info != NULL)
+				*signed_info = (int8_t)sense->fru;
+			break;
+		default:
+			goto bailout;
+			break;
+		}
+		break;
+	}
+	default: 
+		goto bailout;
+		break;
+	}
+
+	return (0);
+bailout:
+	return (1);
+}
+
+int
+scsi_get_sks(struct scsi_sense_data *sense_data, u_int sense_len, uint8_t *sks)
+{
+	scsi_sense_data_type sense_type;
+
+	if (sense_len == 0)
+		goto bailout;
+
+	sense_type = scsi_sense_type(sense_data);
+
+	switch (sense_type) {
+	case SSD_TYPE_DESC: {
+		struct scsi_sense_data_desc *sense;
+		struct scsi_sense_sks *desc;
+
+		sense = (struct scsi_sense_data_desc *)sense_data;
+
+		desc = (struct scsi_sense_sks *)scsi_find_desc(sense, sense_len,
+							       SSD_DESC_SKS);
+		if (desc == NULL)
+			goto bailout;
+
+		/*
+		 * No need to check the SKS valid bit for descriptor sense.
+		 * If the descriptor is present, it is valid.
+		 */
+		bcopy(desc->sense_key_spec, sks, sizeof(desc->sense_key_spec));
+		break;
+	}
+	case SSD_TYPE_FIXED: {
+		struct scsi_sense_data_fixed *sense;
+
+		sense = (struct scsi_sense_data_fixed *)sense_data;
+
+		if ((SSD_FIXED_IS_PRESENT(sense, sense_len, sense_key_spec)== 0)
+		 || (SSD_FIXED_IS_FILLED(sense, sense_key_spec) == 0))
+			goto bailout;
+
+		if ((sense->sense_key_spec[0] & SSD_SCS_VALID) == 0)
+			goto bailout;
+
+		bcopy(sense->sense_key_spec, sks,sizeof(sense->sense_key_spec));
+		break;
+	}
+	default:
+		goto bailout;
+		break;
+	}
+	return (0);
+bailout:
+	return (1);
+}
+
+/*
+ * Provide a common interface for fixed and descriptor sense to detect
+ * whether we have block-specific sense information.  It is clear by the
+ * presence of the block descriptor in descriptor mode, but we have to
+ * infer from the inquiry data and ILI bit in fixed mode.
+ */
+int
+scsi_get_block_info(struct scsi_sense_data *sense_data, u_int sense_len,
+		    struct scsi_inquiry_data *inq_data, uint8_t *block_bits)
+{
+	scsi_sense_data_type sense_type;
+
+	if (inq_data != NULL) {
+		switch (SID_TYPE(inq_data)) {
+		case T_DIRECT:
+		case T_RBC:
+			break;
+		default:
+			goto bailout;
+			break;
+		}
+	}
+
+	sense_type = scsi_sense_type(sense_data);
+
+	switch (sense_type) {
+	case SSD_TYPE_DESC: {
+		struct scsi_sense_data_desc *sense;
+		struct scsi_sense_block *block;
+
+		sense = (struct scsi_sense_data_desc *)sense_data;
+
+		block = (struct scsi_sense_block *)scsi_find_desc(sense,
+		    sense_len, SSD_DESC_BLOCK);
+		if (block == NULL)
+			goto bailout;
+
+		*block_bits = block->byte3;
+		break;
+	}
+	case SSD_TYPE_FIXED: {
+		struct scsi_sense_data_fixed *sense;
+
+		sense = (struct scsi_sense_data_fixed *)sense_data;
+
+		if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags) == 0)
+			goto bailout;
+
+		if ((sense->flags & SSD_ILI) == 0)
+			goto bailout;
+
+		*block_bits = sense->flags & SSD_ILI;
+		break;
+	}
+	default:
+		goto bailout;
+		break;
+	}
+	return (0);
+bailout:
+	return (1);
+}
+
+int
+scsi_get_stream_info(struct scsi_sense_data *sense_data, u_int sense_len,
+		     struct scsi_inquiry_data *inq_data, uint8_t *stream_bits)
+{
+	scsi_sense_data_type sense_type;
+
+	if (inq_data != NULL) {
+		switch (SID_TYPE(inq_data)) {
+		case T_SEQUENTIAL:
+			break;
+		default:
+			goto bailout;
+			break;
+		}
+	}
+
+	sense_type = scsi_sense_type(sense_data);
+
+	switch (sense_type) {
+	case SSD_TYPE_DESC: {
+		struct scsi_sense_data_desc *sense;
+		struct scsi_sense_stream *stream;
+
+		sense = (struct scsi_sense_data_desc *)sense_data;
+
+		stream = (struct scsi_sense_stream *)scsi_find_desc(sense,
+		    sense_len, SSD_DESC_STREAM);
+		if (stream == NULL)
+			goto bailout;
+
+		*stream_bits = stream->byte3;
+		break;
+	}
+	case SSD_TYPE_FIXED: {
+		struct scsi_sense_data_fixed *sense;
+
+		sense = (struct scsi_sense_data_fixed *)sense_data;
+
+		if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags) == 0)
+			goto bailout;
+
+		if ((sense->flags & (SSD_ILI|SSD_EOM|SSD_FILEMARK)) == 0)
+			goto bailout;
+
+		*stream_bits = sense->flags & (SSD_ILI|SSD_EOM|SSD_FILEMARK);
+		break;
+	}
+	default:
+		goto bailout;
+		break;
+	}
+	return (0);
+bailout:
+	return (1);
+}
+
+void
+scsi_info_sbuf(struct sbuf *sb, uint8_t *cdb, int cdb_len,
+	       struct scsi_inquiry_data *inq_data, uint64_t info)
+{
+	sbuf_printf(sb, "Info: %#jx", info);
+}
+
+void
+scsi_command_sbuf(struct sbuf *sb, uint8_t *cdb, int cdb_len,
+		  struct scsi_inquiry_data *inq_data, uint64_t csi)
+{
+	sbuf_printf(sb, "Command Specific Info: %#jx", csi);
+}
+
+
+void
+scsi_progress_sbuf(struct sbuf *sb, uint16_t progress)
+{
+	sbuf_printf(sb, "Progress: %d%% (%d/%d) complete",
+		    (progress * 100) / SSD_SKS_PROGRESS_DENOM,
+		    progress, SSD_SKS_PROGRESS_DENOM);
+}
+
+/*
+ * Returns 1 for failure (i.e. SKS isn't valid) and 0 for success.
+ */
+int
+scsi_sks_sbuf(struct sbuf *sb, int sense_key, uint8_t *sks)
+{
+	if ((sks[0] & SSD_SKS_VALID) == 0)
+		return (1);
+
+	switch (sense_key) {
+	case SSD_KEY_ILLEGAL_REQUEST: {
+		struct scsi_sense_sks_field *field;
+		int bad_command;
+		char tmpstr[40];
+
+		/*Field Pointer*/
+		field = (struct scsi_sense_sks_field *)sks;
+
+		if (field->byte0 & SSD_SKS_FIELD_CMD)
+			bad_command = 1;
+		else
+			bad_command = 0;
+
+		tmpstr[0] = '\0';
+
+		/* Bit pointer is valid */
+		if (field->byte0 & SSD_SKS_BPV)
+			snprintf(tmpstr, sizeof(tmpstr), "bit %d ",
+				 field->byte0 & SSD_SKS_BIT_VALUE);
+
+		sbuf_printf(sb, "%s byte %d %sis invalid",
+			    bad_command ? "Command" : "Data",
+			    scsi_2btoul(field->field), tmpstr);
+		break;
+	}
+	case SSD_KEY_UNIT_ATTENTION: {
+		struct scsi_sense_sks_overflow *overflow;
+
+		overflow = (struct scsi_sense_sks_overflow *)sks;
+
+		/*UA Condition Queue Overflow*/
+		sbuf_printf(sb, "Unit Attention Condition Queue %s",
+			    (overflow->byte0 & SSD_SKS_OVERFLOW_SET) ?
+			    "Overflowed" : "Did Not Overflow??");
+		break;
+	}
+	case SSD_KEY_RECOVERED_ERROR:
+	case SSD_KEY_HARDWARE_ERROR:
+	case SSD_KEY_MEDIUM_ERROR: {
+		struct scsi_sense_sks_retry *retry;
+
+		/*Actual Retry Count*/
+		retry = (struct scsi_sense_sks_retry *)sks;
+
+		sbuf_printf(sb, "Actual Retry Count: %d",
+			    scsi_2btoul(retry->actual_retry_count));
+		break;
+	}
+	case SSD_KEY_NO_SENSE:
+	case SSD_KEY_NOT_READY: {
+		struct scsi_sense_sks_progress *progress;
+		int progress_val;
+
+		/*Progress Indication*/
+		progress = (struct scsi_sense_sks_progress *)sks;
+		progress_val = scsi_2btoul(progress->progress);
+
+		scsi_progress_sbuf(sb, progress_val);
+		break;
+	}
+	case SSD_KEY_COPY_ABORTED: {
+		struct scsi_sense_sks_segment *segment;
+		char tmpstr[40];
+
+		/*Segment Pointer*/
+		segment = (struct scsi_sense_sks_segment *)sks;
+
+		tmpstr[0] = '\0';
+
+		if (segment->byte0 & SSD_SKS_SEGMENT_BPV)
+			snprintf(tmpstr, sizeof(tmpstr), "bit %d ",
+				 segment->byte0 & SSD_SKS_SEGMENT_BITPTR);
+
+		sbuf_printf(sb, "%s byte %d %sis invalid", (segment->byte0 &
+			    SSD_SKS_SEGMENT_SD) ? "Segment" : "Data",
+			    scsi_2btoul(segment->field), tmpstr);
+		break;
+	}
+	default:
+		sbuf_printf(sb, "Sense Key Specific: %#x,%#x", sks[0],
+			    scsi_2btoul(&sks[1]));
+		break;
+	}
+
+	return (0);
+}
+
+void
+scsi_fru_sbuf(struct sbuf *sb, uint64_t fru)
+{
+	sbuf_printf(sb, "Field Replaceable Unit: %d", (int)fru);
+}
+
+void
+scsi_stream_sbuf(struct sbuf *sb, uint8_t stream_bits, uint64_t info)
+{
+	int need_comma;
+
+	need_comma = 0;
+	/*
+	 * XXX KDM this needs more descriptive decoding.
+	 */
+	if (stream_bits & SSD_DESC_STREAM_FM) {
+		sbuf_printf(sb, "Filemark");
+		need_comma = 1;
+	}
+
+	if (stream_bits & SSD_DESC_STREAM_EOM) {
+		sbuf_printf(sb, "%sEOM", (need_comma) ? "," : "");
+		need_comma = 1;
+	}
+
+	if (stream_bits & SSD_DESC_STREAM_ILI)
+		sbuf_printf(sb, "%sILI", (need_comma) ? "," : "");
+
+	sbuf_printf(sb, ": Info: %#jx", (uintmax_t) info);
+}
+
+void
+scsi_block_sbuf(struct sbuf *sb, uint8_t block_bits, uint64_t info)
+{
+	if (block_bits & SSD_DESC_BLOCK_ILI)
+		sbuf_printf(sb, "ILI: residue %#jx", (uintmax_t) info);
+}
+
+void
+scsi_sense_info_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+		     u_int sense_len, uint8_t *cdb, int cdb_len,
+		     struct scsi_inquiry_data *inq_data,
+		     struct scsi_sense_desc_header *header)
+{
+	struct scsi_sense_info *info;
+
+	info = (struct scsi_sense_info *)header;
+
+	scsi_info_sbuf(sb, cdb, cdb_len, inq_data, scsi_8btou64(info->info));
+}
+
+void
+scsi_sense_command_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			u_int sense_len, uint8_t *cdb, int cdb_len,
+			struct scsi_inquiry_data *inq_data,
+			struct scsi_sense_desc_header *header)
+{
+	struct scsi_sense_command *command;
+
+	command = (struct scsi_sense_command *)header;
+
+	scsi_command_sbuf(sb, cdb, cdb_len, inq_data,
+			  scsi_8btou64(command->command_info));
+}
+
+void
+scsi_sense_sks_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+		    u_int sense_len, uint8_t *cdb, int cdb_len,
+		    struct scsi_inquiry_data *inq_data,
+		    struct scsi_sense_desc_header *header)
+{
+	struct scsi_sense_sks *sks;
+	int error_code, sense_key, asc, ascq;
+
+	sks = (struct scsi_sense_sks *)header;
+
+	scsi_extract_sense_len(sense, sense_len, &error_code, &sense_key,
+			       &asc, &ascq, /*show_errors*/ 1);
+
+	scsi_sks_sbuf(sb, sense_key, sks->sense_key_spec);
+}
+
+void
+scsi_sense_fru_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+		    u_int sense_len, uint8_t *cdb, int cdb_len,
+		    struct scsi_inquiry_data *inq_data,
+		    struct scsi_sense_desc_header *header)
+{
+	struct scsi_sense_fru *fru;
+
+	fru = (struct scsi_sense_fru *)header;
+
+	scsi_fru_sbuf(sb, (uint64_t)fru->fru);
+}
+
+void
+scsi_sense_stream_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+		       u_int sense_len, uint8_t *cdb, int cdb_len,
+		       struct scsi_inquiry_data *inq_data,
+		       struct scsi_sense_desc_header *header)
+{
+	struct scsi_sense_stream *stream;
+	uint64_t info;
+
+	stream = (struct scsi_sense_stream *)header;
+	info = 0;
+
+	scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &info, NULL);
+
+	scsi_stream_sbuf(sb, stream->byte3, info);
+}
+
+void
+scsi_sense_block_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+		      u_int sense_len, uint8_t *cdb, int cdb_len,
+		      struct scsi_inquiry_data *inq_data,
+		      struct scsi_sense_desc_header *header)
+{
+	struct scsi_sense_block *block;
+	uint64_t info;
+
+	block = (struct scsi_sense_block *)header;
+	info = 0;
+
+	scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &info, NULL);
+
+	scsi_block_sbuf(sb, block->byte3, info);
+}
+
+void
+scsi_sense_progress_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			 u_int sense_len, uint8_t *cdb, int cdb_len,
+			 struct scsi_inquiry_data *inq_data,
+			 struct scsi_sense_desc_header *header)
+{
+	struct scsi_sense_progress *progress;
+	const char *sense_key_desc;
+	const char *asc_desc;
+	int progress_val;
+
+	progress = (struct scsi_sense_progress *)header;
+
+	/*
+	 * Get descriptions for the sense key, ASC, and ASCQ in the
+	 * progress descriptor.  These could be different than the values
+	 * in the overall sense data.
+	 */
+	scsi_sense_desc(progress->sense_key, progress->add_sense_code,
+			progress->add_sense_code_qual, inq_data,
+			&sense_key_desc, &asc_desc);
+
+	progress_val = scsi_2btoul(progress->progress);
+
+	/*
+	 * The progress indicator is for the operation described by the
+	 * sense key, ASC, and ASCQ in the descriptor.
+	 */
+	sbuf_cat(sb, sense_key_desc);
+	sbuf_printf(sb, " asc:%x,%x (%s): ", progress->add_sense_code, 
+		    progress->add_sense_code_qual, asc_desc);
+	scsi_progress_sbuf(sb, progress_val);
+}
+
+/*
+ * Generic sense descriptor printing routine.  This is used when we have
+ * not yet implemented a specific printing routine for this descriptor.
+ */
+void
+scsi_sense_generic_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+			u_int sense_len, uint8_t *cdb, int cdb_len,
+			struct scsi_inquiry_data *inq_data,
+			struct scsi_sense_desc_header *header)
+{
+	int i;
+	uint8_t *buf_ptr;
+
+	sbuf_printf(sb, "Descriptor %#x:", header->desc_type);
+
+	buf_ptr = (uint8_t *)&header[1];
+
+	for (i = 0; i < header->length; i++, buf_ptr++)
+		sbuf_printf(sb, " %02x", *buf_ptr);
+}
+
+/*
+ * Keep this list in numeric order.  This speeds the array traversal.
+ */
+struct scsi_sense_desc_printer {
+	uint8_t desc_type;
+	/*
+	 * The function arguments here are the superset of what is needed
+	 * to print out various different descriptors.  Command and
+	 * information descriptors need inquiry data and command type.
+	 * Sense key specific descriptors need the sense key.
+	 *
+	 * The sense, cdb, and inquiry data arguments may be NULL, but the
+	 * information printed may not be fully decoded as a result.
+	 */
+	void (*print_func)(struct sbuf *sb, struct scsi_sense_data *sense,
+			   u_int sense_len, uint8_t *cdb, int cdb_len,
+			   struct scsi_inquiry_data *inq_data,
+			   struct scsi_sense_desc_header *header);
+} scsi_sense_printers[] = {
+	{SSD_DESC_INFO, scsi_sense_info_sbuf},
+	{SSD_DESC_COMMAND, scsi_sense_command_sbuf},
+	{SSD_DESC_SKS, scsi_sense_sks_sbuf},
+	{SSD_DESC_FRU, scsi_sense_fru_sbuf},
+	{SSD_DESC_STREAM, scsi_sense_stream_sbuf},
+	{SSD_DESC_BLOCK, scsi_sense_block_sbuf},
+	{SSD_DESC_PROGRESS, scsi_sense_progress_sbuf}
+};
+
+void
+scsi_sense_desc_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+		     u_int sense_len, uint8_t *cdb, int cdb_len,
+		     struct scsi_inquiry_data *inq_data,
+		     struct scsi_sense_desc_header *header)
+{
+	int i, found;
+
+	for (i = 0, found = 0; i < (sizeof(scsi_sense_printers) /
+	     sizeof(scsi_sense_printers[0])); i++) {
+		struct scsi_sense_desc_printer *printer;
+
+		printer = &scsi_sense_printers[i];
+
+		/*
+		 * The list is sorted, so quit if we've passed our
+		 * descriptor number.
+		 */
+		if (printer->desc_type > header->desc_type)
+			break;
+
+		if (printer->desc_type != header->desc_type)
+			continue;
+
+		printer->print_func(sb, sense, sense_len, cdb, cdb_len,
+				    inq_data, header);
+
+		return;
+	}
+
+	/*
+	 * No specific printing routine, so use the generic routine.
+	 */
+	scsi_sense_generic_sbuf(sb, sense, sense_len, cdb, cdb_len,
+				inq_data, header);
+}
+
+scsi_sense_data_type
+scsi_sense_type(struct scsi_sense_data *sense_data)
+{
+	switch (sense_data->error_code & SSD_ERRCODE) {
+	case SSD_DESC_CURRENT_ERROR:
+	case SSD_DESC_DEFERRED_ERROR:
+		return (SSD_TYPE_DESC);
+		break;
+	case SSD_CURRENT_ERROR:
+	case SSD_DEFERRED_ERROR:
+		return (SSD_TYPE_FIXED);
+		break;
+	default:
+		break;
+	}
+
+	return (SSD_TYPE_NONE);
+}
+
+struct scsi_print_sense_info {
+	struct sbuf *sb;
+	char *path_str;
+	uint8_t *cdb;
+	int cdb_len;
+	struct scsi_inquiry_data *inq_data;
+};
+
+static int
+scsi_print_desc_func(struct scsi_sense_data_desc *sense, u_int sense_len,
+		     struct scsi_sense_desc_header *header, void *arg)
+{
+	struct scsi_print_sense_info *print_info;
+
+	print_info = (struct scsi_print_sense_info *)arg;
+
+	switch (header->desc_type) {
+	case SSD_DESC_INFO:
+	case SSD_DESC_FRU:
+	case SSD_DESC_COMMAND:
+	case SSD_DESC_SKS:
+	case SSD_DESC_BLOCK:
+	case SSD_DESC_STREAM:
+		/*
+		 * We have already printed these descriptors, if they are
+		 * present.
+		 */
+		break;
+	default: {
+		sbuf_printf(print_info->sb, "%s", print_info->path_str);
+		scsi_sense_desc_sbuf(print_info->sb,
+				     (struct scsi_sense_data *)sense, sense_len,
+				     print_info->cdb, print_info->cdb_len,
+				     print_info->inq_data, header);
+		sbuf_printf(print_info->sb, "\n");
+		break;
+	}
+	}
+
+	/*
+	 * Tell the iterator that we want to see more descriptors if they
+	 * are present.
+	 */
+	return (0);
+}
+
+void
+scsi_sense_only_sbuf(struct scsi_sense_data *sense, u_int sense_len,
+		     struct sbuf *sb, char *path_str,
+		     struct scsi_inquiry_data *inq_data, uint8_t *cdb,
+		     int cdb_len)
+{
+	int error_code, sense_key, asc, ascq;
+
+	sbuf_cat(sb, path_str);
+
+	scsi_extract_sense_len(sense, sense_len, &error_code, &sense_key,
+			       &asc, &ascq, /*show_errors*/ 1);
+
+	sbuf_printf(sb, "SCSI sense: ");
+	switch (error_code) {
+	case SSD_DEFERRED_ERROR:
+	case SSD_DESC_DEFERRED_ERROR:
+		sbuf_printf(sb, "Deferred error: ");
+
+		/* FALLTHROUGH */
+	case SSD_CURRENT_ERROR:
+	case SSD_DESC_CURRENT_ERROR:
+	{
+		struct scsi_sense_data_desc *desc_sense;
+		struct scsi_print_sense_info print_info;
+		const char *sense_key_desc;
+		const char *asc_desc;
+		uint8_t sks[3];
+		uint64_t val;
+		int info_valid;
+
+		/*
+		 * Get descriptions for the sense key, ASC, and ASCQ.  If
+		 * these aren't present in the sense data (i.e. the sense
+		 * data isn't long enough), the -1 values that
+		 * scsi_extract_sense_len() returns will yield default
+		 * or error descriptions.
+		 */
+		scsi_sense_desc(sense_key, asc, ascq, inq_data,
+				&sense_key_desc, &asc_desc);
+
+		/*
+		 * We first print the sense key and ASC/ASCQ.
+		 */
+		sbuf_cat(sb, sense_key_desc);
+		sbuf_printf(sb, " asc:%x,%x (%s)\n", asc, ascq, asc_desc);
+
+		/*
+		 * Get the info field if it is valid.
+		 */
+		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO,
+					&val, NULL) == 0)
+			info_valid = 1;
+		else
+			info_valid = 0;
+
+		if (info_valid != 0) {
+			uint8_t bits;
+
+			/*
+			 * Determine whether we have any block or stream
+			 * device-specific information.
+			 */
+			if (scsi_get_block_info(sense, sense_len, inq_data,
+						&bits) == 0) {
+				sbuf_cat(sb, path_str);
+				scsi_block_sbuf(sb, bits, val);
+				sbuf_printf(sb, "\n");
+			} else if (scsi_get_stream_info(sense, sense_len,
+							inq_data, &bits) == 0) {
+				sbuf_cat(sb, path_str);
+				scsi_stream_sbuf(sb, bits, val);
+				sbuf_printf(sb, "\n");
+			} else if (val != 0) {
+				/*
+				 * The information field can be valid but 0.
+				 * If the block or stream bits aren't set,
+				 * and this is 0, it isn't terribly useful
+				 * to print it out.
+				 */
+				sbuf_cat(sb, path_str);
+				scsi_info_sbuf(sb, cdb, cdb_len, inq_data, val);
+				sbuf_printf(sb, "\n");
+			}
+		}
+
+		/* 
+		 * Print the FRU.
+		 */
+		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_FRU,
+					&val, NULL) == 0) {
+			sbuf_cat(sb, path_str);
+			scsi_fru_sbuf(sb, val);
+			sbuf_printf(sb, "\n");
+		}
+
+		/*
+		 * Print any command-specific information.
+		 */
+		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_COMMAND,
+					&val, NULL) == 0) {
+			sbuf_cat(sb, path_str);
+			scsi_command_sbuf(sb, cdb, cdb_len, inq_data, val);
+			sbuf_printf(sb, "\n");
+		}
+
+		/*
+		 * Print out any sense-key-specific information.
+		 */
+		if (scsi_get_sks(sense, sense_len, sks) == 0) {
+			sbuf_cat(sb, path_str);
+			scsi_sks_sbuf(sb, sense_key, sks);
+			sbuf_printf(sb, "\n");
+		}
+
+		/*
+		 * If this is fixed sense, we're done.  If we have
+		 * descriptor sense, we might have more information
+		 * available.
+		 */
+		if (scsi_sense_type(sense) != SSD_TYPE_DESC)
+			break;
+
+		desc_sense = (struct scsi_sense_data_desc *)sense;
+
+		print_info.sb = sb;
+		print_info.path_str = path_str;
+		print_info.cdb = cdb;
+		print_info.cdb_len = cdb_len;
+		print_info.inq_data = inq_data;
+
+		/*
+		 * Print any sense descriptors that we have not already printed.
+		 */
+		scsi_desc_iterate(desc_sense, sense_len, scsi_print_desc_func,
+				  &print_info);
+		break;
+
+	}
+	case -1:
+		/*
+		 * scsi_extract_sense_len() sets values to -1 if the
+		 * show_errors flag is set and they aren't present in the
+		 * sense data.  This means that sense_len is 0.
+		 */
+		sbuf_printf(sb, "No sense data present\n");
+		break;
+	default: {
+		sbuf_printf(sb, "Error code 0x%x", error_code);
+		if (sense->error_code & SSD_ERRCODE_VALID) {
+			struct scsi_sense_data_fixed *fixed_sense;
+
+			fixed_sense = (struct scsi_sense_data_fixed *)sense;
+
+			if (SSD_FIXED_IS_PRESENT(fixed_sense, sense_len, info)){
+				uint32_t info;
+
+				info = scsi_4btoul(fixed_sense->info);
+
+				sbuf_printf(sb, " at block no. %d (decimal)",
+					    info);
+			}
+		}
+		sbuf_printf(sb, "\n");
+		break;
+	}
+	}
+}
+
+/*
  * scsi_sense_sbuf() returns 0 for success and -1 for failure.
  */
 #ifdef _KERNEL
@@ -3059,11 +4419,8 @@ scsi_sense_sbuf(struct cam_device *device, struct
 #ifdef _KERNEL
 	struct	  ccb_getdev *cgd;
 #endif /* _KERNEL */
-	u_int32_t info;
-	int	  error_code;
-	int	  sense_key;
-	int	  asc, ascq;
 	char	  path_str[64];
+	uint8_t	  *cdb;
 
 #ifndef _KERNEL
 	if (device == NULL)
@@ -3161,129 +4518,14 @@ scsi_sense_sbuf(struct cam_device *device, struct
 			sense = &csio->sense_data;
 	}
 
+	if (csio->ccb_h.flags & CAM_CDB_POINTER)
+		cdb = csio->cdb_io.cdb_ptr;
+	else
+		cdb = csio->cdb_io.cdb_bytes;
 
-	sbuf_cat(sb, path_str);
-
-	error_code = sense->error_code & SSD_ERRCODE;
-	sense_key = sense->flags & SSD_KEY;
-
-	sbuf_printf(sb, "SCSI sense: ");
-	switch (error_code) {
-	case SSD_DEFERRED_ERROR:
-		sbuf_printf(sb, "Deferred error: ");
-
-		/* FALLTHROUGH */
-	case SSD_CURRENT_ERROR:
-	{
-		const char *sense_key_desc;
-		const char *asc_desc;
-
-		asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0;
-		ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0;
-		scsi_sense_desc(sense_key, asc, ascq, inq_data,
-				&sense_key_desc, &asc_desc);
-		sbuf_cat(sb, sense_key_desc);
-
-		info = scsi_4btoul(sense->info);
-		
-		if (sense->error_code & SSD_ERRCODE_VALID) {
-
-			switch (sense_key) {
-			case SSD_KEY_NOT_READY:
-			case SSD_KEY_ILLEGAL_REQUEST:
-			case SSD_KEY_UNIT_ATTENTION:
-			case SSD_KEY_DATA_PROTECT:
-				break;
-			case SSD_KEY_BLANK_CHECK:
-				sbuf_printf(sb, " req sz: %d (decimal)", info);
-				break;
-			default:
-				if (info) {
-					if (sense->flags & SSD_ILI) {
-						sbuf_printf(sb, " ILI (length "
-							"mismatch): %d", info);
-			
-					} else {
-						sbuf_printf(sb, " info:%x", 
-							    info);
-					}
-				}
-			}
-		} else if (info) {
-			sbuf_printf(sb, " info?:%x", info);
-		}
-
-		if (sense->extra_len >= 4) {
-			if (bcmp(sense->cmd_spec_info, "\0\0\0\0", 4)) {
-				sbuf_printf(sb, " csi:%x,%x,%x,%x",
-					    sense->cmd_spec_info[0],
-					    sense->cmd_spec_info[1],
-					    sense->cmd_spec_info[2],
-					    sense->cmd_spec_info[3]);
-			}
-		}
-
-		sbuf_printf(sb, " asc:%x,%x (%s)", asc, ascq, asc_desc);
-
-		if (sense->extra_len >= 7 && sense->fru) {
-			sbuf_printf(sb, " field replaceable unit: %x", 
-				    sense->fru);
-		}
-
-		if ((sense->extra_len >= 10)
-		 && (sense->sense_key_spec[0] & SSD_SCS_VALID) != 0) {
-			switch(sense_key) {
-			case SSD_KEY_ILLEGAL_REQUEST: {
-				int bad_command;
-				char tmpstr2[40];
-
-				if (sense->sense_key_spec[0] & 0x40)
-					bad_command = 1;
-				else
-					bad_command = 0;
-
-				tmpstr2[0] = '\0';
-
-				/* Bit pointer is valid */
-				if (sense->sense_key_spec[0] & 0x08)
-					snprintf(tmpstr2, sizeof(tmpstr2),
-						 "bit %d ",
-						sense->sense_key_spec[0] & 0x7);
-				sbuf_printf(sb, ": %s byte %d %sis invalid",
-					    bad_command ? "Command" : "Data",
-					    scsi_2btoul(
-					    &sense->sense_key_spec[1]),
-					    tmpstr2);
-				break;
-			}
-			case SSD_KEY_RECOVERED_ERROR:
-			case SSD_KEY_HARDWARE_ERROR:
-			case SSD_KEY_MEDIUM_ERROR:
-				sbuf_printf(sb, " actual retry count: %d",
-					    scsi_2btoul(
-					    &sense->sense_key_spec[1]));
-				break;
-			default:
-				sbuf_printf(sb, " sks:%#x,%#x", 
-					    sense->sense_key_spec[0],
-					    scsi_2btoul(
-					    &sense->sense_key_spec[1]));
-				break;
-			}
-		}
-		break;
-
-	}
-	default:
-		sbuf_printf(sb, "Error code 0x%x", sense->error_code);
-		if (sense->error_code & SSD_ERRCODE_VALID) {
-			sbuf_printf(sb, " at block no. %d (decimal)",
-				    info = scsi_4btoul(sense->info));
-		}
-	}
-
-	sbuf_printf(sb, "\n");
-
+	scsi_sense_only_sbuf(sense, csio->sense_len - csio->sense_resid, sb,
+			     path_str, inq_data, cdb, csio->cdb_len);
+			 
 #ifdef _KERNEL
 	xpt_free_ccb((union ccb*)cgd);
 #endif /* _KERNEL/!_KERNEL */
@@ -3355,6 +4597,135 @@ scsi_sense_print(struct cam_device *device, struct
 #endif /* _KERNEL/!_KERNEL */
 
 /*
+ * Extract basic sense information.  This is backward-compatible with the
+ * previous implementation.  For new implementations,
+ * scsi_extract_sense_len() is recommended.
+ */
+void
+scsi_extract_sense(struct scsi_sense_data *sense_data, int *error_code,
+		   int *sense_key, int *asc, int *ascq)
+{
+	scsi_extract_sense_len(sense_data, sizeof(*sense_data), error_code,
+			       sense_key, asc, ascq, /*show_errors*/ 0);
+}
+
+/*
+ * Extract basic sense information.  If show_errors is set, sense values
+ * will be set to -1 if they are not present.
+ */
+void
+scsi_extract_sense_len(struct scsi_sense_data *sense_data, u_int sense_len,
+		       int *error_code, int *sense_key, int *asc, int *ascq,
+		       int show_errors)
+{
+	/*
+	 * If we have no length, we have no sense.
+	 */
+	if (sense_len == 0) {
+		if (show_errors == 0) {
+			*error_code = 0;
+			*sense_key = 0;
+			*asc = 0;
+			*ascq = 0;
+		} else {
+			*error_code = -1;
+			*sense_key = -1;
+			*asc = -1;
+			*ascq = -1;
+		}
+		return;
+	}
+
+	*error_code = sense_data->error_code & SSD_ERRCODE;
+
+	switch (*error_code) {
+	case SSD_DESC_CURRENT_ERROR:
+	case SSD_DESC_DEFERRED_ERROR: {
+		struct scsi_sense_data_desc *sense;
+
+		sense = (struct scsi_sense_data_desc *)sense_data;
+
+		if (SSD_DESC_IS_PRESENT(sense, sense_len, sense_key))
+			*sense_key = sense->sense_key & SSD_KEY;
+		else
+			*sense_key = (show_errors) ? -1 : 0;
+
+		if (SSD_DESC_IS_PRESENT(sense, sense_len, add_sense_code))
+			*asc = sense->add_sense_code;
+		else
+			*asc = (show_errors) ? -1 : 0;
+
+		if (SSD_DESC_IS_PRESENT(sense, sense_len, add_sense_code_qual))
+			*ascq = sense->add_sense_code_qual;
+		else
+			*ascq = (show_errors) ? -1 : 0;
+		break;
+	}
+	case SSD_CURRENT_ERROR:
+	case SSD_DEFERRED_ERROR:
+	default: {
+		struct scsi_sense_data_fixed *sense;
+
+		sense = (struct scsi_sense_data_fixed *)sense_data;
+
+		if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags))
+			*sense_key = sense->flags & SSD_KEY;
+		else
+			*sense_key = (show_errors) ? -1 : 0;
+
+		if ((SSD_FIXED_IS_PRESENT(sense, sense_len, add_sense_code))
+		 && (SSD_FIXED_IS_FILLED(sense, add_sense_code)))
+			*asc = sense->add_sense_code;
+		else
+			*asc = (show_errors) ? -1 : 0;
+
+		if ((SSD_FIXED_IS_PRESENT(sense, sense_len,add_sense_code_qual))
+		 && (SSD_FIXED_IS_FILLED(sense, add_sense_code_qual)))
+			*ascq = sense->add_sense_code_qual;
+		else
+			*ascq = (show_errors) ? -1 : 0;
+		break;
+	}
+	}
+}
+
+int
+scsi_get_sense_key(struct scsi_sense_data *sense_data, u_int sense_len,
+		   int show_errors)
+{
+	int error_code, sense_key, asc, ascq;
+
+	scsi_extract_sense_len(sense_data, sense_len, &error_code,
+			       &sense_key, &asc, &ascq, show_errors);
+
+	return (sense_key);
+}
+
+int
+scsi_get_asc(struct scsi_sense_data *sense_data, u_int sense_len,
+	     int show_errors)
+{
+	int error_code, sense_key, asc, ascq;
+
+	scsi_extract_sense_len(sense_data, sense_len, &error_code,
+			       &sense_key, &asc, &ascq, show_errors);
+
+	return (asc);
+}
+
+int
+scsi_get_ascq(struct scsi_sense_data *sense_data, u_int sense_len,
+	      int show_errors)
+{
+	int error_code, sense_key, asc, ascq;
+
+	scsi_extract_sense_len(sense_data, sense_len, &error_code,
+			       &sense_key, &asc, &ascq, show_errors);
+
+	return (ascq);
+}
+
+/*
  * This function currently requires at least 36 bytes, or
  * SHORT_INQUIRY_LENGTH, worth of data to function properly.  If this
  * function needs more or less data in the future, another length should be
Index: sys/cam/scsi/smp_all.h
===================================================================
--- sys/cam/scsi/smp_all.h	(revision 225891)
+++ sys/cam/scsi/smp_all.h	(working copy)
@@ -27,7 +27,7 @@
  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGES.
  *
- * $Id: //depot/users/kenm/FreeBSD-test/sys/cam/scsi/smp_all.h#4 $
+ * $Id$
  * $FreeBSD$
  */
 
Index: sys/dev/mly/mly.c
===================================================================
--- sys/dev/mly/mly.c	(revision 225891)
+++ sys/dev/mly/mly.c	(working copy)
@@ -1294,10 +1294,12 @@ mly_complete_event(struct mly_command *mc)
 static void
 mly_process_event(struct mly_softc *sc, struct mly_event *me)
 {
-    struct scsi_sense_data	*ssd = (struct scsi_sense_data *)&me->sense[0];
-    char			*fp, *tp;
-    int				bus, target, event, class, action;
+    struct scsi_sense_data_fixed *ssd;
+    char			 *fp, *tp;
+    int				 bus, target, event, class, action;
 
+    ssd = (struct scsi_sense_data_fixed *)&me->sense[0];
+
     /* 
      * Errors can be reported using vendor-unique sense data.  In this case, the
      * event code will be 0x1c (Request sense data present), the sense key will
Index: sys/dev/firewire/sbp_targ.c
===================================================================
--- sys/dev/firewire/sbp_targ.c	(revision 225891)
+++ sys/dev/firewire/sbp_targ.c	(working copy)
@@ -41,6 +41,7 @@
 #include <sys/types.h>
 #include <sys/conf.h>
 #include <sys/malloc.h>
+#include <sys/endian.h>
 #if __FreeBSD_version < 500000
 #include <sys/devicestat.h>
 #endif
@@ -632,6 +633,12 @@ sbp_targ_send_status(struct orb_info *orbi, union
 	{
 		struct sbp_cmd_status *sbp_cmd_status;
 		struct scsi_sense_data *sense;
+		int error_code, sense_key, asc, ascq;
+		uint8_t stream_bits;
+		uint8_t sks[3];
+		uint64_t info;
+		int64_t sinfo;
+		int sense_len;
 
 		if (debug)
 			printf("%s: STATUS %d\n", __func__,
@@ -659,36 +666,76 @@ sbp_targ_send_status(struct orb_info *orbi, union
 #endif
 #endif
 
-		if ((sense->error_code & SSD_ERRCODE) == SSD_CURRENT_ERROR)
+		sense_len = ccb->csio.sense_len - ccb->csio.sense_resid;
+		scsi_extract_sense_len(sense, sense_len, &error_code,
+		    &sense_key, &asc, &ascq, /*show_errors*/ 0);
+
+		switch (error_code) {
+		case SSD_CURRENT_ERROR:
+		case SSD_DESC_CURRENT_ERROR:
 			sbp_cmd_status->sfmt = SBP_SFMT_CURR;
-		else
+			break;
+		default:
 			sbp_cmd_status->sfmt = SBP_SFMT_DEFER;
+			break;
+		}
 
-		sbp_cmd_status->valid = (sense->error_code & SSD_ERRCODE_VALID)
-		    ? 1 : 0;
-		sbp_cmd_status->s_key = sense->flags & SSD_KEY;
-		sbp_cmd_status->mark = (sense->flags & SSD_FILEMARK)? 1 : 0;
-		sbp_cmd_status->eom = (sense->flags & SSD_EOM) ? 1 : 0;
-		sbp_cmd_status->ill_len = (sense->flags & SSD_ILI) ? 1 : 0;
+		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &info,
+					&sinfo) == 0) {
+			uint32_t info_trunc;
+			sbp_cmd_status->valid = 1;
+			info_trunc = info;
 
-		bcopy(&sense->info[0], &sbp_cmd_status->info, 4);
+			sbp_cmd_status->info = htobe32(info_trunc);
+		} else {
+			sbp_cmd_status->valid = 0;
+		}
 
-		if (sense->extra_len <= 6)
-			/* add_sense_code(_qual), info, cmd_spec_info */
-			sbp_status->len = 4;
-		else
-			/* fru, sense_key_spec */
+		sbp_cmd_status->s_key = sense_key;
+
+		if (scsi_get_stream_info(sense, sense_len, NULL,
+					 &stream_bits) == 0) {
+			sbp_cmd_status->mark =
+			    (stream_bits & SSD_FILEMARK) ? 1 : 0;
+			sbp_cmd_status->eom =
+			    (stream_bits & SSD_EOM) ? 1 : 0;
+			sbp_cmd_status->ill_len =
+			    (stream_bits & SSD_ILI) ? 1 : 0;
+		} else {
+			sbp_cmd_status->mark = 0;
+			sbp_cmd_status->eom = 0;
+			sbp_cmd_status->ill_len = 0;
+		}
+
+
+		/* add_sense_code(_qual), info, cmd_spec_info */
+		sbp_status->len = 4;
+
+		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_COMMAND,
+					&info, &sinfo) == 0) {
+			uint32_t cmdspec_trunc;
+
+			cmdspec_trunc = info;
+
+			sbp_cmd_status->cdb = htobe32(cmdspec_trunc);
+		}
+
+		sbp_cmd_status->s_code = asc;
+		sbp_cmd_status->s_qlfr = ascq;
+
+		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_FRU, &info,
+					&sinfo) == 0) {
+			sbp_cmd_status->fru = (uint8_t)info;
 			sbp_status->len = 5;
-			
-		bcopy(&sense->cmd_spec_info[0], &sbp_cmd_status->cdb, 4);
+		} else {
+			sbp_cmd_status->fru = 0;
+		}
 
-		sbp_cmd_status->s_code = sense->add_sense_code;
-		sbp_cmd_status->s_qlfr = sense->add_sense_code_qual;
-		sbp_cmd_status->fru = sense->fru;
+		if (scsi_get_sks(sense, sense_len, sks) == 0) {
+			bcopy(sks, &sbp_cmd_status->s_keydep[0], sizeof(sks));
+			sbp_status->len = 5;
+		}
 
-		bcopy(&sense->sense_key_spec[0],
-		    &sbp_cmd_status->s_keydep[0], 3);
-
 		break;
 	}
 	default:
Index: sys/dev/firewire/sbp.c
===================================================================
--- sys/dev/firewire/sbp.c	(revision 225891)
+++ sys/dev/firewire/sbp.c	(working copy)
@@ -1515,10 +1515,10 @@ static void
 sbp_scsi_status(struct sbp_status *sbp_status, struct sbp_ocb *ocb)
 {
 	struct sbp_cmd_status *sbp_cmd_status;
-	struct scsi_sense_data *sense;
+	struct scsi_sense_data_fixed *sense;
 
 	sbp_cmd_status = (struct sbp_cmd_status *)sbp_status->data;
-	sense = &ocb->ccb->csio.sense_data;
+	sense = (struct scsi_sense_data_fixed *)&ocb->ccb->csio.sense_data;
 
 SBP_DEBUG(0)
 	sbp_print_scsi_cmd(ocb);
Index: sys/dev/ciss/ciss.c
===================================================================
--- sys/dev/ciss/ciss.c	(revision 225891)
+++ sys/dev/ciss/ciss.c	(working copy)
@@ -3249,13 +3249,17 @@ ciss_cam_complete(struct ciss_request *cr)
 	      ce->sense_length, ce->residual_count);
 	bzero(&csio->sense_data, SSD_FULL_SIZE);
 	bcopy(&ce->sense_info[0], &csio->sense_data, ce->sense_length);
-	csio->sense_len = ce->sense_length;
+	if (csio->sense_len > ce->sense_length)
+		csio->sense_resid = csio->sense_len - ce->sense_length;
+	else
+		csio->sense_resid = 0;
 	csio->resid = ce->residual_count;
 	csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
 #ifdef CISS_DEBUG
 	{
 	    struct scsi_sense_data	*sns = (struct scsi_sense_data *)&ce->sense_info[0];
-	    debug(0, "sense key %x", sns->flags & SSD_KEY);
+	    debug(0, "sense key %x", scsi_get_sense_key(sns, csio->sense_len -
+		  csio->sense_resid, /*show_errors*/ 1));
 	}
 #endif
 	break;
Index: sys/dev/mps/mps_sas.c
===================================================================
--- sys/dev/mps/mps_sas.c	(revision 225891)
+++ sys/dev/mps/mps_sas.c	(working copy)
@@ -1490,7 +1490,6 @@ mpssas_scsiio_complete(struct mps_softc *sc, struc
 	MPI2_SCSI_IO_REPLY *rep;
 	union ccb *ccb;
 	struct mpssas_softc *sassc;
-	u_int sense_len;
 	int dir = 0;
 
 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
@@ -1666,10 +1665,16 @@ mpssas_scsiio_complete(struct mps_softc *sc, struc
 	}
 
 	if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
-		sense_len = MIN(rep->SenseCount,
-		    sizeof(struct scsi_sense_data));
-		if (sense_len < rep->SenseCount)
-			ccb->csio.sense_resid = rep->SenseCount - sense_len;
+		int sense_len;
+
+		if (rep->SenseCount < ccb->csio.sense_len)
+			ccb->csio.sense_resid = ccb->csio.sense_len -
+				rep->SenseCount;
+		else
+			ccb->csio.sense_resid = 0;
+
+		sense_len = min(rep->SenseCount, ccb->csio.sense_len -
+		    ccb->csio.sense_resid);
 		bcopy(cm->cm_sense, &ccb->csio.sense_data, sense_len);
 		ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
 	}
Index: sys/dev/mpt/mpt_cam.c
===================================================================
--- sys/dev/mpt/mpt_cam.c	(revision 225891)
+++ sys/dev/mpt/mpt_cam.c	(working copy)
@@ -3167,12 +3167,19 @@ mpt_scsi_reply_frame_handler(struct mpt_softc *mpt
 
 	if ((sstate & MPI_SCSI_STATE_AUTOSENSE_VALID) != 0
 	 && (ccb->ccb_h.flags & (CAM_SENSE_PHYS | CAM_SENSE_PTR)) == 0) {
+		uint32_t sense_returned;
+
 		ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
-		ccb->csio.sense_resid =
-		    ccb->csio.sense_len - le32toh(scsi_io_reply->SenseCount);
+		
+		sense_returned = le32toh(scsi_io_reply->SenseCount);
+		if (sense_returned < ccb->csio.sense_len)
+			ccb->csio.sense_resid = ccb->csio.sense_len -
+						sense_returned;
+		else
+			ccb->csio.sense_resid = 0;
+
 		bcopy(req->sense_vbuf, &ccb->csio.sense_data,
-		    min(ccb->csio.sense_len,
-		    le32toh(scsi_io_reply->SenseCount)));
+		    min(ccb->csio.sense_len, sense_returned));
 	}
 
 	if ((sstate & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) != 0) {
Index: sys/dev/iir/iir.c
===================================================================
--- sys/dev/iir/iir.c	(revision 225891)
+++ sys/dev/iir/iir.c	(working copy)
@@ -1839,13 +1839,20 @@ gdt_sync_event(struct gdt_softc *gdt, int service,
         } else {
             /* error */
             if (gccb->gc_service == GDT_CACHESERVICE) {
+                struct scsi_sense_data *sense;
+
                 ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
                 ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
                 ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
                 bzero(&ccb->csio.sense_data, ccb->csio.sense_len);
-                ccb->csio.sense_data.error_code =
-                    SSD_CURRENT_ERROR | SSD_ERRCODE_VALID;
-                ccb->csio.sense_data.flags = SSD_KEY_NOT_READY;
+                sense = &ccb->csio.sense_data;
+                scsi_set_sense_data(sense,
+                                    /*sense_format*/ SSD_TYPE_NONE,
+                                    /*current_error*/ 1,
+                                    /*sense_key*/ SSD_KEY_NOT_READY,
+                                    /*asc*/ 0x4,
+                                    /*ascq*/ 0x01,
+                                    SSD_ELEM_NONE);
 
                 gdt->sc_dvr.size = sizeof(gdt->sc_dvr.eu.sync);
                 gdt->sc_dvr.eu.sync.ionode  = gdt->sc_hanum;
Index: sys/dev/iscsi/initiator/iscsi_subr.c
===================================================================
--- sys/dev/iscsi/initiator/iscsi_subr.c	(revision 225891)
+++ sys/dev/iscsi/initiator/iscsi_subr.c	(working copy)
@@ -153,6 +153,7 @@ getSenseData(u_int status, union ccb *ccb, pduq_t
      scsi_rsp_t		*cmd = &pp->ipdu.scsi_rsp;
      caddr_t		bp;
      int		sense_len, mustfree = 0;
+     int		error_code, sense_key, asc, ascq;
 
      bp = mtod(pq->mp, caddr_t);
      if((sense_len = scsi_2btoul(bp)) == 0)
@@ -174,10 +175,14 @@ getSenseData(u_int status, union ccb *ccb, pduq_t
      scsi->sense_resid = 0;
      if(cmd->flag & (BIT(1)|BIT(2)))
 	  scsi->sense_resid = ntohl(pp->ipdu.scsi_rsp.rcnt);
+
+     scsi_extract_sense_len(sense, scsi->sense_len - scsi->sense_resid,
+	&error_code, &sense_key, &asc, &ascq, /*show_errors*/ 1);
+
      debug(3, "sense_len=%d rcnt=%d sense_resid=%d dsl=%d error_code=%x flags=%x",
 	   sense_len,
 	   ntohl(pp->ipdu.scsi_rsp.rcnt), scsi->sense_resid,
-	   pp->ds_len, sense->error_code, sense->flags);
+	   pp->ds_len, error_code, sense_key);
 
      if(mustfree)
 	  free(bp, M_ISCSI);
Index: sys/dev/usb/storage/umass.c
===================================================================
--- sys/dev/usb/storage/umass.c	(revision 225891)
+++ sys/dev/usb/storage/umass.c	(working copy)
@@ -2344,14 +2344,14 @@ umass_cam_action(struct cam_sim *sim, union ccb *c
 					 */
 					if ((sc->sc_quirks & (NO_INQUIRY_EVPD | NO_INQUIRY)) &&
 					    (sc->sc_transfer.cmd_data[1] & SI_EVPD)) {
-						struct scsi_sense_data *sense;
 
-						sense = &ccb->csio.sense_data;
-						bzero(sense, sizeof(*sense));
-						sense->error_code = SSD_CURRENT_ERROR;
-						sense->flags = SSD_KEY_ILLEGAL_REQUEST;
-						sense->add_sense_code = 0x24;
-						sense->extra_len = 10;
+						scsi_set_sense_data(&ccb->csio.sense_data,
+							/*sense_format*/ SSD_TYPE_NONE,
+							/*current_error*/ 1,
+							/*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
+							/*asc*/ 0x24,
+							/*ascq*/ 0x00,
+							/*extra args*/ SSD_ELEM_NONE);
 						ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
 						ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR |
 						    CAM_AUTOSNS_VALID;
@@ -2631,21 +2631,24 @@ umass_cam_sense_cb(struct umass_softc *sc, union c
     uint8_t status)
 {
 	uint8_t *cmd;
-	uint8_t key;
 
 	switch (status) {
 	case STATUS_CMD_OK:
 	case STATUS_CMD_UNKNOWN:
-	case STATUS_CMD_FAILED:
+	case STATUS_CMD_FAILED: {
+		int key, sense_len;
 
+		ccb->csio.sense_resid = residue;
+		sense_len = ccb->csio.sense_len - ccb->csio.sense_resid;
+		key = scsi_get_sense_key(&ccb->csio.sense_data, sense_len,
+					 /*show_errors*/ 1);
+
 		if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) {
 			cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr);
 		} else {
 			cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes);
 		}
 
-		key = (ccb->csio.sense_data.flags & SSD_KEY);
-
 		/*
 		 * Getting sense data always succeeds (apart from wire
 		 * failures):
@@ -2704,7 +2707,7 @@ umass_cam_sense_cb(struct umass_softc *sc, union c
 		}
 		xpt_done(ccb);
 		break;
-
+	}
 	default:
 		DPRINTF(sc, UDMASS_SCSI, "Autosense failed, "
 		    "status %d\n", status);
Index: sys/dev/isp/isp_freebsd.h
===================================================================
--- sys/dev/isp/isp_freebsd.h	(revision 225891)
+++ sys/dev/isp/isp_freebsd.h	(working copy)
@@ -438,11 +438,19 @@ default:							\
 #define	XS_SNSP(ccb)		(&(ccb)->sense_data)
 
 #define	XS_SNSLEN(ccb)		\
-	imin((sizeof((ccb)->sense_data)), ccb->sense_len)
+	imin((sizeof((ccb)->sense_data)), ccb->sense_len - ccb->sense_resid)
 
-#define	XS_SNSKEY(ccb)		((ccb)->sense_data.flags & 0xf)
-#define	XS_SNSASC(ccb)		((ccb)->sense_data.add_sense_code)
-#define	XS_SNSASCQ(ccb)		((ccb)->sense_data.add_sense_code_qual)
+#define	XS_SNSKEY(ccb)		(scsi_get_sense_key(&(ccb)->sense_data, \
+				 ccb->sense_len - ccb->sense_resid, 	\
+				 /*show_errors*/ 1))
+
+#define	XS_SNSASC(ccb)		(scsi_get_asc(&(ccb)->sense_data,	\
+				 ccb->sense_len - ccb->sense_resid, 	\
+				 /*show_errors*/ 1))
+
+#define	XS_SNSASCQ(ccb)		(scsi_get_ascq(&(ccb)->sense_data,	\
+				 ccb->sense_len - ccb->sense_resid, 	\
+				 /*show_errors*/ 1))
 #define	XS_TAG_P(ccb)	\
 	(((ccb)->ccb_h.flags & CAM_TAG_ACTION_VALID) && \
 	 (ccb)->tag_action != CAM_TAG_ACTION_NONE)
@@ -476,9 +484,13 @@ default:							\
 #define	XS_INITERR(ccb)		\
 	XS_SETERR(ccb, CAM_REQ_INPROG), (ccb)->ccb_h.spriv_field0 = 0
 
-#define	XS_SAVE_SENSE(xs, sense_ptr, sense_len)		\
-	(xs)->ccb_h.status |= CAM_AUTOSNS_VALID;	\
-	memcpy(&(xs)->sense_data, sense_ptr, imin(XS_SNSLEN(xs), sense_len))
+#define	XS_SAVE_SENSE(xs, sense_ptr, slen)	do {			\
+		(xs)->ccb_h.status |= CAM_AUTOSNS_VALID;		\
+		memcpy(&(xs)->sense_data, sense_ptr, imin(XS_SNSLEN(xs),\
+		       slen)); 						\
+		if (slen < (xs)->sense_len) 				\
+			(xs)->sense_resid = (xs)->sense_len - slen;	\
+	} while (0);
 
 #define	XS_SENSE_VALID(xs)	(((xs)->ccb_h.status & CAM_AUTOSNS_VALID) != 0)
 


More information about the freebsd-current mailing list