From 29df79f57fefea97264cf3efd361d103a63bbe33 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sat, 22 Jun 2013 13:18:26 +0200
Subject: [PATCH] WIP SAS Management Protocol support (from FreeBSD)

---
 lib/libcam/Makefile          |   3 +-
 sys/bus/cam/cam.c            |  36 +++
 sys/bus/cam/cam.h            |   8 +-
 sys/bus/cam/cam/Makefile     |   2 +-
 sys/bus/cam/cam_ccb.h        |  88 +++++-
 sys/bus/cam/cam_periph.c     |  25 +-
 sys/bus/cam/cam_xpt.c        | 214 ++++++++++++---
 sys/bus/cam/scsi/scsi_all.c  |  28 ++
 sys/bus/cam/scsi/scsi_all.h  | 163 +++++++++++-
 sys/bus/cam/scsi/scsi_pass.c |   6 +-
 sys/bus/cam/scsi/smp_all.c   | 617 +++++++++++++++++++++++++++++++++++++++++++
 sys/bus/cam/scsi/smp_all.h   | 520 ++++++++++++++++++++++++++++++++++++
 sys/conf/files               |   1 +
 sys/dev/raid/mps/mps_sas.c   |   8 -
 14 files changed, 1658 insertions(+), 61 deletions(-)
 create mode 100644 sys/bus/cam/scsi/smp_all.c
 create mode 100644 sys/bus/cam/scsi/smp_all.h

diff --git a/lib/libcam/Makefile b/lib/libcam/Makefile
index 99ee3fc..e047c28 100644
--- a/lib/libcam/Makefile
+++ b/lib/libcam/Makefile
@@ -1,7 +1,8 @@
 # $FreeBSD: src/lib/libcam/Makefile,v 1.6.2.2 2001/04/25 10:04:22 ru Exp $
 
 LIB=	cam
-SRCS=	camlib.c scsi_cmdparse.c scsi_all.c scsi_da.c scsi_sa.c cam.c
+SRCS=	camlib.c scsi_cmdparse.c scsi_all.c scsi_da.c scsi_sa.c cam.c \
+	smp_all.c
 INCS=	camlib.h
 WARNS?=	2
 
diff --git a/sys/bus/cam/cam.c b/sys/bus/cam/cam.c
index c26284b..a854eb3 100644
--- a/sys/bus/cam/cam.c
+++ b/sys/bus/cam/cam.c
@@ -48,6 +48,7 @@
 #include "cam.h"
 #include "cam_ccb.h"
 #include "scsi/scsi_all.h"
+#include "scsi/smp_all.h"
 #include <sys/sbuf.h>
 
 #ifdef _KERNEL
@@ -86,6 +87,7 @@ const struct cam_status_entry cam_status_table[] = {
 	{ CAM_UNREC_HBA_ERROR,	 "Unrecoverable Host Bus Adapter Error"	     },
 	{ CAM_REQ_TOO_BIG,	 "The request was too large for this host"   },
 	{ CAM_REQUEUE_REQ,	 "Unconditionally Re-queue Request",	     },
+	{ CAM_SMP_STATUS_ERROR,	 "SMP Status Error"                          },
 	{ CAM_IDE,		 "Initiator Detected Error Message Received" },
 	{ CAM_RESRC_UNAVAIL,	 "Resource Unavailable"			     },
 	{ CAM_UNACKED_EVENT,	 "Unacknowledged Event by Host"		     },
@@ -245,6 +247,21 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str,
 				break;
 			}
 			break;
+		case XPT_SMP_IO:
+			switch (proto_flags & CAM_EPF_LEVEL_MASK) {
+			case CAM_EPF_NONE:
+				break;
+			case CAM_EPF_ALL:
+				proto_flags |= CAM_ESMF_PRINT_FULL_CMD;
+				/* FALLTHROUGH */
+			case CAM_EPF_NORMAL:
+			case CAM_EPF_MINIMAL:
+				proto_flags |= CAM_ESMF_PRINT_STATUS;
+				/* FALLTHROUGH */
+			default:
+				break;
+			}
+			break;
 		default:
 			break;
 	}
@@ -270,6 +287,12 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str,
 			sbuf_printf(&sb, "\n");
 
 			break;
+		case XPT_SMP_IO:
+			smp_command_sbuf(&ccb->smpio, &sb, path_str, 79 -
+					 strlen(path_str), (proto_flags &
+					 CAM_ESMF_PRINT_FULL_CMD) ? 79 : 0);
+			sbuf_printf(&sb, "\n");
+			break;
 		default:
 			break;
 		}
@@ -324,6 +347,19 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str,
 #endif /* _KERNEL/!_KERNEL */
 			}
 			break;
+		case XPT_SMP_IO:
+			if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=
+			     CAM_SMP_STATUS_ERROR)
+				break;
+
+			if (proto_flags & CAM_ESF_PRINT_STATUS) {
+				sbuf_cat(&sb, path_str);
+				sbuf_printf(&sb, "SMP status: %s (%#x)\n",
+				    smp_error_desc(ccb->smpio.smp_response[2]),
+						   ccb->smpio.smp_response[2]);
+			}
+			/* There is no SMP equivalent to SCSI sense. */
+			break;
 		default:
 			break;
 		}
diff --git a/sys/bus/cam/cam.h b/sys/bus/cam/cam.h
index 47b2b0b..b450ff5 100644
--- a/sys/bus/cam/cam.h
+++ b/sys/bus/cam/cam.h
@@ -26,7 +26,6 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/cam.h,v 1.6.2.1 2000/03/17 22:36:21 peter Exp $
- * $DragonFly: src/sys/bus/cam/cam.h,v 1.7 2007/11/18 17:59:54 pavalos Exp $
  */
 
 #ifndef _CAM_CAM_H
@@ -131,6 +130,7 @@ typedef enum {
 				 * requests for the target at the sim level
 				 * back into the XPT queue.
 				 */
+	CAM_SMP_STATUS_ERROR,	/* SMP error, look at error code in CCB */
 	CAM_IDE = 0x33,		/* Initiator Detected Error */
 	CAM_RESRC_UNAVAIL,	/* Resource Unavailable */
 	CAM_UNACKED_EVENT,	/* Unacknowledged Event by Host */
@@ -181,6 +181,12 @@ typedef enum {
 	CAM_ESF_PRINT_SENSE	= 0x20
 } cam_error_scsi_flags;
 
+typedef enum {
+	CAM_ESMF_PRINT_NONE	= 0x00,
+	CAM_ESMF_PRINT_STATUS	= 0x10,
+	CAM_ESMF_PRINT_FULL_CMD	= 0x20,
+} cam_error_smp_flags;
+
 struct cam_status_entry
 {
 	cam_status  status_code;
diff --git a/sys/bus/cam/cam/Makefile b/sys/bus/cam/cam/Makefile
index 544e731..f0fa3b4 100644
--- a/sys/bus/cam/cam/Makefile
+++ b/sys/bus/cam/cam/Makefile
@@ -1,5 +1,4 @@
 # $FreeBSD: src/sys/modules/cam/Makefile,v 1.10.2.2 2004/04/19 17:32:16 njl Exp $
-# $DragonFly: src/sys/bus/cam/cam/Makefile,v 1.1 2007/11/12 07:27:50 pavalos Exp $
 
 .PATH: ${.CURDIR}/.. ${.CURDIR}/../scsi
 
@@ -23,6 +22,7 @@ SRCS+=	scsi_pt.c
 SRCS+=	scsi_sa.c
 SRCS+=	scsi_ses.c
 SRCS+=	scsi_targ_bh.c scsi_target.c
+SRCS+=	smp_all.c
 
 EXPORT_SYMS=	YES	# XXX evaluate
 
diff --git a/sys/bus/cam/cam_ccb.h b/sys/bus/cam/cam_ccb.h
index 2b172b5..f44a281 100644
--- a/sys/bus/cam/cam_ccb.h
+++ b/sys/bus/cam/cam_ccb.h
@@ -63,7 +63,7 @@ typedef enum {
 					      */
 	CAM_SCATTER_VALID	= 0x00000010,/* Scatter/gather list is valid  */
 	CAM_DIS_AUTOSENSE	= 0x00000020,/* Disable autosense feature     */
-	CAM_DIR_RESV		= 0x00000000,/* Data direction (00:reserved)  */
+	CAM_DIR_BOTH		= 0x00000000,/* Data direction (00:IN/OUT)    */
 	CAM_DIR_IN		= 0x00000040,/* Data direction (01:DATA IN)   */
 	CAM_DIR_OUT		= 0x00000080,/* Data direction (10:DATA OUT)  */
 	CAM_DIR_NONE		= 0x000000C0,/* Data direction (11:no data)   */
@@ -139,6 +139,8 @@ typedef enum {
 				/* Path statistics (error counts, etc.) */
 	XPT_GDEV_STATS		= 0x0c,
 				/* Device statistics (error counts, etc.) */
+	XPT_GDEV_ADVINFO	= 0x0e,
+				/* Advanced device information */
 /* SCSI Control Functions: 0x10->0x1F */
 	XPT_ABORT		= 0x10,
 				/* Abort the specified CCB */
@@ -167,6 +169,9 @@ typedef enum {
 				 * a device give the sector size and
 				 * volume size.
 				 */
+	XPT_SMP_IO		= 0x1b | XPT_FC_DEV_QUEUED,
+				/* Serial Management Protocol */
+
 
 /* HBA engine commands 0x20->0x2F */
 	XPT_ENG_INQ		= 0x20 | XPT_FC_XPT_ONLY,
@@ -569,6 +574,32 @@ struct ccb_pathstats {
 	struct	timeval last_reset;	/* Uptime of last bus reset/loop init */
 };
 
+typedef enum {
+	SMP_FLAG_NONE		= 0x00,
+	SMP_FLAG_REQ_SG		= 0x01,
+	SMP_FLAG_RSP_SG		= 0x02
+} ccb_smp_pass_flags;
+
+/*
+ * Serial Management Protocol CCB
+ * XXX Currently the semantics for this CCB are that it is executed either
+ * by the addressed device, or that device's parent (i.e. an expander for
+ * any device on an expander) if the addressed device doesn't support SMP.
+ * Later, once we have the ability to probe SMP-only devices and put them
+ * in CAM's topology, the CCB will only be executed by the addressed device
+ * if possible.
+ */
+struct ccb_smpio {
+	struct ccb_hdr		ccb_h;
+	uint8_t			*smp_request;
+	int			smp_request_len;
+	uint16_t		smp_request_sglist_cnt;
+	uint8_t			*smp_response;
+	int			smp_response_len;
+	uint16_t		smp_response_sglist_cnt;
+	ccb_smp_pass_flags	flags;
+};
+
 typedef union {
 	u_int8_t *sense_ptr;		/*
 					 * Pointer to storage
@@ -873,6 +904,26 @@ struct ccb_eng_exec {	/* This structure must match SCSIIO size */
 #define XPT_CCB_INVALID	-1	/* for signaling a bad CCB to free */
 
 /*
+ * CCB for getting advanced device information.  This operates in a fashion
+ * similar to XPT_GDEV_TYPE.  Specify the target in ccb_h, the buffer
+ * type requested, and provide a buffer size/buffer to write to.  If the
+ * buffer is too small, the handler will set GDEVAI_FLAG_MORE.
+ */
+struct ccb_getdev_advinfo {
+	struct ccb_hdr ccb_h;
+	uint32_t flags;
+#define	CGDAI_FLAG_TRANSPORT	0x1
+#define	CGDAI_FLAG_PROTO	0x2
+	uint32_t buftype;		/* IN: Type of data being requested */
+	/* NB: buftype is interpreted on a per-transport basis */
+#define	CGDAI_TYPE_SCSI_DEVID	1
+	off_t bufsiz;			/* IN: Size of external buffer */
+#define	CAM_SCSI_DEVID_MAXLEN	65536	/* length in buffer is an uint16_t */
+	off_t provsiz;			/* OUT: Size required/used */
+	uint8_t *buf;			/* IN/OUT: Buffer for requested data */
+};
+
+/*
  * Union of all CCB types for kernel space allocation.  This union should
  * never be used for manipulating CCBs - its only use is for the allocation
  * and deallocation of raw CCB space and is the return type of xpt_ccb_alloc
@@ -903,12 +954,21 @@ union ccb {
 	struct	ccb_notify_ack		cna;
 	struct	ccb_eng_inq		cei;
 	struct	ccb_eng_exec		cee;
+	struct	ccb_smpio		smpio;
 	struct 	ccb_rescan		crcn;
 	struct  ccb_debug		cdbg;
+	struct	ccb_getdev_advinfo	cgdai;
 };
 
 __BEGIN_DECLS
 static __inline void
+cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
+	       void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags,
+	       uint8_t *smp_request, int smp_request_len,
+	       uint8_t *smp_response, int smp_response_len,
+	       uint32_t timeout);
+
+static __inline void
 cam_fill_csio(struct ccb_scsiio *csio, u_int32_t retries,
 	      void (*cbfcnp)(struct cam_periph *, union ccb *),
 	      u_int32_t flags, u_int8_t tag_action,
@@ -963,6 +1023,32 @@ cam_fill_ctio(struct ccb_scsiio *csio, u_int32_t retries,
 	csio->init_id = init_id;
 }
 
+static __inline void
+cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
+	       void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags,
+	       uint8_t *smp_request, int smp_request_len,
+	       uint8_t *smp_response, int smp_response_len,
+	       uint32_t timeout)
+{
+#ifdef _KERNEL
+	KASSERT((flags & CAM_DIR_MASK) == CAM_DIR_BOTH,
+		("direction != CAM_DIR_BOTH"));
+	KASSERT((smp_request != NULL) && (smp_response != NULL),
+		("need valid request and response buffers"));
+	KASSERT((smp_request_len != 0) && (smp_response_len != 0),
+		("need non-zero request and response lengths"));
+#endif /*_KERNEL*/
+	smpio->ccb_h.func_code = XPT_SMP_IO;
+	smpio->ccb_h.flags = flags;
+	smpio->ccb_h.retry_count = retries;
+	smpio->ccb_h.cbfcnp = cbfcnp;
+	smpio->ccb_h.timeout = timeout;
+	smpio->smp_request = smp_request;
+	smpio->smp_request_len = smp_request_len;
+	smpio->smp_response = smp_response;
+	smpio->smp_response_len = smp_response_len;
+}
+
 void cam_calc_geometry(struct ccb_calc_geometry *ccg, int extended);
 
 __END_DECLS
diff --git a/sys/bus/cam/cam_periph.c b/sys/bus/cam/cam_periph.c
index 4f8b069..59d0575 100644
--- a/sys/bus/cam/cam_periph.c
+++ b/sys/bus/cam/cam_periph.c
@@ -27,7 +27,6 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/cam_periph.c,v 1.70 2008/02/12 11:07:33 raj Exp $
- * $DragonFly: src/sys/bus/cam/cam_periph.c,v 1.41 2008/07/18 00:07:21 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -638,6 +637,21 @@ cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo)
 		mapinfo->dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK;
 		numbufs = 1;
 		break;
+	case XPT_SMP_IO:
+		data_ptrs[0] = &ccb->smpio.smp_request;
+		lengths[0] = ccb->smpio.smp_request_len;
+		mapinfo->dirs[0] = CAM_DIR_OUT;
+		data_ptrs[1] = &ccb->smpio.smp_response;
+		lengths[1] = ccb->smpio.smp_response_len;
+		mapinfo->dirs[1] = CAM_DIR_IN;
+		numbufs = 2;
+		break;
+	case XPT_GDEV_ADVINFO:
+		data_ptrs[0] = (uint8_t **)&ccb->cgdai.buf;
+		lengths[0] = ccb->cgdai.bufsiz;
+		mapinfo->dirs[0] = CAM_DIR_IN;
+		numbufs = 1;
+		break;
 	default:
 		return(EINVAL);
 		break; /* NOTREACHED */
@@ -798,6 +812,15 @@ cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo)
 		data_ptrs[0] = &ccb->csio.data_ptr;
 		numbufs = min(mapinfo->num_bufs_used, 1);
 		break;
+	case XPT_SMP_IO:
+		numbufs = min(mapinfo->num_bufs_used, 2);
+		data_ptrs[0] = &ccb->smpio.smp_request;
+		data_ptrs[1] = &ccb->smpio.smp_response;
+		break;
+	case XPT_GDEV_ADVINFO:
+		numbufs = min(mapinfo->num_bufs_used, 1);
+		data_ptrs[0] = (uint8_t **)&ccb->cgdai.buf;
+		break;
 	default:
 		/* allow ourselves to be swapped once again */
 		return;
diff --git a/sys/bus/cam/cam_xpt.c b/sys/bus/cam/cam_xpt.c
index 26b1717..bd87cca 100644
--- a/sys/bus/cam/cam_xpt.c
+++ b/sys/bus/cam/cam_xpt.c
@@ -140,6 +140,10 @@ struct cam_ed {
 	cam_xport	 transport;
 	u_int		 transport_version;
 	struct		 scsi_inquiry_data inq_data;
+	uint8_t		 *supported_vpds;
+	uint8_t		 supported_vpds_len;
+	uint32_t	 device_id_len;
+	uint8_t		 *device_id;
 	u_int8_t	 inq_flags;	/*
 					 * Current settings for inquiry flags.
 					 * This allows us to override settings
@@ -212,7 +216,7 @@ struct xpt_quirk_entry {
 	struct scsi_inquiry_pattern inq_pat;
 	u_int8_t quirks;
 #define	CAM_QUIRK_NOLUNS	0x01
-#define	CAM_QUIRK_NOSERIAL	0x02
+#define	CAM_QUIRK_NOVPDS	0x02
 #define	CAM_QUIRK_HILUNS	0x04
 #define	CAM_QUIRK_NOHILUNS	0x08
 	u_int mintags;
@@ -549,7 +553,7 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
 			T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
 			" TDC 3600", "U07:"
 		},
-		CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0
+		CAM_QUIRK_NOVPDS, /*mintags*/0, /*maxtags*/0
 	},
 	{
 		/*
@@ -951,6 +955,31 @@ dev_allocq_is_runnable(struct cam_devq *devq)
 }
 
 static void
+scsi_getdev_advinfo(union ccb *start_ccb)
+{
+	struct cam_ed *device;
+	struct ccb_getdev_advinfo *cgdai;
+	off_t amt;
+
+	device = start_ccb->ccb_h.path->device;
+	cgdai = &start_ccb->cgdai;
+	switch(cgdai->buftype) {
+	case CGDAI_TYPE_SCSI_DEVID:
+		cgdai->provsiz = device->device_id_len;
+		if (device->device_id_len == 0)
+			break;
+		amt = device->device_id_len;
+		if (cgdai->provsiz > cgdai->bufsiz)
+			amt = cgdai->bufsiz;
+		bcopy(device->device_id, cgdai->buf, amt);
+		break;
+	default:
+		break;
+	}
+	start_ccb->ccb_h.status = CAM_REQ_CMP;
+}
+
+static void
 xpt_periph_init(void)
 {
 	make_dev(&xpt_ops, 0, UID_ROOT, GID_OPERATOR, 0600, "xpt0");
@@ -3012,6 +3041,7 @@ xpt_action(union ccb *start_ccb)
 		/* FALLTHROUGH */
 	case XPT_RESET_DEV:
 	case XPT_ENG_EXEC:
+	case XPT_SMP_IO:
 	{
 		struct cam_path *path;
 		struct cam_sim *sim;
@@ -3130,6 +3160,11 @@ xpt_action(union ccb *start_ccb)
 		(*(sim->sim_action))(sim, start_ccb);
 		break;
 	}
+	case XPT_GDEV_ADVINFO:
+	{
+		scsi_getdev_advinfo(start_ccb);
+		break;
+	}
 	case XPT_PATH_STATS:
 		start_ccb->cpis.last_reset =
 			start_ccb->ccb_h.path->bus->last_reset;
@@ -5073,6 +5108,10 @@ xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
 		device->queue_flags = 0;
 		device->serial_num = NULL;
 		device->serial_num_len = 0;
+		device->device_id = NULL;
+		device->device_id_len = 0;
+		device->supported_vpds = NULL;
+		device->supported_vpds_len = 0;
 		device->qfrozen_cnt = 0;
 		device->flags = CAM_DEV_UNCONFIGURED;
 		device->tag_delay_count = 0;
@@ -5482,8 +5521,9 @@ typedef enum {
 	PROBE_INQUIRY,	/* this counts as DV0 for Basic Domain Validation */
 	PROBE_FULL_INQUIRY,
 	PROBE_MODE_SENSE,
-	PROBE_SERIAL_NUM_0,
-	PROBE_SERIAL_NUM_1,
+	PROBE_SUPPORTED_VPD_LIST,
+	PROBE_DEVICE_ID,
+	PROBE_SERIAL_NUM,
 	PROBE_TUR_FOR_NEGOTIATION,
 	PROBE_INQUIRY_BASIC_DV1,
 	PROBE_INQUIRY_BASIC_DV2,
@@ -5685,6 +5725,21 @@ probeschedule(struct cam_periph *periph)
 	xpt_schedule(periph, ccb->ccb_h.pinfo.priority);
 }
 
+static int
+device_has_vpd(struct cam_ed *device, uint8_t page_id)
+{
+	int i, num_pages;
+	struct scsi_vpd_supported_pages *vpds;
+
+	vpds = (struct scsi_vpd_supported_pages *)device->supported_vpds;
+	num_pages = device->supported_vpds_len - SVPD_SUPPORTED_PAGES_HDR_LEN;
+	for (i = 0;i < num_pages;i++)
+		if (vpds->page_list[i] == page_id)
+			return 1;
+
+	return 0;
+}
+
 static void
 probestart(struct cam_periph *periph, union ccb *start_ccb)
 {
@@ -5794,16 +5849,17 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
 				/*timeout*/60000);
 		break;
 	}
-	case PROBE_SERIAL_NUM_0:
+	case PROBE_SUPPORTED_VPD_LIST:
 	{
-		struct scsi_vpd_supported_page_list *vpd_list = NULL;
+		struct scsi_vpd_supported_page_list *vpd_list;
 		struct cam_ed *device;
 
+		vpd_list = NULL;
 		device = periph->path->device;
-		if ((device->quirk->quirks & CAM_QUIRK_NOSERIAL) == 0) {
+
+		if ((device->quirk->quirks & CAM_QUIRK_NOVPDS) == 0)
 			vpd_list = kmalloc(sizeof(*vpd_list), M_CAMXPT,
 			    M_INTWAIT | M_ZERO);
-		}
 
 		if (vpd_list != NULL) {
 			scsi_inquiry(csio,
@@ -5826,7 +5882,39 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
 		probedone(periph, start_ccb);
 		return;
 	}
-	case PROBE_SERIAL_NUM_1:
+	case PROBE_DEVICE_ID:
+	{
+		struct scsi_vpd_device_id *devid;
+		struct cam_ed *device;
+
+		devid = NULL;
+		device = periph->path->device;
+		if (device_has_vpd(device, SVPD_DEVICE_ID))
+			devid = kmalloc(SVPD_DEVICE_ID_MAX_SIZE, M_CAMXPT,
+			    M_INTWAIT | M_ZERO);
+
+		if (devid != NULL) {
+			scsi_inquiry(csio,
+				     /*retries*/4,
+				     probedone,
+				     MSG_SIMPLE_Q_TAG,
+				     (uint8_t *)devid,
+				     SVPD_DEVICE_ID_MAX_SIZE,
+				     /*evpd*/TRUE,
+				     SVPD_DEVICE_ID,
+				     SSD_MIN_SIZE,
+				     /*timeout*/60 * 1000);
+			break;
+		}
+		/*
+		 * We'll have to do without, let our probedone
+		 * routine finish up for us.
+		 */
+		start_ccb->csio.data_ptr = NULL;
+		probedone(periph, start_ccb);
+		return;
+	}
+	case PROBE_SERIAL_NUM:
 	{
 		struct scsi_vpd_unit_serial_number *serial_buf;
 		struct cam_ed* device;
@@ -5836,9 +5924,11 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
 		device->serial_num = NULL;
 		device->serial_num_len = 0;
 
-		serial_buf = (struct scsi_vpd_unit_serial_number *)
-			kmalloc(sizeof(*serial_buf), M_CAMXPT,
-				M_INTWAIT | M_ZERO);
+		if (device_has_vpd(device, SVPD_UNIT_SERIAL_NUMBER))
+			serial_buf = (struct scsi_vpd_unit_serial_number *)
+				kmalloc(sizeof(*serial_buf), M_CAMXPT,
+				    M_INTWAIT | M_ZERO);
+
 		scsi_inquiry(csio,
 			     /*retries*/4,
 			     probedone,
@@ -5977,6 +6067,8 @@ proberequestbackoff(struct cam_periph *periph, struct cam_ed *device)
 	return (1);
 }
 
+#define CCB_COMPLETED_OK(ccb) (((ccb).status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+
 static void
 probedone(struct cam_periph *periph, union ccb *done_ccb)
 {
@@ -6053,7 +6145,8 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
 				if (INQ_DATA_TQ_ENABLED(inq_buf))
 					softc->action = PROBE_MODE_SENSE;
 				else
-					softc->action = PROBE_SERIAL_NUM_0;
+					softc->action =
+					    PROBE_SUPPORTED_VPD_LIST;
 
 				path->device->flags &= ~CAM_DEV_UNCONFIGURED;
 				xpt_reference_device(path->device);
@@ -6120,36 +6213,38 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
 		}
 		xpt_release_ccb(done_ccb);
 		kfree(mode_hdr, M_CAMXPT);
-		softc->action = PROBE_SERIAL_NUM_0;
+		softc->action = PROBE_SUPPORTED_VPD_LIST;
 		xpt_schedule(periph, priority);
 		return;
 	}
-	case PROBE_SERIAL_NUM_0:
+	case PROBE_SUPPORTED_VPD_LIST:
 	{
 		struct ccb_scsiio *csio;
 		struct scsi_vpd_supported_page_list *page_list;
-		int length, serialnum_supported, i;
 
-		serialnum_supported = 0;
 		csio = &done_ccb->csio;
 		page_list =
 		    (struct scsi_vpd_supported_page_list *)csio->data_ptr;
 
+		if (path->device->supported_vpds != NULL) {
+			kfree(path->device->supported_vpds, M_CAMXPT);
+			path->device->supported_vpds = NULL;
+			path->device->supported_vpds_len = 0;
+		}
+
 		if (page_list == NULL) {
 			/*
 			 * Don't process the command as it was never sent
 			 */
-		} else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP
-		    && (page_list->length > 0)) {
-			length = min(page_list->length,
-			    SVPD_SUPPORTED_PAGES_SIZE);
-			for (i = 0; i < length; i++) {
-				if (page_list->list[i] ==
-				    SVPD_UNIT_SERIAL_NUMBER) {
-					serialnum_supported = 1;
-					break;
-				}
-			}
+		} else if (CCB_COMPLETED_OK(csio->ccb_h)) {
+			/* Got vpd list */
+			path->device->supported_vpds_len = page_list->length +
+			    SVPD_SUPPORTED_PAGES_HDR_LEN;
+			path->device->supported_vpds = (uint8_t *)page_list;
+			xpt_release_ccb(done_ccb);
+			softc->action = PROBE_DEVICE_ID;
+			xpt_schedule(periph, priority);
+			return;
 		} else if (cam_periph_error(done_ccb, 0,
 					    SF_RETRY_UA|SF_NO_PRINT,
 					    &softc->saved_ccb) == ERESTART) {
@@ -6160,22 +6255,62 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
 					 /*run_queue*/TRUE);
 		}
 
-		if (page_list != NULL)
+		if (page_list)
 			kfree(page_list, M_DEVBUF);
+		/* No VPDs available, skip to device check. */
+		csio->data_ptr = NULL;
+		goto probe_device_check;
+	}
+	case PROBE_DEVICE_ID:
+	{
+		struct scsi_vpd_device_id *devid;
+		struct ccb_scsiio *csio;
+		uint32_t length = 0;
 
-		if (serialnum_supported) {
-			xpt_release_ccb(done_ccb);
-			softc->action = PROBE_SERIAL_NUM_1;
-			xpt_schedule(periph, priority);
+		csio = &done_ccb->csio;
+		devid = (struct scsi_vpd_device_id *)csio->data_ptr;
+
+		/* Clean up from previous instance of this device */
+		if (path->device->device_id != NULL) {
+			path->device->device_id_len = 0;
+			kfree(path->device->device_id, M_CAMXPT);
+			path->device->device_id = NULL;
+		}
+
+		if (devid == NULL) {
+			/* Don't process the command as it was never sent */
+		} else if (CCB_COMPLETED_OK(csio->ccb_h)) {
+			length = scsi_2btoul(devid->length);
+			if (length != 0) {
+				/*
+				 * NB: device_id_len is actual response
+				 * size, not buffer size.
+				 */
+				path->device->device_id_len = length +
+				    SVPD_DEVICE_ID_HDR_LEN;
+				path->device->device_id = (uint8_t *)devid;
+			}
+		} else if (cam_periph_error(done_ccb, 0,
+					    SF_RETRY_UA|SF_NO_PRINT,
+					    &softc->saved_ccb) == ERESTART) {
 			return;
+		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+			/* Don't wedge the queue */
+			xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
+					 /*run_queue*/TRUE);
 		}
+
+		/* Free the device id space if we don't use it */
+		if (devid && length == 0)
+			kfree(devid, M_CAMXPT);
 		xpt_release_ccb(done_ccb);
-		softc->action = PROBE_TUR_FOR_NEGOTIATION;
-		xpt_schedule(periph, done_ccb->ccb_h.pinfo.priority);
+		softc->action = PROBE_SERIAL_NUM;
+		xpt_schedule(periph, priority);
 		return;
 	}
 
-	case PROBE_SERIAL_NUM_1:
+probe_device_check:
+	case PROBE_SERIAL_NUM:
 	{
 		struct ccb_scsiio *csio;
 		struct scsi_vpd_unit_serial_number *serial_buf;
@@ -6190,13 +6325,6 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
 		serial_buf =
 		    (struct scsi_vpd_unit_serial_number *)csio->data_ptr;
 
-		/* Clean up from previous instance of this device */
-		if (path->device->serial_num != NULL) {
-			kfree(path->device->serial_num, M_CAMXPT);
-			path->device->serial_num = NULL;
-			path->device->serial_num_len = 0;
-		}
-
 		if (serial_buf == NULL) {
 			/*
 			 * Don't process the command as it was never sent
diff --git a/sys/bus/cam/scsi/scsi_all.c b/sys/bus/cam/scsi/scsi_all.c
index 4a5d441..a7cec17 100644
--- a/sys/bus/cam/scsi/scsi_all.c
+++ b/sys/bus/cam/scsi/scsi_all.c
@@ -3536,6 +3536,34 @@ scsi_calc_syncparam(u_int period)
 	return (period/400);
 }
 
+uint8_t *
+scsi_get_sas_addr(struct scsi_vpd_device_id *id, uint32_t len)
+{
+	uint8_t *bufp, *buf_end;
+	struct scsi_vpd_id_descriptor *descr;
+	struct scsi_vpd_id_naa_basic *naa;
+
+	bufp = buf_end = (uint8_t *)id;
+	bufp += SVPD_DEVICE_ID_HDR_LEN;
+	buf_end += len;
+	while (bufp < buf_end) {
+		descr = (struct scsi_vpd_id_descriptor *)bufp;
+		bufp += SVPD_DEVICE_ID_DESC_HDR_LEN;
+		/* Right now, we only care about SAS NAA IEEE Reg addrs */
+		if (((descr->id_type & SVPD_ID_PIV) != 0)
+		 && (descr->proto_codeset >> SVPD_ID_PROTO_SHIFT) ==
+		     SCSI_PROTO_SAS
+		 && (descr->id_type & SVPD_ID_TYPE_MASK) == SVPD_ID_TYPE_NAA){
+			naa = (struct scsi_vpd_id_naa_basic *)bufp;
+			if ((naa->naa >> 4) == SVPD_ID_NAA_IEEE_REG)
+				return bufp;
+		}
+		bufp += descr->length;
+	}
+
+	return NULL;
+}
+
 void
 scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries,
 		     void (*cbfcnp)(struct cam_periph *, union ccb *),
diff --git a/sys/bus/cam/scsi/scsi_all.h b/sys/bus/cam/scsi/scsi_all.h
index c722f31..4f9eda7 100644
--- a/sys/bus/cam/scsi/scsi_all.h
+++ b/sys/bus/cam/scsi/scsi_all.h
@@ -15,7 +15,6 @@
  * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
  *
  * $FreeBSD: src/sys/cam/scsi/scsi_all.h,v 1.31 2008/08/07 17:25:05 jkim Exp $
- * $DragonFly: src/sys/bus/cam/scsi/scsi_all.h,v 1.15 2008/08/23 22:27:27 pavalos Exp $
  */
 
 /*
@@ -777,13 +776,28 @@ struct scsi_vpd_supported_page_list
 {
 	u_int8_t device;
 	u_int8_t page_code;
-#define SVPD_SUPPORTED_PAGE_LIST 0x00
+#define	SVPD_SUPPORTED_PAGE_LIST	0x00
+#define	SVPD_SUPPORTED_PAGES_HDR_LEN	4
 	u_int8_t reserved;
 	u_int8_t length;	/* number of VPD entries */
 #define SVPD_SUPPORTED_PAGES_SIZE	251
 	u_int8_t list[SVPD_SUPPORTED_PAGES_SIZE];
 };
 
+/*
+ * This structure is more suited to target operation, because the
+ * number of supported pages is left to the user to allocate.
+ */
+struct scsi_vpd_supported_pages
+{
+	u_int8_t device;
+	u_int8_t page_code;
+	u_int8_t reserved;
+#define	SVPD_SUPPORTED_PAGES	0x00
+	u_int8_t length;
+	u_int8_t page_list[0];
+};
+
 struct scsi_vpd_unit_serial_number
 {
 	u_int8_t device;
@@ -832,6 +846,148 @@ struct scsi_vpd_devid_hdr {
 	u_int8_t len;
 };
 
+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
+	u_int8_t length[2];
+	u_int8_t desc_list[0];
+};
+
+struct scsi_vpd_id_descriptor
+{
+	u_int8_t	proto_codeset;
+#define	SCSI_PROTO_FC		0x00
+#define	SCSI_PROTO_SPI		0x01
+#define	SCSI_PROTO_SSA		0x02
+#define	SCSI_PROTO_1394		0x03
+#define	SCSI_PROTO_RDMA		0x04
+#define SCSI_PROTO_iSCSI	0x05
+#define	SCSI_PROTO_SAS		0x06
+#define	SVPD_ID_PROTO_SHIFT	4
+#define	SVPD_ID_CODESET_BINARY	0x01
+#define	SVPD_ID_CODESET_ASCII	0x02
+	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_TYPE_VENDOR	0x00
+#define	SVPD_ID_TYPE_T10	0x01
+#define	SVPD_ID_TYPE_EUI64	0x02
+#define	SVPD_ID_TYPE_NAA	0x03
+#define	SVPD_ID_TYPE_RELTARG	0x04
+#define	SVPD_ID_TYPE_TPORTGRP	0x05
+#define	SVPD_ID_TYPE_LUNGRP	0x06
+#define	SVPD_ID_TYPE_MD5_LUN_ID	0x07
+#define	SVPD_ID_TYPE_SCSI_NAME	0x08
+#define	SVPD_ID_TYPE_MASK	0x0f
+	u_int8_t	reserved;
+	u_int8_t	length;
+	u_int8_t	identifier[0];
+};
+
+struct scsi_vpd_id_t10
+{
+	u_int8_t	vendor[8];
+	u_int8_t	vendor_spec_id[0];
+};
+
+struct scsi_vpd_id_eui64
+{
+	u_int8_t	ieee_company_id[3];
+	u_int8_t	extension_id[5];
+};
+
+struct scsi_vpd_id_naa_basic
+{
+	uint8_t naa;
+	/* big endian, packed:
+	uint8_t	naa : 4;
+	uint8_t naa_desig : 4;
+	*/
+#define	SVPD_ID_NAA_IEEE_EXT		0x02
+#define	SVPD_ID_NAA_LOCAL_REG		0x03
+#define	SVPD_ID_NAA_IEEE_REG		0x05
+#define	SVPD_ID_NAA_IEEE_REG_EXT	0x06
+	uint8_t	naa_data[0];
+};
+
+struct scsi_vpd_id_naa_ieee_extended_id
+{
+	uint8_t naa;
+	uint8_t vendor_specific_id_a;
+	uint8_t ieee_company_id[3];
+	uint8_t vendor_specific_id_b[4];
+};
+
+struct scsi_vpd_id_naa_local_reg
+{
+	uint8_t naa;
+	uint8_t local_value[7];
+};
+
+struct scsi_vpd_id_naa_ieee_reg
+{
+	uint8_t naa;
+	uint8_t reg_value[7];
+	/* big endian, packed:
+	uint8_t naa_basic : 4;
+	uint8_t ieee_company_id_0 : 4;
+	uint8_t ieee_company_id_1[2];
+	uint8_t ieee_company_id_2 : 4;
+	uint8_t vendor_specific_id_0 : 4;
+	uint8_t vendor_specific_id_1[4];
+	*/
+};
+
+struct scsi_vpd_id_naa_ieee_reg_extended
+{
+	uint8_t naa;
+	uint8_t reg_value[15];
+	/* big endian, packed:
+	uint8_t naa_basic : 4;
+	uint8_t ieee_company_id_0 : 4;
+	uint8_t ieee_company_id_1[2];
+	uint8_t ieee_company_id_2 : 4;
+	uint8_t vendor_specific_id_0 : 4;
+	uint8_t vendor_specific_id_1[4];
+	uint8_t vendor_specific_id_ext[8];
+	*/
+};
+
+struct scsi_vpd_id_rel_trgt_port_id
+{
+	uint8_t obsolete[2];
+	uint8_t rel_trgt_port_id[2];
+};
+
+struct scsi_vpd_id_trgt_port_grp_id
+{
+	uint8_t reserved[2];
+	uint8_t trgt_port_grp[2];
+};
+
+struct scsi_vpd_id_lun_grp_id
+{
+	uint8_t reserved[2];
+	uint8_t log_unit_grp[2];
+};
+
+struct scsi_vpd_id_md5_lun_id
+{
+	uint8_t lun_id[16];
+};
+
+struct scsi_vpd_id_scsi_name
+{
+	uint8_t name_string[256];
+};
+
 struct scsi_read_capacity
 {
 	u_int8_t opcode;
@@ -1184,7 +1340,8 @@ void		scsi_print_inquiry(struct scsi_inquiry_data *inq_data);
 
 u_int		scsi_calc_syncsrate(u_int period_factor);
 u_int		scsi_calc_syncparam(u_int period);
-	
+uint8_t *	scsi_get_sas_addr(struct scsi_vpd_device_id *id, uint32_t len);
+
 void		scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries,
 				     void (*cbfcnp)(struct cam_periph *, 
 						    union ccb *),
diff --git a/sys/bus/cam/scsi/scsi_pass.c b/sys/bus/cam/scsi/scsi_pass.c
index f6c53e4..f25eac2 100644
--- a/sys/bus/cam/scsi/scsi_pass.c
+++ b/sys/bus/cam/scsi/scsi_pass.c
@@ -543,7 +543,7 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
 	 * We only attempt to map the user memory into kernel space
 	 * if they haven't passed in a physical memory pointer,
 	 * and if there is actually an I/O operation to perform.
-	 * Right now cam_periph_mapmem() only supports SCSI and device
+	 * cam_periph_mapmem() supports SCSI, SMP, ADVINFO and device
 	 * match CCBs.  For the SCSI CCBs, we only pass the CCB in if
 	 * there's actually data to map.  cam_periph_mapmem() will do the
 	 * right thing, even if there isn't data to map, but since CCBs
@@ -553,7 +553,9 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
 	if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0)
 	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO)
 	    && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
-	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) {
+	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH)
+	  || (ccb->ccb_h.func_code == XPT_SMP_IO)
+	  || (ccb->ccb_h.func_code == XPT_GDEV_ADVINFO))) {
 
 		bzero(&mapinfo, sizeof(mapinfo));
 
diff --git a/sys/bus/cam/scsi/smp_all.c b/sys/bus/cam/scsi/smp_all.c
new file mode 100644
index 0000000..a0525b8
--- /dev/null
+++ b/sys/bus/cam/scsi/smp_all.c
@@ -0,0 +1,617 @@
+/*-
+ * Copyright (c) 2010 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * 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.c#4 $
+ * $FreeBSD: src/sys/cam/scsi/smp_all.c,v 1.2 2012/11/17 01:51:22 svnexp Exp $
+ */
+
+/*
+ * Serial Management Protocol helper functions.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/libkern.h>
+#include <sys/kernel.h>
+#else /* _KERNEL */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#endif /* _KERNEL */
+
+#include <bus/cam/cam.h>
+#include <bus/cam/cam_ccb.h>
+#include <bus/cam/cam_xpt.h>
+#include <bus/cam/scsi/smp_all.h>
+#include <sys/sbuf.h>
+
+#ifndef _KERNEL
+#include <camlib.h>
+#endif
+
+static char *smp_yesno(int val);
+
+static char *
+smp_yesno(int val)
+{
+	char *str;
+
+	if (val)
+		str = "Yes";
+	else
+		str = "No";
+
+	return (str);
+}
+
+struct smp_error_table_entry {
+	uint8_t	function_result;
+	const char *desc;
+};
+
+/* List current as of SPL Revision 7 */
+static struct smp_error_table_entry smp_error_table[] = {
+	{SMP_FR_ACCEPTED, "SMP Function Accepted"},
+	{SMP_FR_UNKNOWN_FUNC, "Unknown SMP Function"},
+	{SMP_FR_FUNCTION_FAILED, "SMP Function Failed"},
+	{SMP_FR_INVALID_REQ_FRAME_LEN, "Invalid Request Frame Length"},
+	{SMP_FR_INVALID_EXP_CHG_CNT, "Invalid Expander Change Count"},
+	{SMP_FR_BUSY, "Busy"},
+	{SMP_FR_INCOMPLETE_DESC_LIST, "Incomplete Descriptor List"},
+	{SMP_FR_PHY_DOES_NOT_EXIST, "Phy Does Not Exist"},
+	{SMP_FR_INDEX_DOES_NOT_EXIST, "Index Does Not Exist"},
+	{SMP_FR_PHY_DOES_NOT_SUP_SATA, "Phy Does Not Support SATA"},
+	{SMP_FR_UNKNOWN_PHY_OP, "Unknown Phy Operation"},
+	{SMP_FR_UNKNOWN_PHY_TEST_FUNC, "Unknown Phy Test Function"},
+	{SMP_FR_PHY_TEST_FUNC_INPROG, "Phy Test Function In Progress"},
+	{SMP_FR_PHY_VACANT, "Phy Vacant"},
+	{SMP_FR_UNKNOWN_PHY_EVENT_SRC, "Unknown Phy Event Source"},
+	{SMP_FR_UNKNOWN_DESC_TYPE, "Unknown Descriptor Type"},
+	{SMP_FR_UNKNOWN_PHY_FILTER, "Unknown Phy Filter"},
+	{SMP_FR_AFFILIATION_VIOLATION, "Affiliation Violation"},
+	{SMP_FR_SMP_ZONE_VIOLATION, "SMP Zone Violation"},
+	{SMP_FR_NO_MGMT_ACCESS_RIGHTS, "No Management Access Rights"},
+	{SMP_FR_UNKNOWN_ED_ZONING_VAL, "Unknown Enable Disable Zoning Value"},
+	{SMP_FR_ZONE_LOCK_VIOLATION, "Zone Lock Violation"},
+	{SMP_FR_NOT_ACTIVATED, "Not Activated"},
+	{SMP_FR_ZG_OUT_OF_RANGE, "Zone Group Out of Range"},
+	{SMP_FR_NO_PHYS_PRESENCE, "No Physical Presence"},
+	{SMP_FR_SAVING_NOT_SUP, "Saving Not Supported"},
+	{SMP_FR_SRC_ZONE_DNE, "Source Zone Group Does Not Exist"},
+	{SMP_FR_DISABLED_PWD_NOT_SUP, "Disabled Password Not Supported"}
+};
+
+const char *
+smp_error_desc(int function_result)
+{
+	int i;
+
+	for (i = 0; i < (sizeof(smp_error_table)/sizeof(smp_error_table[0]));
+	     i++){
+		if (function_result == smp_error_table[i].function_result)
+			return (smp_error_table[i].desc);
+	}
+	return ("Reserved Function Result");
+}
+
+/* List current as of SPL Revision 7 */
+struct smp_cmd_table_entry {
+	uint8_t	cmd_num;
+	const char *desc;
+} smp_cmd_table[] = {
+	{SMP_FUNC_REPORT_GENERAL, "REPORT GENERAL"},
+	{SMP_FUNC_REPORT_MANUF_INFO, "REPORT MANUFACTURER INFORMATION"},
+	{SMP_FUNC_REPORT_SC_STATUS, "REPORT SELF-CONFIGURATION STATUS"},
+	{SMP_FUNC_REPORT_ZONE_PERM_TBL, "REPORT ZONE PERMISSION TABLE"},
+	{SMP_FUNC_REPORT_BROADCAST, "REPORT BROADCAST"},
+	{SMP_FUNC_DISCOVER, "DISCOVER"},
+	{SMP_FUNC_REPORT_PHY_ERR_LOG, "REPORT PHY ERROR LOG"},
+	{SMP_FUNC_REPORT_PHY_SATA, "REPORT PHY SATA"},
+	{SMP_FUNC_REPORT_ROUTE_INFO, "REPORT ROUTE INFORMATION"},
+	{SMP_FUNC_REPORT_PHY_EVENT, "REPORT PHY EVENT"},
+	{SMP_FUNC_DISCOVER_LIST, "DISCOVER LIST"},
+	{SMP_FUNC_REPORT_PHY_EVENT_LIST, "REPORT PHY EVENT LIST"},
+	{SMP_FUNC_REPORT_EXP_RTL, "REPORT EXPANDER ROUTE TABLE LIST"},
+	{SMP_FUNC_CONFIG_GENERAL, "CONFIGURE GENERAL"},
+	{SMP_FUNC_ENABLE_DISABLE_ZONING, "ENABLE DISABLE ZONING"},
+	{SMP_FUNC_ZONED_BROADCAST, "ZONED BROADCAST"},
+	{SMP_FUNC_ZONE_LOCK, "ZONE LOCK"},
+	{SMP_FUNC_ZONE_ACTIVATE, "ZONE ACTIVATE"},
+	{SMP_FUNC_ZONE_UNLOCK, "ZONE UNLOCK"},
+	{SMP_FUNC_CONFIG_ZM_PWD, "CONFIGURE ZONE MANAGER PASSWORD"},
+	{SMP_FUNC_CONFIG_ZONE_PHY_INFO, "CONFIGURE ZONE PHY INFORMATION"},
+	{SMP_FUNC_CONFIG_ZONE_PERM_TBL, "CONFIGURE ZONE PERMISSION TABLE"},
+	{SMP_FUNC_CONFIG_ROUTE_INFO, "CONFIGURE ROUTE INFORMATION"},
+	{SMP_FUNC_PHY_CONTROL, "PHY CONTROL"},
+	{SMP_FUNC_PHY_TEST_FUNC, "PHY TEST FUNCTION"},
+	{SMP_FUNC_CONFIG_PHY_EVENT, "CONFIGURE PHY EVENT"}
+};
+
+const char *
+smp_command_desc(uint8_t cmd_num)
+{
+	int i;
+
+	for (i = 0; i < (sizeof(smp_cmd_table)/sizeof(smp_cmd_table[0])) &&
+	     smp_cmd_table[i].cmd_num <= cmd_num; i++) {
+		if (cmd_num == smp_cmd_table[i].cmd_num)
+			return (smp_cmd_table[i].desc);
+	}
+
+	/*
+	 * 0x40 to 0x7f and 0xc0 to 0xff are the vendor specific SMP
+	 * command ranges.
+	 */
+	if (((cmd_num >= 0x40) && (cmd_num <= 0x7f))
+	 || (cmd_num >= 0xc0)) {
+		return ("Vendor Specific SMP Command");
+	} else {
+		return ("Unknown SMP Command");
+	}
+}
+
+/*
+ * Decode a SMP request buffer into a string of hexadecimal numbers.
+ *
+ * smp_request:    SMP request
+ * request_len:    length of the SMP request buffer, may be reduced if the
+ *                 caller only wants part of the buffer printed
+ * sb:             sbuf(9) buffer
+ * line_prefix:    prefix for new lines, or an empty string ("")
+ * first_line_len: length left on first line
+ * line_len:       total length of subsequent lines, 0 for no additional lines
+ *                 if there are no additional lines, first line will get ...
+ *                 at the end if there is additional data
+ */
+void
+smp_command_decode(uint8_t *smp_request, int request_len, struct sbuf *sb,
+		   char *line_prefix, int first_line_len, int line_len)
+{
+	int i, cur_len;
+
+	for (i = 0, cur_len = first_line_len; i < request_len; i++) {
+		/*
+		 * Each byte takes 3 characters.  As soon as we go less
+		 * than 6 (meaning we have at least 3 and at most 5
+		 * characters left), check to see whether the subsequent
+		 * line length (line_len) is long enough to bother with.
+		 * If the user set it to 0, or some other length that isn't
+		 * enough to hold at least the prefix and one byte, put ...
+		 * on the first line to indicate that there is more data
+		 * and bail out.
+		 */
+		if ((cur_len < 6)
+		 && (line_len < (strlen(line_prefix) + 3))) {
+			sbuf_printf(sb, "...");
+			return;
+		}
+		if (cur_len < 3) {
+			sbuf_printf(sb, "\n%s", line_prefix);
+			cur_len = line_len - strlen(line_prefix);
+		}
+		sbuf_printf(sb, "%02x ", smp_request[i]);
+		cur_len = cur_len - 3;
+	}
+}
+
+void
+smp_command_sbuf(struct ccb_smpio *smpio, struct sbuf *sb,
+		 char *line_prefix, int first_line_len, int line_len)
+{
+	sbuf_printf(sb, "%s. ", smp_command_desc(smpio->smp_request[1]));
+
+	/*
+	 * Acccount for the command description and the period and space
+	 * after the command description.
+	 */
+	first_line_len -= strlen(smp_command_desc(smpio->smp_request[1])) + 2;
+
+	smp_command_decode(smpio->smp_request, smpio->smp_request_len, sb,
+			   line_prefix, first_line_len, line_len);
+}
+
+/*
+ * Print SMP error output.  For userland commands, we need the cam_device
+ * structure so we can get the path information from the CCB.
+ */
+#ifdef _KERNEL
+void
+smp_error_sbuf(struct ccb_smpio *smpio, struct sbuf *sb)
+#else /* !_KERNEL*/
+void
+smp_error_sbuf(struct cam_device *device, struct ccb_smpio *smpio,
+	       struct sbuf *sb)
+#endif /* _KERNEL/!_KERNEL */
+{
+	char path_str[64];
+
+#ifdef _KERNEL
+	xpt_path_string(smpio->ccb_h.path, path_str, sizeof(path_str));
+#else
+	cam_path_string(device, path_str, sizeof(path_str));
+#endif
+	smp_command_sbuf(smpio, sb, path_str, 80 - strlen(path_str), 80);
+	sbuf_printf(sb, "\n");
+
+	sbuf_cat(sb, path_str);
+	sbuf_printf(sb, "SMP Error: %s (0x%x)\n",
+		    smp_error_desc(smpio->smp_response[2]),
+		    smpio->smp_response[2]);
+}
+
+/*
+ * Decode the SMP REPORT GENERAL response.  The format is current as of SPL
+ * Revision 7, but the parsing should be backward compatible for older
+ * versions of the spec.
+ */
+void
+smp_report_general_sbuf(struct smp_report_general_response *response,
+			int response_len, struct sbuf *sb)
+{
+	sbuf_printf(sb, "Report General\n");
+	sbuf_printf(sb, "Response Length: %d words (%d bytes)\n",
+		    response->response_len,
+		    response->response_len * SMP_WORD_LEN);
+	sbuf_printf(sb, "Expander Change Count: %d\n",
+		    scsi_2btoul(response->expander_change_count));
+	sbuf_printf(sb, "Expander Route Indexes: %d\n",
+		    scsi_2btoul(response->expander_route_indexes));
+	sbuf_printf(sb, "Long Response: %s\n",
+		    smp_yesno(response->long_response &
+			      SMP_RG_LONG_RESPONSE));
+	sbuf_printf(sb, "Number of Phys: %d\n", response->num_phys);
+	sbuf_printf(sb, "Table to Table Supported: %s\n",
+		    smp_yesno(response->config_bits0 &
+		    SMP_RG_TABLE_TO_TABLE_SUP));
+	sbuf_printf(sb, "Zone Configuring: %s\n",
+		    smp_yesno(response->config_bits0 &
+		    SMP_RG_ZONE_CONFIGURING));
+	sbuf_printf(sb, "Self Configuring: %s\n",
+		    smp_yesno(response->config_bits0 &
+		    SMP_RG_SELF_CONFIGURING));
+	sbuf_printf(sb, "STP Continue AWT: %s\n",
+		    smp_yesno(response->config_bits0 &
+		    SMP_RG_STP_CONTINUE_AWT));
+	sbuf_printf(sb, "Open Reject Retry Supported: %s\n",
+		    smp_yesno(response->config_bits0 &
+		    SMP_RG_OPEN_REJECT_RETRY_SUP));
+	sbuf_printf(sb, "Configures Others: %s\n",
+		    smp_yesno(response->config_bits0 &
+		    SMP_RG_CONFIGURES_OTHERS));
+	sbuf_printf(sb, "Configuring: %s\n",
+		    smp_yesno(response->config_bits0 &
+		    SMP_RG_CONFIGURING));
+	sbuf_printf(sb, "Externally Configurable Route Table: %s\n",
+		    smp_yesno(response->config_bits0 &
+		    SMP_RG_CONFIGURING));
+	sbuf_printf(sb, "Enclosure Logical Identifier: 0x%016jx\n",
+		    (uintmax_t)scsi_8btou64(response->encl_logical_id));
+
+	/*
+	 * If the response->response_len is 0, then we don't have the
+	 * extended information.  Also, if the user didn't allocate enough
+	 * space for the full request, don't try to parse it.
+	 */
+	if ((response->response_len == 0)
+	 || (response_len < (sizeof(struct smp_report_general_response) -
+	     sizeof(response->crc))))
+		return;
+
+	sbuf_printf(sb, "STP Bus Inactivity Time Limit: %d\n",
+		    scsi_2btoul(response->stp_bus_inact_time_limit));
+	sbuf_printf(sb, "STP Maximum Connect Time Limit: %d\n",
+		    scsi_2btoul(response->stp_max_conn_time_limit));
+	sbuf_printf(sb, "STP SMP I_T Nexus Loss Time: %d\n",
+		    scsi_2btoul(response->stp_smp_it_nexus_loss_time));
+
+	sbuf_printf(sb, "Number of Zone Groups: %d\n",
+		    (response->config_bits1 & SMP_RG_NUM_ZONE_GROUPS_MASK) >>
+		    SMP_RG_NUM_ZONE_GROUPS_SHIFT);
+	sbuf_printf(sb, "Zone Locked: %s\n",
+		    smp_yesno(response->config_bits1 & SMP_RG_ZONE_LOCKED));
+	sbuf_printf(sb, "Physical Presence Supported: %s\n",
+		    smp_yesno(response->config_bits1 & SMP_RG_PP_SUPPORTED));
+	sbuf_printf(sb, "Physical Presence Asserted: %s\n",
+		    smp_yesno(response->config_bits1 & SMP_RG_PP_ASSERTED));
+	sbuf_printf(sb, "Zoning Supported: %s\n",
+		    smp_yesno(response->config_bits1 &
+			      SMP_RG_ZONING_SUPPORTED));
+	sbuf_printf(sb, "Zoning Enabled: %s\n",
+		    smp_yesno(response->config_bits1 & SMP_RG_ZONING_ENABLED));
+
+	sbuf_printf(sb, "Saving: %s\n",
+		    smp_yesno(response->config_bits2 & SMP_RG_SAVING));
+	sbuf_printf(sb, "Saving Zone Manager Password Supported: %s\n",
+		    smp_yesno(response->config_bits2 &
+			      SMP_RG_SAVING_ZM_PWD_SUP));
+	sbuf_printf(sb, "Saving Zone Phy Information Supported: %s\n",
+		    smp_yesno(response->config_bits2 &
+			      SMP_RG_SAVING_PHY_INFO_SUP));
+	sbuf_printf(sb, "Saving Zone Permission Table Supported: %s\n",
+		    smp_yesno(response->config_bits2 &
+			      SMP_RG_SAVING_ZPERM_TAB_SUP));
+	sbuf_printf(sb, "Saving Zoning Enabled Supported: %s\n",
+		    smp_yesno(response->config_bits2 &
+			      SMP_RG_SAVING_ZENABLED_SUP));
+
+	sbuf_printf(sb, "Maximum Number of Routed SAS Addresses: %d\n",
+		    scsi_2btoul(response->max_num_routed_addrs));
+
+	sbuf_printf(sb, "Active Zone Manager SAS Address: 0x%016jx\n",
+		    scsi_8btou64(response->active_zm_address));
+
+	sbuf_printf(sb, "Zone Inactivity Time Limit: %d\n",
+		    scsi_2btoul(response->zone_lock_inact_time_limit));
+
+	sbuf_printf(sb, "First Enclosure Connector Element Index: %d\n",
+		    response->first_encl_conn_el_index);
+
+	sbuf_printf(sb, "Number of Enclosure Connector Element Indexes: %d\n",
+		    response->num_encl_conn_el_indexes);
+
+	sbuf_printf(sb, "Reduced Functionality: %s\n",
+		    smp_yesno(response->reduced_functionality &
+			      SMP_RG_REDUCED_FUNCTIONALITY));
+
+	sbuf_printf(sb, "Time to Reduced Functionality: %d\n",
+		    response->time_to_reduced_func);
+	sbuf_printf(sb, "Initial Time to Reduced Functionality: %d\n",
+		    response->initial_time_to_reduced_func);
+	sbuf_printf(sb, "Maximum Reduced Functionality Time: %d\n",
+		    response->max_reduced_func_time);
+
+	sbuf_printf(sb, "Last Self-Configuration Status Descriptor Index: %d\n",
+		    scsi_2btoul(response->last_sc_stat_desc_index));
+
+	sbuf_printf(sb, "Maximum Number of Storated Self-Configuration "
+		    "Status Descriptors: %d\n",
+		    scsi_2btoul(response->max_sc_stat_descs));
+
+	sbuf_printf(sb, "Last Phy Event List Descriptor Index: %d\n",
+		    scsi_2btoul(response->last_phy_evl_desc_index));
+
+	sbuf_printf(sb, "Maximum Number of Stored Phy Event List "
+		    "Descriptors: %d\n",
+		    scsi_2btoul(response->max_stored_pel_descs));
+
+	sbuf_printf(sb, "STP Reject to Open Limit: %d\n",
+		    scsi_2btoul(response->stp_reject_to_open_limit));
+}
+
+/*
+ * Decode the SMP REPORT MANUFACTURER INFORMATION response.  The format is
+ * current as of SPL Revision 7, but the parsing should be backward
+ * compatible for older versions of the spec.
+ */
+void
+smp_report_manuf_info_sbuf(struct smp_report_manuf_info_response *response,
+			   int response_len, struct sbuf *sb)
+{
+	char vendor[16], product[48], revision[16];
+	char comp_vendor[16];
+
+	sbuf_printf(sb, "Report Manufacturer Information\n");
+	sbuf_printf(sb, "Expander Change count: %d\n",
+		    scsi_2btoul(response->expander_change_count));
+	sbuf_printf(sb, "SAS 1.1 Format: %s\n",
+		    smp_yesno(response->sas_11_format & SMP_RMI_SAS11_FORMAT));
+	cam_strvis(vendor, response->vendor, sizeof(response->vendor),
+		   sizeof(vendor));
+	cam_strvis(product, response->product, sizeof(response->product),
+		   sizeof(product));
+	cam_strvis(revision, response->revision, sizeof(response->revision),
+		   sizeof(revision));
+	sbuf_printf(sb, "<%s %s %s>\n", vendor, product, revision);
+
+	if ((response->sas_11_format & SMP_RMI_SAS11_FORMAT) == 0) {
+		uint8_t *curbyte;
+		int line_start, line_cursor;
+
+		sbuf_printf(sb, "Vendor Specific Data:\n");
+
+		/*
+		 * Print out the bytes roughly in the style of hd(1), but
+		 * without the extra ASCII decoding.  Hexadecimal line
+		 * numbers on the left, and 16 bytes per line, with an
+		 * extra space after the first 8 bytes.
+		 *
+		 * It would be nice if this sort of thing were available
+		 * in a library routine.
+		 */
+		for (curbyte = (uint8_t *)&response->comp_vendor, line_start= 1,
+		     line_cursor = 0; curbyte < (uint8_t *)&response->crc;
+		     curbyte++, line_cursor++) {
+			if (line_start != 0) {
+				sbuf_printf(sb, "%08lx  ",
+					    (unsigned long)(curbyte -
+					    (uint8_t *)response));
+				line_start = 0;
+				line_cursor = 0;
+			}
+			sbuf_printf(sb, "%02x", *curbyte);
+
+			if (line_cursor == 15) {
+				sbuf_printf(sb, "\n");
+				line_start = 1;
+			} else
+				sbuf_printf(sb, " %s", (line_cursor == 7) ?
+					    " " : "");
+		}
+		if (line_cursor != 16)
+			sbuf_printf(sb, "\n");
+		return;
+	}
+
+	cam_strvis(comp_vendor, response->comp_vendor,
+		   sizeof(response->comp_vendor), sizeof(comp_vendor));
+	sbuf_printf(sb, "Component Vendor: %s\n", comp_vendor);
+	sbuf_printf(sb, "Component ID: %#x\n", scsi_2btoul(response->comp_id));
+	sbuf_printf(sb, "Component Revision: %#x\n", response->comp_revision);
+	sbuf_printf(sb, "Vendor Specific: 0x%016jx\n",
+		    (uintmax_t)scsi_8btou64(response->vendor_specific));
+}
+
+/*
+ * Compose a SMP REPORT GENERAL request and put it into a CCB.  This is
+ * current as of SPL Revision 7.
+ */
+void
+smp_report_general(struct ccb_smpio *smpio, uint32_t retries,
+		   void (*cbfcnp)(struct cam_periph *, union ccb *),
+		   struct smp_report_general_request *request, int request_len,
+		   uint8_t *response, int response_len, int long_response,
+		   uint32_t timeout)
+{
+	cam_fill_smpio(smpio,
+		       retries,
+		       cbfcnp,
+		       /*flags*/CAM_DIR_BOTH,
+		       (uint8_t *)request,
+		       request_len - SMP_CRC_LEN,
+		       response,
+		       response_len,
+		       timeout);
+
+	bzero(request, sizeof(*request));
+
+	request->frame_type = SMP_FRAME_TYPE_REQUEST;
+	request->function = SMP_FUNC_REPORT_GENERAL;
+	request->response_len = long_response ? SMP_RG_RESPONSE_LEN : 0;
+	request->request_len = 0;
+}
+
+/*
+ * Compose a SMP DISCOVER request and put it into a CCB.  This is current
+ * as of SPL Revision 7.
+ */
+void
+smp_discover(struct ccb_smpio *smpio, uint32_t retries,
+	     void (*cbfcnp)(struct cam_periph *, union ccb *),
+	     struct smp_discover_request *request, int request_len,
+	     uint8_t *response, int response_len, int long_response,
+	     int ignore_zone_group, int phy, uint32_t timeout)
+{
+	cam_fill_smpio(smpio,
+		       retries,
+		       cbfcnp,
+		       /*flags*/CAM_DIR_BOTH,
+		       (uint8_t *)request,
+		       request_len - SMP_CRC_LEN,
+		       response,
+		       response_len,
+		       timeout);
+
+	bzero(request, sizeof(*request));
+	request->frame_type = SMP_FRAME_TYPE_REQUEST;
+	request->function = SMP_FUNC_DISCOVER;
+	request->response_len = long_response ? SMP_DIS_RESPONSE_LEN : 0;
+	request->request_len = long_response ? SMP_DIS_REQUEST_LEN : 0;
+	if (ignore_zone_group != 0)
+		request->ignore_zone_group |= SMP_DIS_IGNORE_ZONE_GROUP;
+	request->phy = phy;
+}
+
+/*
+ * Compose a SMP REPORT MANUFACTURER INFORMATION request and put it into a
+ * CCB.  This is current as of SPL Revision 7.
+ */
+void
+smp_report_manuf_info(struct ccb_smpio *smpio, uint32_t retries,
+		      void (*cbfcnp)(struct cam_periph *, union ccb *),
+		      struct smp_report_manuf_info_request *request,
+		      int request_len, uint8_t *response, int response_len,
+		      int long_response, uint32_t timeout)
+{
+	cam_fill_smpio(smpio,
+		       retries,
+		       cbfcnp,
+		       /*flags*/CAM_DIR_BOTH,
+		       (uint8_t *)request,
+		       request_len - SMP_CRC_LEN,
+		       response,
+		       response_len,
+		       timeout);
+
+	bzero(request, sizeof(*request));
+
+	request->frame_type = SMP_FRAME_TYPE_REQUEST;
+	request->function = SMP_FUNC_REPORT_MANUF_INFO;
+	request->response_len = long_response ? SMP_RMI_RESPONSE_LEN : 0;
+	request->request_len = long_response ? SMP_RMI_REQUEST_LEN : 0;
+}
+
+/*
+ * Compose a SMP PHY CONTROL request and put it into a CCB.  This is
+ * current as of SPL Revision 7.
+ */
+void
+smp_phy_control(struct ccb_smpio *smpio, uint32_t retries,
+		void (*cbfcnp)(struct cam_periph *, union ccb *),
+		struct smp_phy_control_request *request, int request_len,
+		uint8_t *response, int response_len, int long_response,
+		uint32_t expected_exp_change_count, int phy, int phy_op,
+		int update_pp_timeout_val, uint64_t attached_device_name,
+		int prog_min_prl, int prog_max_prl, int slumber_partial,
+		int pp_timeout_value, uint32_t timeout)
+{
+	cam_fill_smpio(smpio,
+		       retries,
+		       cbfcnp,
+		       /*flags*/CAM_DIR_BOTH,
+		       (uint8_t *)request,
+		       request_len - SMP_CRC_LEN,
+		       response,
+		       response_len,
+		       timeout);
+
+	bzero(request, sizeof(*request));
+
+	request->frame_type = SMP_FRAME_TYPE_REQUEST;
+	request->function = SMP_FUNC_PHY_CONTROL;
+	request->response_len = long_response ? SMP_PC_RESPONSE_LEN : 0;
+	request->request_len = long_response ? SMP_PC_REQUEST_LEN : 0;
+	scsi_ulto2b(expected_exp_change_count, request->expected_exp_chg_cnt);
+	request->phy = phy;
+	request->phy_operation = phy_op;
+
+	if (update_pp_timeout_val != 0)
+		request->update_pp_timeout |= SMP_PC_UPDATE_PP_TIMEOUT;
+
+	scsi_u64to8b(attached_device_name, request->attached_device_name);
+	request->prog_min_phys_link_rate = (prog_min_prl <<
+		SMP_PC_PROG_MIN_PL_RATE_SHIFT) & SMP_PC_PROG_MIN_PL_RATE_MASK;
+	request->prog_max_phys_link_rate = (prog_max_prl <<
+		SMP_PC_PROG_MAX_PL_RATE_SHIFT) & SMP_PC_PROG_MAX_PL_RATE_MASK;
+	request->config_bits0 = slumber_partial;
+	request->pp_timeout_value = pp_timeout_value;
+}
diff --git a/sys/bus/cam/scsi/smp_all.h b/sys/bus/cam/scsi/smp_all.h
new file mode 100644
index 0000000..b42fd8f
--- /dev/null
+++ b/sys/bus/cam/scsi/smp_all.h
@@ -0,0 +1,520 @@
+/*-
+ * Copyright (c) 2010 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * 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 $
+ * $FreeBSD: src/sys/cam/scsi/smp_all.h,v 1.2 2012/11/17 01:51:22 svnexp Exp $
+ */
+
+/*
+ * Serial Management Protocol definitions.
+ */
+
+#ifndef	_SCSI_SMP_ALL_H
+#define	_SCSI_SMP_ALL_H	1
+
+#define	SMP_FRAME_TYPE_REQUEST	0x40
+#define	SMP_FRAME_TYPE_RESPONSE	0x41
+#define	SMP_WORD_LEN		4
+#define	SMP_CRC_LEN		4
+
+/*
+ * SMP Functions (current as of SPL Revision 7)
+ */
+/* 0x00 to 0x7f: SMP input functions */
+/* 0x00 to 0x0f: General SMP input functions */
+#define	SMP_FUNC_REPORT_GENERAL		0x00
+#define	SMP_FUNC_REPORT_MANUF_INFO	0x01
+#define	SMP_FUNC_REPORT_SC_STATUS	0x03
+#define	SMP_FUNC_REPORT_ZONE_PERM_TBL	0x04
+#define	SMP_FUNC_REPORT_ZONE_MAN_PWD	0x05
+#define	SMP_FUNC_REPORT_BROADCAST	0x06
+
+/* 0x10 to 0x1f: Phy-based SMP input functions */
+#define	SMP_FUNC_DISCOVER		0x10
+#define	SMP_FUNC_REPORT_PHY_ERR_LOG	0x11
+#define	SMP_FUNC_REPORT_PHY_SATA	0x12
+#define	SMP_FUNC_REPORT_ROUTE_INFO	0x13
+#define	SMP_FUNC_REPORT_PHY_EVENT	0x14
+
+/* 0x20 to 0x2f: Descriptor list-based SMP input functions */
+#define	SMP_FUNC_DISCOVER_LIST		0x20
+#define	SMP_FUNC_REPORT_PHY_EVENT_LIST	0x21
+#define	SMP_FUNC_REPORT_EXP_RTL		0x22
+
+/* 0x30 to 0x3f: Reserved for SMP input functions */
+/* 0x40 to 0x7f: Vendor specific */
+
+/* 0x80 to 0xff: SMP output functions */
+/* 0x80 to 0x8f: General SMP output functions */
+#define	SMP_FUNC_CONFIG_GENERAL		0x80
+#define	SMP_FUNC_ENABLE_DISABLE_ZONING	0x81
+#define	SMP_FUNC_ZONED_BROADCAST	0x85
+#define	SMP_FUNC_ZONE_LOCK		0x86
+#define	SMP_FUNC_ZONE_ACTIVATE		0x87
+#define	SMP_FUNC_ZONE_UNLOCK		0x88
+#define	SMP_FUNC_CONFIG_ZM_PWD		0x89
+#define	SMP_FUNC_CONFIG_ZONE_PHY_INFO	0x8a
+#define	SMP_FUNC_CONFIG_ZONE_PERM_TBL	0x8b
+
+/* 0x90 to 0x9f: Phy-based SMP output functions */
+#define	SMP_FUNC_CONFIG_ROUTE_INFO	0x90
+#define	SMP_FUNC_PHY_CONTROL		0x91
+#define	SMP_FUNC_PHY_TEST_FUNC		0x92
+#define	SMP_FUNC_CONFIG_PHY_EVENT	0x93
+
+/* 0xa0 to 0xbf: Reserved for SMP output functions */
+/* 0xc0 to 0xff: Vendor specific */
+
+/*
+ * Function Results (current as of SPL Revision 7)
+ */
+#define	SMP_FR_ACCEPTED			0x00
+#define	SMP_FR_UNKNOWN_FUNC		0x01
+#define	SMP_FR_FUNCTION_FAILED		0x02
+#define	SMP_FR_INVALID_REQ_FRAME_LEN	0x03
+#define	SMP_FR_INVALID_EXP_CHG_CNT	0x04
+#define	SMP_FR_BUSY			0x05
+#define	SMP_FR_INCOMPLETE_DESC_LIST	0x06
+#define	SMP_FR_PHY_DOES_NOT_EXIST	0x10
+#define	SMP_FR_INDEX_DOES_NOT_EXIST	0x11
+#define	SMP_FR_PHY_DOES_NOT_SUP_SATA	0x12
+#define	SMP_FR_UNKNOWN_PHY_OP		0x13
+#define	SMP_FR_UNKNOWN_PHY_TEST_FUNC	0x14
+#define	SMP_FR_PHY_TEST_FUNC_INPROG	0x15
+#define	SMP_FR_PHY_VACANT		0x16
+#define	SMP_FR_UNKNOWN_PHY_EVENT_SRC	0x17
+#define	SMP_FR_UNKNOWN_DESC_TYPE	0x18
+#define	SMP_FR_UNKNOWN_PHY_FILTER	0x19
+#define	SMP_FR_AFFILIATION_VIOLATION	0x1a
+#define	SMP_FR_SMP_ZONE_VIOLATION	0x20
+#define	SMP_FR_NO_MGMT_ACCESS_RIGHTS	0x21
+#define	SMP_FR_UNKNOWN_ED_ZONING_VAL	0x22
+#define	SMP_FR_ZONE_LOCK_VIOLATION	0x23
+#define	SMP_FR_NOT_ACTIVATED		0x24
+#define	SMP_FR_ZG_OUT_OF_RANGE		0x25
+#define	SMP_FR_NO_PHYS_PRESENCE		0x26
+#define	SMP_FR_SAVING_NOT_SUP		0x27
+#define	SMP_FR_SRC_ZONE_DNE		0x28
+#define	SMP_FR_DISABLED_PWD_NOT_SUP	0x29
+
+/*
+ * REPORT GENERAL request and response, current as of SPL Revision 7.
+ */
+struct smp_report_general_request
+{
+	uint8_t	frame_type;
+	uint8_t	function;
+	uint8_t	response_len;
+	uint8_t	request_len;
+	uint8_t	crc[4];
+};
+
+struct smp_report_general_response
+{
+	uint8_t	frame_type;
+	uint8_t	function;
+	uint8_t	function_result;
+	uint8_t	response_len;
+#define	SMP_RG_RESPONSE_LEN		0x11
+	uint8_t	expander_change_count[2];
+	uint8_t	expander_route_indexes[2];
+	uint8_t	long_response;
+#define	SMP_RG_LONG_RESPONSE		0x80
+	uint8_t	num_phys;
+	uint8_t	config_bits0;
+#define	SMP_RG_TABLE_TO_TABLE_SUP	0x80
+#define	SMP_RG_ZONE_CONFIGURING		0x40
+#define	SMP_RG_SELF_CONFIGURING		0x20
+#define	SMP_RG_STP_CONTINUE_AWT		0x10
+#define	SMP_RG_OPEN_REJECT_RETRY_SUP	0x08
+#define	SMP_RG_CONFIGURES_OTHERS	0x04
+#define	SMP_RG_CONFIGURING		0x02
+#define	SMP_RG_EXT_CONFIG_ROUTE_TABLE	0x01
+	uint8_t	reserved0;
+	uint8_t	encl_logical_id[8];
+	uint8_t	reserved1[8];
+	uint8_t	reserved2[2];
+	uint8_t	stp_bus_inact_time_limit[2];
+	uint8_t	stp_max_conn_time_limit[2];
+	uint8_t	stp_smp_it_nexus_loss_time[2];
+	uint8_t	config_bits1;
+#define	SMP_RG_NUM_ZONE_GROUPS_MASK	0xc0
+#define	SMP_RG_NUM_ZONE_GROUPS_SHIFT	6
+#define	SMP_RG_ZONE_LOCKED		0x10
+#define	SMP_RG_PP_SUPPORTED		0x08
+#define	SMP_RG_PP_ASSERTED		0x04
+#define	SMP_RG_ZONING_SUPPORTED		0x02
+#define	SMP_RG_ZONING_ENABLED		0x01
+	uint8_t	config_bits2;
+#define	SMP_RG_SAVING			0x10
+#define	SMP_RG_SAVING_ZM_PWD_SUP	0x08
+#define	SMP_RG_SAVING_PHY_INFO_SUP	0x04
+#define	SMP_RG_SAVING_ZPERM_TAB_SUP	0x02
+#define	SMP_RG_SAVING_ZENABLED_SUP	0x01
+	uint8_t	max_num_routed_addrs[2];
+	uint8_t	active_zm_address[8];
+	uint8_t	zone_lock_inact_time_limit[2];
+	uint8_t	reserved3[2];
+	uint8_t	reserved4;
+	uint8_t	first_encl_conn_el_index;
+	uint8_t	num_encl_conn_el_indexes;
+	uint8_t	reserved5;
+	uint8_t	reduced_functionality;
+#define	SMP_RG_REDUCED_FUNCTIONALITY	0x80
+	uint8_t	time_to_reduced_func;
+	uint8_t	initial_time_to_reduced_func;
+	uint8_t	max_reduced_func_time;
+	uint8_t	last_sc_stat_desc_index[2];
+	uint8_t	max_sc_stat_descs[2];
+	uint8_t	last_phy_evl_desc_index[2];
+	uint8_t	max_stored_pel_descs[2];
+	uint8_t	stp_reject_to_open_limit[2];
+	uint8_t	reserved6[2];
+	uint8_t	crc[4];
+};
+
+/*
+ * REPORT MANUFACTURER INFORMATION request and response, current as of SPL
+ * Revision 7.
+ */
+struct smp_report_manuf_info_request
+{
+	uint8_t	frame_type;
+	uint8_t	function;
+	uint8_t	response_len;
+	uint8_t	request_len;
+#define	SMP_RMI_REQUEST_LEN		0x00
+	uint8_t	crc[4];
+};
+
+struct smp_report_manuf_info_response
+{
+	uint8_t	frame_type;
+	uint8_t	function;
+	uint8_t	function_result;
+	uint8_t	response_len;
+#define	SMP_RMI_RESPONSE_LEN		0x0e
+	uint8_t	expander_change_count[2];
+	uint8_t	reserved0[2];
+	uint8_t	sas_11_format;
+#define	SMP_RMI_SAS11_FORMAT		0x01
+	uint8_t	reserved1[3];
+	uint8_t	vendor[8];
+	uint8_t	product[16];
+	uint8_t	revision[4];
+	uint8_t	comp_vendor[8];
+	uint8_t	comp_id[2];
+	uint8_t	comp_revision;
+	uint8_t	reserved2;
+	uint8_t	vendor_specific[8];
+	uint8_t	crc[4];
+};
+
+/*
+ * DISCOVER request and response, current as of SPL Revision 7.
+ */
+struct smp_discover_request
+{
+	uint8_t	frame_type;
+	uint8_t	function;
+	uint8_t response_len;
+	uint8_t request_len;
+#define	SMP_DIS_REQUEST_LEN		0x02
+	uint8_t reserved0[4];
+	uint8_t	ignore_zone_group;
+#define	SMP_DIS_IGNORE_ZONE_GROUP	0x01
+	uint8_t	phy;
+	uint8_t	reserved1[2];
+	uint8_t	crc[4];
+};
+
+struct smp_discover_response
+{
+	uint8_t	frame_type;
+	uint8_t	function;
+	uint8_t	function_result;
+	uint8_t	response_len;
+#define	SMP_DIS_RESPONSE_LEN		0x20
+	uint8_t	expander_change_count[2];
+	uint8_t	reserved0[3];
+	uint8_t	phy;
+	uint8_t	reserved1[2];
+	uint8_t	attached_device;
+#define	SMP_DIS_AD_TYPE_MASK		0x70
+#define	SMP_DIS_AD_TYPE_NONE		0x00
+#define	SMP_DIS_AD_TYPE_SAS_SATA	0x10
+#define	SMP_DIS_AD_TYPE_EXP		0x20
+#define	SMP_DIS_AD_TYPE_EXP_OLD		0x30
+#define	SMP_DIS_ATTACH_REASON_MASK	0x0f
+	uint8_t	neg_logical_link_rate;
+#define	SMP_DIS_LR_MASK			0x0f
+#define	SMP_DIS_LR_DISABLED		0x01
+#define	SMP_DIS_LR_PHY_RES_PROB		0x02
+#define	SMP_DIS_LR_SPINUP_HOLD		0x03
+#define	SMP_DIS_LR_PORT_SEL		0x04
+#define	SMP_DIS_LR_RESET_IN_PROG	0x05
+#define	SMP_DIS_LR_UNSUP_PHY_ATTACHED	0x06
+#define	SMP_DIS_LR_G1_15GBPS		0x08
+#define	SMP_DIS_LR_G2_30GBPS		0x09
+#define	SMP_DIS_LR_G3_60GBPS		0x0a
+	uint8_t	config_bits0;
+#define	SMP_DIS_ATTACHED_SSP_INIT	0x08
+#define	SMP_DIS_ATTACHED_STP_INIT	0x04
+#define	SMP_DIS_ATTACHED_SMP_INIT	0x02
+#define	SMP_DIS_ATTACHED_SATA_HOST	0x01
+	uint8_t	config_bits1;
+#define	SMP_DIS_ATTACHED_SATA_PORTSEL	0x80
+#define	SMP_DIS_STP_BUFFER_TOO_SMALL	0x10
+#define	SMP_DIS_ATTACHED_SSP_TARG	0x08
+#define	SMP_DIS_ATTACHED_STP_TARG	0x04
+#define	SMP_DIS_ATTACHED_SMP_TARG	0x02
+#define	SMP_DIS_ATTACHED_SATA_DEV	0x01
+	uint8_t	sas_address[8];
+	uint8_t	attached_sas_address[8];
+	uint8_t	attached_phy_id;
+	uint8_t	config_bits2;
+#define	SMP_DIS_ATT_SLUMB_CAP		0x10
+#define	SMP_DIS_ATT_PAR_CAP		0x08
+#define	SMP_DIS_ATT_IN_ZPSDS_PER	0x04
+#define	SMP_DIS_ATT_REQ_IN_ZPSDS	0x02
+#define	SMP_DIS_ATT_BREAK_RPL_CAP	0x01
+	uint8_t	reserved2[6];
+	uint8_t	link_rate0;
+#define	SMP_DIS_PROG_MIN_LR_MASK	0xf0
+#define	SMP_DIS_PROG_MIN_LR_SHIFT	4
+#define	SMP_DIS_HARD_MIN_LR_MASK	0x0f
+	uint8_t	link_rate1;
+#define	SMP_DIS_PROG_MAX_LR_MAX		0xf0
+#define	SMP_DIS_PROG_MAX_LR_SHIFT	4
+#define	SMP_DIS_HARD_MAX_LR_MASK	0x0f
+	uint8_t	phy_change_count;
+	uint8_t	pp_timeout;
+#define	SMP_DIS_VIRTUAL_PHY		0x80
+#define	SMP_DIS_PP_TIMEOUT_MASK		0x0f
+	uint8_t	routing_attr;
+	uint8_t	conn_type;
+	uint8_t	conn_el_index;
+	uint8_t	conn_phys_link;
+	uint8_t	config_bits3;
+#define	SMP_DIS_PHY_POW_COND_MASK	0xc0
+#define	SMP_DIS_PHY_POW_COND_SHIFT	6
+#define	SMP_DIS_SAS_SLUMB_CAP		0x08
+#define	SMP_DIS_SAS_PART_CAP		0x04
+#define	SMP_DIS_SATA_SLUMB_CAP		0x02
+#define	SMP_DIS_SATA_PART_CAP		0x01
+	uint8_t	config_bits4;
+#define	SMP_DIS_SAS_SLUMB_ENB		0x08
+#define	SMP_DIS_SAS_PART_ENB		0x04
+#define	SMP_DIS_SATA_SLUMB_ENB		0x02
+#define	SMP_DIS_SATA_PART_ENB		0x01
+	uint8_t	vendor_spec[2];
+	uint8_t	attached_dev_name[8];
+	uint8_t	config_bits5;
+#define	SMP_DIS_REQ_IN_ZPSDS_CHG	0x40
+#define	SMP_DIS_IN_ZPSDS_PER		0x20
+#define	SMP_DIS_REQ_IN_ZPSDS		0x10
+#define	SMP_DIS_ZG_PER			0x04
+#define	SMP_DIS_IN_ZPSDS		0x02
+#define	SMP_DIS_ZONING_ENB		0x01
+	uint8_t	reserved3[2];
+	uint8_t	zone_group;
+	uint8_t	self_config_status;
+	uint8_t	self_config_levels_comp;
+	uint8_t	reserved4[2];
+	uint8_t	self_config_sas_addr[8];
+	uint8_t	prog_phy_cap[4];
+	uint8_t	current_phy_cap[4];
+	uint8_t	attached_phy_cap[4];
+	uint8_t	reserved5[6];
+	uint8_t	neg_phys_link_rate;
+#define	SMP_DIS_REASON_MASK		0xf0
+#define	SMP_DIS_REASON_SHIFT		4
+#define	SMP_DIS_PHYS_LR_MASK		0x0f
+	uint8_t	config_bits6;
+#define	SMP_DIS_OPTICAL_MODE_ENB	0x04
+#define	SMP_DIS_NEG_SSC			0x02
+#define	SMP_DIS_HW_MUX_SUP		0x01
+	uint8_t	config_bits7;
+#define	SMP_DIS_DEF_IN_ZPSDS_PER	0x20
+#define	SMP_DIS_DEF_REQ_IN_ZPSDS	0x10
+#define	SMP_DIS_DEF_ZG_PER		0x04
+#define	SMP_DIS_DEF_ZONING_ENB		0x01
+	uint8_t	reserved6;
+	uint8_t	reserved7;
+	uint8_t	default_zone_group;
+	uint8_t	config_bits8;
+#define	SMP_DIS_SAVED_IN_ZPSDS_PER	0x20
+#define	SMP_DIS_SAVED_REQ_IN_SPSDS	0x10
+#define	SMP_DIS_SAVED_ZG_PER		0x04
+#define	SMP_DIS_SAVED_ZONING_ENB	0x01
+	uint8_t	reserved8;
+	uint8_t	reserved9;
+	uint8_t	saved_zone_group;
+	uint8_t	config_bits9;
+#define	SMP_DIS_SHADOW_IN_ZPSDS_PER	0x20
+#define	SMP_DIS_SHADOW_IN_REQ_IN_ZPSDS	0x10
+#define	SMP_DIS_SHADOW_ZG_PER		0x04
+	uint8_t reserved10;
+	uint8_t reserved11;
+	uint8_t	shadow_zone_group;
+	uint8_t	device_slot_num;
+	uint8_t	device_slot_group_num;
+	uint8_t	device_slot_group_out_conn[6];
+	uint8_t	stp_buffer_size[2];
+	uint8_t	reserved12;
+	uint8_t	reserved13;
+	uint8_t	crc[4];
+};
+
+/*
+ * PHY CONTROL request and response.  Current as of SPL Revision 7.
+ */
+struct smp_phy_control_request
+{
+	uint8_t	frame_type;
+	uint8_t	function;
+	uint8_t response_len;
+#define	SMP_PC_RESPONSE_LEN		0x00
+	uint8_t request_len;
+#define	SMP_PC_REQUEST_LEN		0x09
+	uint8_t expected_exp_chg_cnt[2];
+	uint8_t reserved0[3];
+	uint8_t phy;
+	uint8_t phy_operation;
+#define	SMP_PC_PHY_OP_NOP		0x00
+#define	SMP_PC_PHY_OP_LINK_RESET	0x01
+#define	SMP_PC_PHY_OP_HARD_RESET	0x02
+#define	SMP_PC_PHY_OP_DISABLE		0x03
+#define	SMP_PC_PHY_OP_CLEAR_ERR_LOG	0x05
+#define	SMP_PC_PHY_OP_CLEAR_AFFILIATON	0x06
+#define	SMP_PC_PHY_OP_TRANS_SATA_PSS	0x07
+#define	SMP_PC_PHY_OP_CLEAR_STP_ITN_LS	0x08
+#define	SMP_PC_PHY_OP_SET_ATT_DEV_NAME	0x09
+	uint8_t update_pp_timeout;
+#define	SMP_PC_UPDATE_PP_TIMEOUT	0x01
+	uint8_t reserved1[12];
+	uint8_t attached_device_name[8];
+	uint8_t prog_min_phys_link_rate;
+#define	SMP_PC_PROG_MIN_PL_RATE_MASK	0xf0
+#define	SMP_PC_PROG_MIN_PL_RATE_SHIFT	4
+	uint8_t prog_max_phys_link_rate;
+#define	SMP_PC_PROG_MAX_PL_RATE_MASK	0xf0
+#define	SMP_PC_PROG_MAX_PL_RATE_SHIFT	4
+	uint8_t	config_bits0;
+#define	SMP_PC_SP_NC			0x00
+#define	SMP_PC_SP_DISABLE		0x02
+#define	SMP_PC_SP_ENABLE		0x01
+#define	SMP_PC_SAS_SLUMBER_NC		0x00
+#define	SMP_PC_SAS_SLUMBER_DISABLE	0x80
+#define	SMP_PC_SAS_SLUMBER_ENABLE	0x40
+#define	SMP_PC_SAS_SLUMBER_MASK		0xc0
+#define	SMP_PC_SAS_SLUMBER_SHIFT	6
+#define	SMP_PC_SAS_PARTIAL_NC		0x00
+#define	SMP_PC_SAS_PARTIAL_DISABLE	0x20
+#define	SMP_PC_SAS_PARTIAL_ENABLE	0x10
+#define	SMP_PC_SAS_PARTIAL_MASK		0x30
+#define	SMP_PC_SAS_PARTIAL_SHIFT	4
+#define	SMP_PC_SATA_SLUMBER_NC		0x00
+#define	SMP_PC_SATA_SLUMBER_DISABLE	0x08
+#define	SMP_PC_SATA_SLUMBER_ENABLE	0x04
+#define	SMP_PC_SATA_SLUMBER_MASK	0x0c
+#define	SMP_PC_SATA_SLUMBER_SHIFT	2
+#define	SMP_PC_SATA_PARTIAL_NC		0x00
+#define	SMP_PC_SATA_PARTIAL_DISABLE	0x02
+#define	SMP_PC_SATA_PARTIAL_ENABLE	0x01
+#define	SMP_PC_SATA_PARTIAL_MASK	0x03
+#define	SMP_PC_SATA_PARTIAL_SHIFT	0
+	uint8_t	reserved2;
+	uint8_t	pp_timeout_value;
+#define	SMP_PC_PP_TIMEOUT_MASK		0x0f
+	uint8_t reserved3[3];
+	uint8_t	crc[4];
+};
+
+struct smp_phy_control_response
+{
+	uint8_t	frame_type;
+	uint8_t	function;
+	uint8_t	function_result;
+	uint8_t	response_len;
+#define	SMP_PC_RESPONSE_LEN		0x00
+	uint8_t crc[4];
+};
+
+__BEGIN_DECLS
+
+const char *smp_error_desc(int function_result);
+const char *smp_command_desc(uint8_t cmd_num);
+void smp_command_decode(uint8_t *smp_request, int request_len, struct sbuf *sb,
+			char *line_prefix, int first_line_len, int line_len);
+void smp_command_sbuf(struct ccb_smpio *smpio, struct sbuf *sb,
+		      char *line_prefix, int first_line_len, int line_len);
+
+#ifdef _KERNEL
+void smp_error_sbuf(struct ccb_smpio *smpio, struct sbuf *sb);
+#else /* !_KERNEL*/
+void smp_error_sbuf(struct cam_device *device, struct ccb_smpio *smpio,
+		    struct sbuf *sb);
+#endif /* _KERNEL/!_KERNEL */
+
+void smp_report_general_sbuf(struct smp_report_general_response *response,
+			     int response_len, struct sbuf *sb);
+
+void smp_report_manuf_info_sbuf(struct smp_report_manuf_info_response *response,
+				int response_len, struct sbuf *sb);
+
+void smp_report_general(struct ccb_smpio *smpio, uint32_t retries,
+			void (*cbfcnp)(struct cam_periph *, union ccb *),
+			struct smp_report_general_request *request,
+			int request_len, uint8_t *response, int response_len,
+			int long_response, uint32_t timeout);
+
+void smp_discover(struct ccb_smpio *smpio, uint32_t retries,
+		  void (*cbfcnp)(struct cam_periph *, union ccb *),
+		  struct smp_discover_request *request, int request_len,
+		  uint8_t *response, int response_len, int long_response,
+		  int ignore_zone_group, int phy, uint32_t timeout);
+
+void smp_report_manuf_info(struct ccb_smpio *smpio, uint32_t retries,
+			   void (*cbfcnp)(struct cam_periph *, union ccb *),
+			   struct smp_report_manuf_info_request *request,
+			   int request_len, uint8_t *response, int response_len,
+			   int long_response, uint32_t timeout);
+
+void smp_phy_control(struct ccb_smpio *smpio, uint32_t retries,
+		     void (*cbfcnp)(struct cam_periph *, union ccb *),
+		     struct smp_phy_control_request *request, int request_len,
+		     uint8_t *response, int response_len, int long_response,
+		     uint32_t expected_exp_change_count, int phy, int phy_op,
+		     int update_pp_timeout_val, uint64_t attached_device_name,
+		     int prog_min_prl, int prog_max_prl, int slumber_partial,
+		     int pp_timeout_value, uint32_t timeout);
+__END_DECLS
+
+#endif /*_SCSI_SMP_ALL_H*/
diff --git a/sys/conf/files b/sys/conf/files
index ed631fd..63ced26 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -60,6 +60,7 @@ bus/cam/scsi/scsi_ses.c			optional ses
 bus/cam/scsi/scsi_sg.c			optional sg
 bus/cam/scsi/scsi_target.c		optional targ
 bus/cam/scsi/scsi_targ_bh.c		optional targbh
+bus/cam/scsi/smp_all.c			optional scbus
 
 dev/disk/dm/device-mapper.c		optional dm
 dev/disk/dm/dm_dev.c			optional dm
diff --git a/sys/dev/raid/mps/mps_sas.c b/sys/dev/raid/mps/mps_sas.c
index 1dfb8ec..327a581 100644
--- a/sys/dev/raid/mps/mps_sas.c
+++ b/sys/dev/raid/mps/mps_sas.c
@@ -89,9 +89,7 @@
 #include <bus/cam/cam_periph.h>
 #include <bus/cam/scsi/scsi_all.h>
 #include <bus/cam/scsi/scsi_message.h>
-#if 0 /* XXX __FreeBSD_version >= 900026 */
 #include <bus/cam/scsi/smp_all.h>
-#endif
 
 #include <dev/raid/mps/mpi/mpi2_type.h>
 #include <dev/raid/mps/mpi/mpi2.h>
@@ -152,12 +150,10 @@ static void mpssas_direct_drive_io(struct mpssas_softc *sassc,
 static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *);
 static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *);
 static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
-#if __FreeBSD_version >= 900026
 static void mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm);
 static void mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb,
 			       uint64_t sasaddr);
 static void mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb);
-#endif //FreeBSD_version >= 900026
 static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *);
 static int  mpssas_send_abort(struct mps_softc *sc, struct mps_command *tm, struct mps_command *cm);
 static int  mpssas_send_reset(struct mps_softc *sc, struct mps_command *tm, uint8_t type);
@@ -895,11 +891,9 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
 	case XPT_SCSI_IO:
 		mpssas_action_scsiio(sassc, ccb);
 		return;
-#if __FreeBSD_version >= 900026
 	case XPT_SMP_IO:
 		mpssas_action_smpio(sassc, ccb);
 		return;
-#endif
 	default:
 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
 		break;
@@ -2287,7 +2281,6 @@ mpssas_direct_drive_io(struct mpssas_softc *sassc, struct mps_command *cm,
 	}
 }
 
-#if __FreeBSD_version >= 900026
 static void
 mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm)
 {
@@ -2643,7 +2636,6 @@ bailout:
 	xpt_done(ccb);
 
 }
-#endif //__FreeBSD_version >= 900026
 
 static void
 mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb)
-- 
1.7.12.2

