SMP passthrough support for CAM and mps(4)

Kenneth D. Merry ken at freebsd.org
Tue Nov 30 06:31:13 UTC 2010


Hey folks,

I've made a few changes to the SMP passthrough patchset, based on feedback
from mjacob@ and scottl@ and my own testing, so here are updated patches
to head as of today.

My plan is to commit this to head tomorrow (Tuesday November 30th), so if
you have any objections or comments please speak now.

The changes from the last patchset are:

Change 465872 on 2010/11/19 by kenm at ken.spectrabsd

        Add a 'quiet' (-q) argument to camcontrol smpphylist.
        
        With this option, camcontrol will only print out phys that are
        attached to devices in the CAM EDT (Existing Device Table).  This
        option also suppresses headers, and makes the output a little
        easier to parse from a script.
        
        camcontrol.c:   Add the -q option to the smpphylist subcommand.
        
        camcontrol.8:   Document the -q option for smpphylist.

Change 466160 on 2010/11/24 by kenm at ken.spectrabsd

        Tweak the output of 'camcontrol smpphylist'.
        
        Don't pad the output with leading zeros, since that generally
        indicates an octal number and confuses things when trying to write
        scripts to parse the output.

Change 466161 on 2010/11/24 by kenm at ken.spectrabsd

        Fix a couple of issues with msp_free_command:
        
        - Set cm_max_segs=0, otherwise we get lots of spurious messages as
          soon as a command where that has been set gets recycled for use
          on a normal command.
        
        - Set cm_reply = NULL.  Scott Long told me about this bug, where
          the reply can end up being re-used.

Change 466162 on 2010/11/24 by kenm at ken.spectrabsd

        Clarify some comments about the XPT_GDEV_ADVINFO ccb.

Change 466462 on 2010/11/29 by kenm at ken.spectrabsd

        Add more comments about SMP protocol version support.
        
        smp_all.c,
        smp_all.h:      Describe which version of the protocol the various
                        structures and functions are based upon.  The hope
                        is that as they are revised in the future, the
                        authors will update the version.

Change 466459 on 2010/11/29 by kenm at ken.spectrabsd

        Bump __FreeBSD_version to 900026 for the inclusion of the
        XPT_GDEV_ADVINFO and XPT_SMP_IO CCBs.
        
        sys/param.h:    Bump __FreeBSD_version
        
        mps_sas.c:      Make SMP passthrough CCB support contingent on
                        __FreeBSD_version.  This will make the driver
                        easier to merge back to older versions of FreeBSD
                        without SMP passthrough.


Thanks,

Ken

On Tue, Nov 16, 2010 at 14:05:07 -0700, Kenneth D. Merry wrote:
> Hey folks,
> 
> Here is largely complete work to support SMP passthrough in CAM and the
> mps(4) driver.
> 
> My plan is to commit this in the near future, so I'm looking for any
> comments.
> 
> This allows users to send arbitrary SMP commands down to SAS expanders.
> camcontrol(8) has canned support for some SMP commands (like report general,
> report manufacturer information and phy control), and there is a new
> 'camcontrol smpcmd' command that allows sending generic SMP requests.
> (The syntax is like 'camcontrol cmd'.)
> 
> So, you can do things like this:
> 
> Grab the list of devices:
> 
> [root at doc-sd ~]# camcontrol devlist
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 2 lun 0 (da0,pass0)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 3 lun 0 (da1,pass1)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 6 lun 0 (da2,pass2)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 7 lun 0 (da3,pass3)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 10 lun 0 (da4,pass4)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 11 lun 0 (da5,pass5)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 14 lun 0 (da6,pass6)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 15 lun 0 (da7,pass7)
> <LSILOGIC SASX28 A.0 9>            at scbus0 target 20 lun 0 (ses0,pass8)
> 
> Look to see which PHY da0 is attached to:
> 
> [root at doc-sd ~]# camcontrol smpphylist ses0
> 30 PHYs:
> PHY  Attached SAS Address
> 000  0x500304800070ab10
> 001  0x500304800070ab10
> 002  0x500304800070ab10
> 003  0x500304800070ab10
> 004  0x500304800070ab10
> 005  0x500304800070ab10
> 006  0x500304800070ab10
> 007  0x500304800070ab10
> 008  0x0000000000000000
> 009  0x0000000000000000
> 010  0x500304800045154a   <ATA Hitachi HUA72101 AB0A>       (da0,pass0)
> 011  0x500304800045154b   <ATA Hitachi HUA72101 AB0A>       (da1,pass1)
> 012  0x0000000000000000
> 013  0x0000000000000000
> 014  0x500304800045154e   <ATA Hitachi HUA72101 AB0A>       (da2,pass2)
> 015  0x500304800045154f   <ATA Hitachi HUA72101 AB0A>       (da3,pass3)
> 016  0x0000000000000000
> 017  0x0000000000000000
> 018  0x5003048000451552   <ATA Hitachi HUA72101 AB0A>       (da4,pass4)
> 019  0x5003048000451553   <ATA Hitachi HUA72101 AB0A>       (da5,pass5)
> 020  0x0000000000000000
> 021  0x0000000000000000
> 022  0x5003048000451556   <ATA Hitachi HUA72101 AB0A>       (da6,pass6)
> 023  0x5003048000451557   <ATA Hitachi HUA72101 AB0A>       (da7,pass7)
> 024  0x0000000000000000
> 025  0x0000000000000000
> 026  0x0000000000000000
> 027  0x0000000000000000
> 028  0x500304800045157d
> 029  0x500304800045157e
> 
> Turn off PHY 10:
> 
> [root at doc-sd ~]# camcontrol smppc ses0 -p 10 -o disable
> 
> See that da0 has gone away:
> 
> [root at doc-sd ~]# camcontrol devlist
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 3 lun 0 (da1,pass1)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 6 lun 0 (da2,pass2)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 7 lun 0 (da3,pass3)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 10 lun 0 (da4,pass4)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 11 lun 0 (da5,pass5)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 14 lun 0 (da6,pass6)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 15 lun 0 (da7,pass7)
> <LSILOGIC SASX28 A.0 9>            at scbus0 target 20 lun 0 (ses0,pass8)
> 
> Send a link reset to PHY 10 to turn it back on:
> 
> [root at doc-sd ~]# camcontrol smppc ses0 -p 10 -o linkreset
> 
> See that CAM is probing that device:
> 
> [root at doc-sd ~]# camcontrol devlist
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 2 lun 0 (probe0)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 3 lun 0 (da1,pass1)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 6 lun 0 (da2,pass2)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 7 lun 0 (da3,pass3)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 10 lun 0 (da4,pass4)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 11 lun 0 (da5,pass5)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 14 lun 0 (da6,pass6)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 15 lun 0 (da7,pass7)
> <LSILOGIC SASX28 A.0 9>            at scbus0 target 20 lun 0 (ses0,pass8)
> 
> da0 is now attached once more:
> 
> [root at doc-sd ~]# camcontrol devlist
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 2 lun 0 (pass0,da0)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 3 lun 0 (da1,pass1)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 6 lun 0 (da2,pass2)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 7 lun 0 (da3,pass3)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 10 lun 0 (da4,pass4)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 11 lun 0 (da5,pass5)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 14 lun 0 (da6,pass6)
> <ATA Hitachi HUA72101 AB0A>        at scbus0 target 15 lun 0 (da7,pass7)
> <LSILOGIC SASX28 A.0 9>            at scbus0 target 20 lun 0 (ses0,pass8)
> 
> Since SMP is a totally different protocol than say SCSI or ATA, we don't
> have support for probing and attaching devices that are SMP-only right now.
> 
> The LSI and Maxim expanders that I have tested so far have SMP targets that
> do not support SSP.  So they don't appear in the topology.  (The SES device
> is in the expander as well, but the SES devices on the LSI and Maxim
> expanders I have seen so far do not include SMP targets.)
> 
> We've got plans to include SMP targets in the CAM topology later on, but for
> now, the semantics of the XPT_SMP_IO CCB are that it will be executed
> either by the addressed device, if it supports SMP, or by that device's
> parent, if it supports SMP.
> 
> So, at the moment, you can actually send SMP commands to da0, which doesn't
> actually have an SMP target, and they'll get routed to the expander it
> hangs off of.
> 
> The patch against -current as of yesterday is attached.
> 
> Thanks,
> 
> Ken
> -- 
> Kenneth Merry
> ken at FreeBSD.ORG
-- 
Kenneth Merry
ken at FreeBSD.ORG
-------------- next part --------------
*** src/lib/libcam/Makefile.orig
--- src/lib/libcam/Makefile
***************
*** 3,9 ****
  LIB=		cam
  SHLIBDIR?=	/lib
  SRCS=		camlib.c scsi_cmdparse.c scsi_all.c scsi_da.c scsi_sa.c cam.c \
! 		ata_all.c
  INCS=		camlib.h
  
  DPADD=		${LIBSBUF}
--- 3,9 ----
  LIB=		cam
  SHLIBDIR?=	/lib
  SRCS=		camlib.c scsi_cmdparse.c scsi_all.c scsi_da.c scsi_sa.c cam.c \
! 		ata_all.c smp_all.c
  INCS=		camlib.h
  
  DPADD=		${LIBSBUF}
*** src/sbin/camcontrol/camcontrol.8.orig
--- src/sbin/camcontrol/camcontrol.8
***************
*** 131,136 ****
--- 131,173 ----
  .Op Fl r Ar fmt
  .Ek
  .Nm
+ .Ic smpcmd
+ .Op device id
+ .Op generic args
+ .Aq Fl r Ar len Ar fmt Op args
+ .Aq Fl R Ar len Ar fmt Op args
+ .Nm
+ .Ic smprg
+ .Op device id
+ .Op generic args
+ .Op Fl l
+ .Nm
+ .Ic smppc
+ .Op device id
+ .Op generic args
+ .Aq Fl p Ar phy
+ .Op Fl l
+ .Op Fl o Ar operation
+ .Op Fl d Ar name
+ .Op Fl m Ar rate
+ .Op Fl M Ar rate
+ .Op Fl T Ar pp_timeout
+ .Op Fl a Ar enable|disable
+ .Op Fl A Ar enable|disable
+ .Op Fl s Ar enable|disable
+ .Op Fl S Ar enable|disable
+ .Nm
+ .Ic smpphylist
+ .Op device id
+ .Op generic args
+ .Op Fl l
+ .Op Fl q
+ .Nm
+ .Ic smpmaninfo
+ .Op device id
+ .Op generic args
+ .Op Fl l
+ .Nm
  .Ic debug
  .Op Fl I
  .Op Fl P
***************
*** 554,559 ****
--- 591,761 ----
  .Sq - ,
  11 result registers will be written to standard output in hex.
  .El
+ .It Ic smpcmd
+ Allows the user to send an arbitrary Serial 
+ Management Protocol (SMP) command to a device.
+ The
+ .Ic smpcmd
+ function requires the
+ .Fl r
+ argument to specify the SMP request to be sent, and the
+ .Fl R
+ argument to specify the format of the SMP response.
+ The syntax for the SMP request and response arguments is documented in
+ .Xr cam_cdbparse 3 .
+ .Pp
+ Note that SAS adapters that support SMP passthrough (at least the currently
+ known adapters) do not accept CRC bytes from the user in the request and do
+ not pass CRC bytes back to the user in the response.
+ Therefore users should not include the CRC bytes in the length of the
+ request and not expect CRC bytes to be returned in the response.
+ .Bl -tag -width 17n
+ .It Fl r Ar len Ar fmt Op args
+ This specifies the size of the SMP request, without the CRC bytes, and the
+ SMP request format.  If the format is
+ .Sq - ,
+ .Ar len
+ bytes of data will be read from standard input and written as the SMP
+ request.
+ .It Fl R Ar len Ar fmt Op args
+ This specifies the size of the buffer allocated for the SMP response, and
+ the SMP response format.
+ If the format is
+ .Sq - ,
+ .Ar len
+ bytes of data will be allocated for the response and the response will be
+ written to standard output.
+ .El
+ .It Ic smprg
+ Allows the user to send the Serial Management Protocol (SMP) Report General
+ command to a device.
+ .Nm
+ will display the data returned by the Report General command.
+ .Bl -tag -width 8n
+ .It Fl l
+ Request the long response format.
+ Not all SMP targets support the long response format.
+ .El
+ .It Ic smppc
+ Allows the user to issue the Serial Management Protocol (SMP) PHY Control
+ command to a device.
+ This function should be used with some caution, as it can render devices
+ inaccessible, and could potentially cause data corruption as well.
+ The
+ .Fl p
+ argument is required to specify the PHY to operate on.
+ .Bl -tag -width 17n
+ .It Fl p Ar phy
+ Specify the PHY to operate on.
+ This argument is required. 
+ .It Fl l
+ Request the long request/response format.
+ Not all SMP targets support the long response format.
+ For the PHY Control command, this currently only affects whether the
+ request length is set to a value other than 0.
+ .It Fl o Ar operation
+ Specify a PHY control operation.
+ Only one
+ .Fl o
+ operation may be specified.
+ The operation may be specified numerically (in decimal, hexadecimal, or octal)
+ or one of the following operation names may be specified:
+ .Bl -tag -width 16n
+ .It nop
+ No operation.
+ It is not necessary to specify this argument.
+ .It linkreset
+ Send the LINK RESET command to the phy.
+ .It hardreset
+ Send the HARD RESET command to the phy.
+ .It disable
+ Send the DISABLE command to the phy.
+ Note that the LINK RESET or HARD RESET commands should re-enable the phy.
+ .It clearerrorlog
+ Send the CLEAR ERROR LOG command.
+ This clears the error log counters for the specified phy.
+ .It clearaffiliation
+ Send the CLEAR AFFILIATION command.
+ This clears the affiliation from the STP initiator port with the same SAS
+ address as the SMP initiator that requests the clear operation.
+ .It sataportsel
+ Send the TRANSMIT SATA PORT SELECTION SIGNAL command to the phy.
+ This will cause a SATA port selector to use the given phy as its active phy
+ and make the other phy inactive.
+ .It clearitnl
+ Send the CLEAR STP I_T NEXUS LOSS command to the PHY.
+ .It setdevname
+ Send the SET ATTACHED DEVICE NAME command to the PHY.
+ This requires the
+ .Fl d
+ argument to specify the device name.
+ .El
+ .It Fl d Ar name
+ Specify the attached device name.
+ This option is needed with the
+ .Fl o Ar setdevname
+ phy operation.
+ The name is a 64-bit number, and can be specified in decimal, hexadecimal
+ or octal format.
+ .It Fl m Ar rate
+ Set the minimum physical link rate for the phy.
+ This is a numeric argument.
+ Currently known link rates are:
+ .Bl -tag -width 5n
+ .It 0x0
+ Do not change current value.
+ .It 0x8
+ 1.5 Gbps
+ .It 0x9
+ 3 Gbps
+ .It 0xa
+ 6 Gbps
+ .El
+ .Pp
+ Other values may be specified for newer physical link rates.
+ .It Fl M Ar rate
+ Set the maximum physical link rate for the phy.
+ This is a numeric argument.
+ See the
+ .Fl m
+ argument description for known link rate arguments.
+ .It Fl T Ar pp_timeout
+ Set the partial pathway timeout value, in microseconds.
+ See the
+ .Tn ANSI
+ .Tn SAS
+ Protcol Layer (SPL)
+ specification for more information on this field.
+ .It Fl a Ar enable|disable
+ Enable or disable SATA slumber phy power conditions.
+ .It Fl A Ar enable|disable
+ Enable or disable SATA partial power conditions.
+ .It Fl s Ar enable|disable
+ Enable or disable SAS slumber phy power conditions.
+ .It Fl S Ar enable|disable
+ Enable or disable SAS partial phy power conditions.
+ .El
+ .It Ic smpphylist
+ List phys attached to a SAS expander, the address of the end device
+ attached to the phy, and the inquiry data for that device and peripheral
+ devices attached to that device.
+ The inquiry data and peripheral devices are displayed if available.
+ .Bl -tag -width 5n
+ .It Fl l
+ Turn on the long response format for the underlying SMP commands used for
+ this command.
+ .It Fl q
+ Only print out phys that are attached to a device in the CAM EDT (Existing
+ Device Table).
+ .El
+ .It Ic smpmaninfo
+ Send the SMP Report Manufacturer Information command to the device and
+ display the response.
+ .Bl -tag -width 5n
+ .It Fl l
+ Turn on the long response format for the underlying SMP commands used for
+ this command.
+ .El
  .It Ic debug
  Turn on CAM debugging printfs in the kernel.
  This requires options CAMDEBUG
*** src/sbin/camcontrol/camcontrol.c.orig
--- src/sbin/camcontrol/camcontrol.c
***************
*** 33,43 ****
--- 33,46 ----
  #include <sys/stdint.h>
  #include <sys/types.h>
  #include <sys/endian.h>
+ #include <sys/sbuf.h>
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
+ #include <inttypes.h>
+ #include <limits.h>
  #include <fcntl.h>
  #include <ctype.h>
  #include <err.h>
***************
*** 50,55 ****
--- 53,59 ----
  #include <cam/scsi/scsi_da.h>
  #include <cam/scsi/scsi_pass.h>
  #include <cam/scsi/scsi_message.h>
+ #include <cam/scsi/smp_all.h>
  #include <cam/ata/ata_all.h>
  #include <camlib.h>
  #include "camcontrol.h"
***************
*** 77,83 ****
  	CAM_CMD_IDENTIFY	= 0x00000013,
  	CAM_CMD_IDLE		= 0x00000014,
  	CAM_CMD_STANDBY		= 0x00000015,
! 	CAM_CMD_SLEEP		= 0x00000016
  } cam_cmdmask;
  
  typedef enum {
--- 81,92 ----
  	CAM_CMD_IDENTIFY	= 0x00000013,
  	CAM_CMD_IDLE		= 0x00000014,
  	CAM_CMD_STANDBY		= 0x00000015,
! 	CAM_CMD_SLEEP		= 0x00000016,
! 	CAM_CMD_SMP_CMD		= 0x00000017,
! 	CAM_CMD_SMP_RG		= 0x00000018,
! 	CAM_CMD_SMP_PC		= 0x00000019,
! 	CAM_CMD_SMP_PHYLIST	= 0x0000001a,
! 	CAM_CMD_SMP_MANINFO	= 0x0000001b
  } cam_cmdmask;
  
  typedef enum {
***************
*** 117,123 ****
  
  struct camcontrol_opts {
  	const char	*optname;
! 	cam_cmdmask	cmdnum;
  	cam_argmask	argnum;
  	const char	*subopt;
  };
--- 126,132 ----
  
  struct camcontrol_opts {
  	const char	*optname;
! 	uint32_t	cmdnum;
  	cam_argmask	argnum;
  	const char	*subopt;
  };
***************
*** 126,131 ****
--- 135,143 ----
  static const char scsicmd_opts[] = "a:c:dfi:o:r";
  static const char readdefect_opts[] = "f:GP";
  static const char negotiate_opts[] = "acD:M:O:qR:T:UW:";
+ static const char smprg_opts[] = "l";
+ static const char smppc_opts[] = "a:A:d:lm:M:o:p:s:S:T:";
+ static const char smpphylist_opts[] = "lq";
  #endif
  
  struct camcontrol_opts option_table[] = {
***************
*** 145,150 ****
--- 157,170 ----
  #ifndef MINIMALISTIC
  	{"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},
  	{"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},
+ 	{"smpcmd", CAM_CMD_SMP_CMD, CAM_ARG_NONE, "r:R:"},
+ 	{"smprg", CAM_CMD_SMP_RG, CAM_ARG_NONE, smprg_opts},
+ 	{"smpreportgeneral", CAM_CMD_SMP_RG, CAM_ARG_NONE, smprg_opts},
+ 	{"smppc", CAM_CMD_SMP_PC, CAM_ARG_NONE, smppc_opts},
+ 	{"smpphycontrol", CAM_CMD_SMP_PC, CAM_ARG_NONE, smppc_opts},
+ 	{"smpplist", CAM_CMD_SMP_PHYLIST, CAM_ARG_NONE, smpphylist_opts},
+ 	{"smpphylist", CAM_CMD_SMP_PHYLIST, CAM_ARG_NONE, smpphylist_opts},
+ 	{"smpmaninfo", CAM_CMD_SMP_MANINFO, CAM_ARG_NONE, "l"},
  	{"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},
  	{"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},
  #endif /* MINIMALISTIC */
***************
*** 173,183 ****
  	CC_OR_FOUND
  } camcontrol_optret;
  
  cam_cmdmask cmdlist;
  cam_argmask arglist;
  
! 
! camcontrol_optret getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,
  			    const char **subopt);
  #ifndef MINIMALISTIC
  static int getdevlist(struct cam_device *device);
--- 193,217 ----
  	CC_OR_FOUND
  } camcontrol_optret;
  
+ struct cam_devitem {
+ 	struct device_match_result dev_match;
+ 	int num_periphs;
+ 	struct periph_match_result *periph_matches;
+ 	struct scsi_vpd_device_id *device_id;
+ 	int device_id_len;
+ 	STAILQ_ENTRY(cam_devitem) links;
+ };
+ 
+ struct cam_devlist {
+ 	STAILQ_HEAD(, cam_devitem) dev_queue;
+ 	path_id_t path_id;
+ };
+ 
  cam_cmdmask cmdlist;
  cam_argmask arglist;
  
! camcontrol_optret getoption(struct camcontrol_opts *table, char *arg,
! 			    uint32_t *cmdnum, cam_argmask *argnum,
  			    const char **subopt);
  #ifndef MINIMALISTIC
  static int getdevlist(struct cam_device *device);
***************
*** 206,211 ****
--- 240,260 ----
  		     char *combinedopt, int retry_count, int timeout);
  static int scsicmd(struct cam_device *device, int argc, char **argv,
  		   char *combinedopt, int retry_count, int timeout);
+ static int smpcmd(struct cam_device *device, int argc, char **argv,
+ 		  char *combinedopt, int retry_count, int timeout);
+ static int smpreportgeneral(struct cam_device *device, int argc, char **argv,
+ 			    char *combinedopt, int retry_count, int timeout);
+ static int smpphycontrol(struct cam_device *device, int argc, char **argv,
+ 			 char *combinedopt, int retry_count, int timeout);
+ static int smpmaninfo(struct cam_device *device, int argc, char **argv,
+ 		      char *combinedopt, int retry_count, int timeout);
+ static int getdevid(struct cam_devitem *item);
+ static int buildbusdevlist(struct cam_devlist *devlist);
+ static void freebusdevlist(struct cam_devlist *devlist);
+ static struct cam_devitem *findsasdevice(struct cam_devlist *devlist,
+ 					 uint64_t sasaddr);
+ static int smpphylist(struct cam_device *device, int argc, char **argv,
+ 		      char *combinedopt, int retry_count, int timeout);
  static int tagcontrol(struct cam_device *device, int argc, char **argv,
  		      char *combinedopt);
  static void cts_print(struct cam_device *device,
***************
*** 234,246 ****
  #endif
  
  camcontrol_optret
! getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,
! 	  const char **subopt)
  {
  	struct camcontrol_opts *opts;
  	int num_matches = 0;
  
! 	for (opts = option_table; (opts != NULL) && (opts->optname != NULL);
  	     opts++) {
  		if (strncmp(opts->optname, arg, strlen(arg)) == 0) {
  			*cmdnum = opts->cmdnum;
--- 283,295 ----
  #endif
  
  camcontrol_optret
! getoption(struct camcontrol_opts *table, char *arg, uint32_t *cmdnum,
! 	  cam_argmask *argnum, const char **subopt)
  {
  	struct camcontrol_opts *opts;
  	int num_matches = 0;
  
! 	for (opts = table; (opts != NULL) && (opts->optname != NULL);
  	     opts++) {
  		if (strncmp(opts->optname, arg, strlen(arg)) == 0) {
  			*cmdnum = opts->cmdnum;
***************
*** 2455,2464 ****
  
  	if (((retval = cam_send_ccb(device, ccb)) < 0)
  	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
  		if (retval < 0)
! 			warn("error sending command");
  		else
! 			warnx("error sending command");
  
  		if (arglist & CAM_ARG_VERBOSE) {
  			cam_error_print(device, ccb, CAM_ESF_ALL,
--- 2504,2515 ----
  
  	if (((retval = cam_send_ccb(device, ccb)) < 0)
  	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ 		const char *warnstr = "error sending command";
+ 
  		if (retval < 0)
! 			warn(warnstr);
  		else
! 			warnx(warnstr);
  
  		if (arglist & CAM_ARG_VERBOSE) {
  			cam_error_print(device, ccb, CAM_ESF_ALL,
***************
*** 4274,4279 ****
--- 4325,5517 ----
  }
  
  static int
+ smpcmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
+        int retry_count, int timeout)
+ {
+ 	int c, error;
+ 	union ccb *ccb;
+ 	uint8_t *smp_request = NULL, *smp_response = NULL;
+ 	int request_size = 0, response_size = 0;
+ 	int fd_request = 0, fd_response = 0;
+ 	char *datastr = NULL;
+ 	struct get_hook hook;
+ 	int retval;
+ 	int flags = 0;
+ 
+ 	/*
+ 	 * Note that at the moment we don't support sending SMP CCBs to
+ 	 * devices that aren't probed by CAM.
+ 	 */
+ 	ccb = cam_getccb(device);
+ 	if (ccb == NULL) {
+ 		warnx("%s: error allocating CCB", __func__);
+ 		return (1);
+ 	}
+ 
+ 	bzero(&(&ccb->ccb_h)[1],
+ 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
+ 
+ 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ 		switch (c) {
+ 		case 'R':
+ 			arglist |= CAM_ARG_CMD_IN;
+ 			response_size = strtol(optarg, NULL, 0);
+ 			if (response_size <= 0) {
+ 				warnx("invalid number of response bytes %d",
+ 				      response_size);
+ 				error = 1;
+ 				goto smpcmd_bailout;
+ 			}
+ 			hook.argc = argc - optind;
+ 			hook.argv = argv + optind;
+ 			hook.got = 0;
+ 			optind++;
+ 			datastr = cget(&hook, NULL);
+ 			/*
+ 			 * If the user supplied "-" instead of a format, he
+ 			 * wants the data to be written to stdout.
+ 			 */
+ 			if ((datastr != NULL)
+ 			 && (datastr[0] == '-'))
+ 				fd_response = 1;
+ 
+ 			smp_response = (u_int8_t *)malloc(response_size);
+ 			if (smp_response == NULL) {
+ 				warn("can't malloc memory for SMP response");
+ 				error = 1;
+ 				goto smpcmd_bailout;
+ 			}
+ 			break;
+ 		case 'r':
+ 			arglist |= CAM_ARG_CMD_OUT;
+ 			request_size = strtol(optarg, NULL, 0);
+ 			if (request_size <= 0) {
+ 				warnx("invalid number of request bytes %d",
+ 				      request_size);
+ 				error = 1;
+ 				goto smpcmd_bailout;
+ 			}
+ 			hook.argc = argc - optind;
+ 			hook.argv = argv + optind;
+ 			hook.got = 0;
+ 			datastr = cget(&hook, NULL);
+ 			smp_request = (u_int8_t *)malloc(request_size);
+ 			if (smp_request == NULL) {
+ 				warn("can't malloc memory for SMP request");
+ 				error = 1;
+ 				goto smpcmd_bailout;
+ 			}
+ 			bzero(smp_request, request_size);
+ 			/*
+ 			 * If the user supplied "-" instead of a format, he
+ 			 * wants the data to be read from stdin.
+ 			 */
+ 			if ((datastr != NULL)
+ 			 && (datastr[0] == '-'))
+ 				fd_request = 1;
+ 			else
+ 				buff_encode_visit(smp_request, request_size,
+ 						  datastr,
+ 						  iget, &hook);
+ 			optind += hook.got;
+ 			break;
+ 		default:
+ 			break;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * If fd_data is set, and we're writing to the device, we need to
+ 	 * read the data the user wants written from stdin.
+ 	 */
+ 	if ((fd_request == 1) && (arglist & CAM_ARG_CMD_OUT)) {
+ 		ssize_t amt_read;
+ 		int amt_to_read = request_size;
+ 		u_int8_t *buf_ptr = smp_request;
+ 
+ 		for (amt_read = 0; amt_to_read > 0;
+ 		     amt_read = read(STDIN_FILENO, buf_ptr, amt_to_read)) {
+ 			if (amt_read == -1) {
+ 				warn("error reading data from stdin");
+ 				error = 1;
+ 				goto smpcmd_bailout;
+ 			}
+ 			amt_to_read -= amt_read;
+ 			buf_ptr += amt_read;
+ 		}
+ 	}
+ 
+ 	if (((arglist & CAM_ARG_CMD_IN) == 0)
+ 	 || ((arglist & CAM_ARG_CMD_OUT) == 0)) {
+ 		warnx("%s: need both the request (-r) and response (-R) "
+ 		      "arguments", __func__);
+ 		error = 1;
+ 		goto smpcmd_bailout;
+ 	}
+ 
+ 	flags |= CAM_DEV_QFRZDIS;
+ 
+ 	cam_fill_smpio(&ccb->smpio,
+ 		       /*retries*/ retry_count,
+ 		       /*cbfcnp*/ NULL,
+ 		       /*flags*/ flags,
+ 		       /*smp_request*/ smp_request,
+ 		       /*smp_request_len*/ request_size,
+ 		       /*smp_response*/ smp_response,
+ 		       /*smp_response_len*/ response_size,
+ 		       /*timeout*/ timeout ? timeout : 5000);
+ 
+ 	ccb->smpio.flags = SMP_FLAG_NONE;
+ 
+ 	if (((retval = cam_send_ccb(device, ccb)) < 0)
+ 	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ 		const char *warnstr = "error sending command";
+ 
+ 		if (retval < 0)
+ 			warn(warnstr);
+ 		else
+ 			warnx(warnstr);
+ 
+ 		if (arglist & CAM_ARG_VERBOSE) {
+ 			cam_error_print(device, ccb, CAM_ESF_ALL,
+ 					CAM_EPF_ALL, stderr);
+ 		}
+ 	}
+ 
+ 	if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+ 	 && (response_size > 0)) {
+ 		if (fd_response == 0) {
+ 			buff_decode_visit(smp_response, response_size,
+ 					  datastr, arg_put, NULL);
+ 			fprintf(stdout, "\n");
+ 		} else {
+ 			ssize_t amt_written;
+ 			int amt_to_write = response_size;
+ 			u_int8_t *buf_ptr = smp_response;
+ 
+ 			for (amt_written = 0; (amt_to_write > 0) &&
+ 			     (amt_written = write(STDOUT_FILENO, buf_ptr,
+ 						  amt_to_write)) > 0;){
+ 				amt_to_write -= amt_written;
+ 				buf_ptr += amt_written;
+ 			}
+ 			if (amt_written == -1) {
+ 				warn("error writing data to stdout");
+ 				error = 1;
+ 				goto smpcmd_bailout;
+ 			} else if ((amt_written == 0)
+ 				&& (amt_to_write > 0)) {
+ 				warnx("only wrote %u bytes out of %u",
+ 				      response_size - amt_to_write, 
+ 				      response_size);
+ 			}
+ 		}
+ 	}
+ smpcmd_bailout:
+ 	if (ccb != NULL)
+ 		cam_freeccb(ccb);
+ 
+ 	if (smp_request != NULL)
+ 		free(smp_request);
+ 
+ 	if (smp_response != NULL)
+ 		free(smp_response);
+ 
+ 	return (error);
+ }
+ 
+ static int
+ smpreportgeneral(struct cam_device *device, int argc, char **argv,
+ 		 char *combinedopt, int retry_count, int timeout)
+ {
+ 	union ccb *ccb;
+ 	struct smp_report_general_request *request = NULL;
+ 	struct smp_report_general_response *response = NULL;
+ 	struct sbuf *sb = NULL;
+ 	int error = 0;
+ 	int c, long_response = 0;
+ 	int retval;
+ 
+ 	/*
+ 	 * Note that at the moment we don't support sending SMP CCBs to
+ 	 * devices that aren't probed by CAM.
+ 	 */
+ 	ccb = cam_getccb(device);
+ 	if (ccb == NULL) {
+ 		warnx("%s: error allocating CCB", __func__);
+ 		return (1);
+ 	}
+ 
+ 	bzero(&(&ccb->ccb_h)[1],
+ 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
+ 
+ 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ 		switch (c) {
+ 		case 'l':
+ 			long_response = 1;
+ 			break;
+ 		default:
+ 			break;
+ 		}
+ 	}
+ 	request = malloc(sizeof(*request));
+ 	if (request == NULL) {
+ 		warn("%s: unable to allocate %zd bytes", __func__,
+ 		     sizeof(*request));
+ 		error = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	response = malloc(sizeof(*response));
+ 	if (response == NULL) {
+ 		warn("%s: unable to allocate %zd bytes", __func__,
+ 		     sizeof(*response));
+ 		error = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	smp_report_general(&ccb->smpio,
+ 			   retry_count,
+ 			   /*cbfcnp*/ NULL,
+ 			   request,
+ 			   /*request_len*/ sizeof(*request),
+ 			   (uint8_t *)response,
+ 			   /*response_len*/ sizeof(*response),
+ 			   /*long_response*/ long_response,
+ 			   timeout);
+ 
+ 	if (((retval = cam_send_ccb(device, ccb)) < 0)
+ 	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ 		const char *warnstr = "error sending command";
+ 
+ 		if (retval < 0)
+ 			warn(warnstr);
+ 		else
+ 			warnx(warnstr);
+ 
+ 		if (arglist & CAM_ARG_VERBOSE) {
+ 			cam_error_print(device, ccb, CAM_ESF_ALL,
+ 					CAM_EPF_ALL, stderr);
+ 		}
+ 		error = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	/*
+ 	 * XXX KDM detect and decode SMP errors here.
+ 	 */
+ 	sb = sbuf_new_auto();
+ 	if (sb == NULL) {
+ 		warnx("%s: error allocating sbuf", __func__);
+ 		goto bailout;
+ 	}
+ 
+ 	smp_report_general_sbuf(response, sizeof(*response), sb);
+ 
+ 	sbuf_finish(sb);
+ 
+ 	printf("%s", sbuf_data(sb));
+ 
+ bailout:
+ 	if (ccb != NULL)
+ 		cam_freeccb(ccb);
+ 
+ 	if (request != NULL)
+ 		free(request);
+ 
+ 	if (response != NULL)
+ 		free(response);
+ 
+ 	if (sb != NULL)
+ 		sbuf_delete(sb);
+ 
+ 	return (error);
+ }
+ 
+ struct camcontrol_opts phy_ops[] = {
+ 	{"nop", SMP_PC_PHY_OP_NOP, CAM_ARG_NONE, NULL},
+ 	{"linkreset", SMP_PC_PHY_OP_LINK_RESET, CAM_ARG_NONE, NULL},
+ 	{"hardreset", SMP_PC_PHY_OP_HARD_RESET, CAM_ARG_NONE, NULL},
+ 	{"disable", SMP_PC_PHY_OP_DISABLE, CAM_ARG_NONE, NULL},
+ 	{"clearerrlog", SMP_PC_PHY_OP_CLEAR_ERR_LOG, CAM_ARG_NONE, NULL},
+ 	{"clearaffiliation", SMP_PC_PHY_OP_CLEAR_AFFILIATON, CAM_ARG_NONE,NULL},
+ 	{"sataportsel", SMP_PC_PHY_OP_TRANS_SATA_PSS, CAM_ARG_NONE, NULL},
+ 	{"clearitnl", SMP_PC_PHY_OP_CLEAR_STP_ITN_LS, CAM_ARG_NONE, NULL},
+ 	{"setdevname", SMP_PC_PHY_OP_SET_ATT_DEV_NAME, CAM_ARG_NONE, NULL},
+ 	{NULL, 0, 0, NULL}
+ };
+ 
+ static int
+ smpphycontrol(struct cam_device *device, int argc, char **argv,
+ 	      char *combinedopt, int retry_count, int timeout)
+ {
+ 	union ccb *ccb;
+ 	struct smp_phy_control_request *request = NULL;
+ 	struct smp_phy_control_response *response = NULL;
+ 	int long_response = 0;
+ 	int retval = 0;
+ 	int phy = -1;
+ 	uint32_t phy_operation = SMP_PC_PHY_OP_NOP;
+ 	int phy_op_set = 0;
+ 	uint64_t attached_dev_name = 0;
+ 	int dev_name_set = 0;
+ 	uint32_t min_plr = 0, max_plr = 0;
+ 	uint32_t pp_timeout_val = 0;
+ 	int slumber_partial = 0;
+ 	int set_pp_timeout_val = 0;
+ 	int c;
+ 
+ 	/*
+ 	 * Note that at the moment we don't support sending SMP CCBs to
+ 	 * devices that aren't probed by CAM.
+ 	 */
+ 	ccb = cam_getccb(device);
+ 	if (ccb == NULL) {
+ 		warnx("%s: error allocating CCB", __func__);
+ 		return (1);
+ 	}
+ 
+ 	bzero(&(&ccb->ccb_h)[1],
+ 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
+ 
+ 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ 		switch (c) {
+ 		case 'a':
+ 		case 'A':
+ 		case 's':
+ 		case 'S': {
+ 			int enable = -1;
+ 
+ 			if (strcasecmp(optarg, "enable") == 0)
+ 				enable = 1;
+ 			else if (strcasecmp(optarg, "disable") == 0)
+ 				enable = 2;
+ 			else {
+ 				warnx("%s: Invalid argument %s", __func__,
+ 				      optarg);
+ 				retval = 1;
+ 				goto bailout;
+ 			}
+ 			switch (c) {
+ 			case 's':
+ 				slumber_partial |= enable <<
+ 						   SMP_PC_SAS_SLUMBER_SHIFT;
+ 				break;
+ 			case 'S':
+ 				slumber_partial |= enable <<
+ 						   SMP_PC_SAS_PARTIAL_SHIFT;
+ 				break;
+ 			case 'a':
+ 				slumber_partial |= enable <<
+ 						   SMP_PC_SATA_SLUMBER_SHIFT;
+ 				break;
+ 			case 'A':
+ 				slumber_partial |= enable <<
+ 						   SMP_PC_SATA_PARTIAL_SHIFT;
+ 				break;
+ 			default:
+ 				warnx("%s: programmer error", __func__);
+ 				retval = 1;
+ 				goto bailout;
+ 				break; /*NOTREACHED*/
+ 			}
+ 			break;
+ 		}
+ 		case 'd':
+ 			attached_dev_name = (uintmax_t)strtoumax(optarg,
+ 								 NULL,0);
+ 			dev_name_set = 1;
+ 			break;
+ 		case 'l':
+ 			long_response = 1;
+ 			break;
+ 		case 'm':
+ 			/*
+ 			 * We don't do extensive checking here, so this
+ 			 * will continue to work when new speeds come out.
+ 			 */
+ 			min_plr = strtoul(optarg, NULL, 0);
+ 			if ((min_plr == 0)
+ 			 || (min_plr > 0xf)) {
+ 				warnx("%s: invalid link rate %x",
+ 				      __func__, min_plr);
+ 				retval = 1;
+ 				goto bailout;
+ 			}
+ 			break;
+ 		case 'M':
+ 			/*
+ 			 * We don't do extensive checking here, so this
+ 			 * will continue to work when new speeds come out.
+ 			 */
+ 			max_plr = strtoul(optarg, NULL, 0);
+ 			if ((max_plr == 0)
+ 			 || (max_plr > 0xf)) {
+ 				warnx("%s: invalid link rate %x",
+ 				      __func__, max_plr);
+ 				retval = 1;
+ 				goto bailout;
+ 			}
+ 			break;
+ 		case 'o': {
+ 			camcontrol_optret optreturn;
+ 			cam_argmask argnums;
+ 			const char *subopt;
+ 
+ 			if (phy_op_set != 0) {
+ 				warnx("%s: only one phy operation argument "
+ 				      "(-o) allowed", __func__);
+ 				retval = 1;
+ 				goto bailout;
+ 			}
+ 
+ 			phy_op_set = 1;
+ 
+ 			/*
+ 			 * Allow the user to specify the phy operation
+ 			 * numerically, as well as with a name.  This will
+ 			 * future-proof it a bit, so options that are added
+ 			 * in future specs can be used.
+ 			 */
+ 			if (isdigit(optarg[0])) {
+ 				phy_operation = strtoul(optarg, NULL, 0);
+ 				if ((phy_operation == 0)
+ 				 || (phy_operation > 0xff)) {
+ 					warnx("%s: invalid phy operation %#x",
+ 					      __func__, phy_operation);
+ 					retval = 1;
+ 					goto bailout;
+ 				}
+ 				break;
+ 			}
+ 			optreturn = getoption(phy_ops, optarg, &phy_operation,
+ 					      &argnums, &subopt);
+ 
+ 			if (optreturn == CC_OR_AMBIGUOUS) {
+ 				warnx("%s: ambiguous option %s", __func__,
+ 				      optarg);
+ 				usage(0);
+ 				retval = 1;
+ 				goto bailout;
+ 			} else if (optreturn == CC_OR_NOT_FOUND) {
+ 				warnx("%s: option %s not found", __func__,
+ 				      optarg);
+ 				usage(0);
+ 				retval = 1;
+ 				goto bailout;
+ 			}
+ 			break;
+ 		}
+ 		case 'p':
+ 			phy = atoi(optarg);
+ 			break;
+ 		case 'T':
+ 			pp_timeout_val = strtoul(optarg, NULL, 0);
+ 			if (pp_timeout_val > 15) {
+ 				warnx("%s: invalid partial pathway timeout "
+ 				      "value %u, need a value less than 16",
+ 				      __func__, pp_timeout_val);
+ 				retval = 1;
+ 				goto bailout;
+ 			}
+ 			set_pp_timeout_val = 1;
+ 			break;
+ 		default:
+ 			break;
+ 		}
+ 	}
+ 
+ 	if (phy == -1) {
+ 		warnx("%s: a PHY (-p phy) argument is required",__func__);
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	if (((dev_name_set != 0)
+ 	  && (phy_operation != SMP_PC_PHY_OP_SET_ATT_DEV_NAME))
+ 	 || ((phy_operation == SMP_PC_PHY_OP_SET_ATT_DEV_NAME)
+ 	  && (dev_name_set == 0))) {
+ 		warnx("%s: -d name and -o setdevname arguments both "
+ 		      "required to set device name", __func__);
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	request = malloc(sizeof(*request));
+ 	if (request == NULL) {
+ 		warn("%s: unable to allocate %zd bytes", __func__,
+ 		     sizeof(*request));
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	response = malloc(sizeof(*response));
+ 	if (response == NULL) {
+ 		warn("%s: unable to allocate %zd bytes", __func__,
+ 		     sizeof(*request));
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	smp_phy_control(&ccb->smpio,
+ 			retry_count,
+ 			/*cbfcnp*/ NULL,
+ 			request,
+ 			sizeof(*request),
+ 			(uint8_t *)response,
+ 			sizeof(*response),
+ 			long_response,
+ 			/*expected_exp_change_count*/ 0,
+ 			phy,
+ 			phy_operation,
+ 			(set_pp_timeout_val != 0) ? 1 : 0,
+ 			attached_dev_name,
+ 			min_plr,
+ 			max_plr,
+ 			slumber_partial,
+ 			pp_timeout_val,
+ 			timeout);
+ 
+ 	if (((retval = cam_send_ccb(device, ccb)) < 0)
+ 	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ 		const char *warnstr = "error sending command";
+ 
+ 		if (retval < 0)
+ 			warn(warnstr);
+ 		else
+ 			warnx(warnstr);
+ 
+ 		if (arglist & CAM_ARG_VERBOSE) {
+ 			/*
+ 			 * Use CAM_EPF_NORMAL so we only get one line of
+ 			 * SMP command decoding.
+ 			 */
+ 			cam_error_print(device, ccb, CAM_ESF_ALL,
+ 					CAM_EPF_NORMAL, stderr);
+ 		}
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	/* XXX KDM print out something here for success? */
+ bailout:
+ 	if (ccb != NULL)
+ 		cam_freeccb(ccb);
+ 
+ 	if (request != NULL)
+ 		free(request);
+ 
+ 	if (response != NULL)
+ 		free(response);
+ 
+ 	return (retval);
+ }
+ 
+ static int
+ smpmaninfo(struct cam_device *device, int argc, char **argv,
+ 	   char *combinedopt, int retry_count, int timeout)
+ {
+ 	union ccb *ccb;
+ 	struct smp_report_manuf_info_request request;
+ 	struct smp_report_manuf_info_response response;
+ 	struct sbuf *sb = NULL;
+ 	int long_response = 0;
+ 	int retval = 0;
+ 	int c;
+ 
+ 	/*
+ 	 * Note that at the moment we don't support sending SMP CCBs to
+ 	 * devices that aren't probed by CAM.
+ 	 */
+ 	ccb = cam_getccb(device);
+ 	if (ccb == NULL) {
+ 		warnx("%s: error allocating CCB", __func__);
+ 		return (1);
+ 	}
+ 
+ 	bzero(&(&ccb->ccb_h)[1],
+ 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
+ 
+ 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ 		switch (c) {
+ 		case 'l':
+ 			long_response = 1;
+ 			break;
+ 		default:
+ 			break;
+ 		}
+ 	}
+ 	bzero(&request, sizeof(request));
+ 	bzero(&response, sizeof(response));
+ 
+ 	smp_report_manuf_info(&ccb->smpio,
+ 			      retry_count,
+ 			      /*cbfcnp*/ NULL,
+ 			      &request,
+ 			      sizeof(request),
+ 			      (uint8_t *)&response,
+ 			      sizeof(response),
+ 			      long_response,
+ 			      timeout);
+ 
+ 	if (((retval = cam_send_ccb(device, ccb)) < 0)
+ 	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ 		const char *warnstr = "error sending command";
+ 
+ 		if (retval < 0)
+ 			warn(warnstr);
+ 		else
+ 			warnx(warnstr);
+ 
+ 		if (arglist & CAM_ARG_VERBOSE) {
+ 			cam_error_print(device, ccb, CAM_ESF_ALL,
+ 					CAM_EPF_ALL, stderr);
+ 		}
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	sb = sbuf_new_auto();
+ 	if (sb == NULL) {
+ 		warnx("%s: error allocating sbuf", __func__);
+ 		goto bailout;
+ 	}
+ 
+ 	smp_report_manuf_info_sbuf(&response, sizeof(response), sb);
+ 
+ 	sbuf_finish(sb);
+ 
+ 	printf("%s", sbuf_data(sb));
+ 
+ bailout:
+ 
+ 	if (ccb != NULL)
+ 		cam_freeccb(ccb);
+ 
+ 	if (sb != NULL)
+ 		sbuf_delete(sb);
+ 
+ 	return (retval);
+ }
+ 
+ static int
+ getdevid(struct cam_devitem *item)
+ {
+ 	int retval = 0;
+ 	union ccb *ccb = NULL;
+ 
+ 	struct cam_device *dev;
+ 
+ 	dev = cam_open_btl(item->dev_match.path_id,
+ 			   item->dev_match.target_id,
+ 			   item->dev_match.target_lun, O_RDWR, NULL);
+ 
+ 	if (dev == NULL) {
+ 		warnx("%s", cam_errbuf);
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	item->device_id_len = CAM_SCSI_DEVID_MAXLEN;
+ 	item->device_id = malloc(item->device_id_len);
+ 	if (item->device_id == NULL) {
+ 		warn("%s: unable to allocate %d bytes", __func__,
+ 		     item->device_id_len);
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	ccb = cam_getccb(dev);
+ 	if (ccb == NULL) {
+ 		warnx("%s: error allocating CCB", __func__);
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	bzero(&(&ccb->ccb_h)[1],
+ 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
+ 	ccb->ccb_h.func_code = XPT_GDEV_ADVINFO;
+ 	ccb->ccb_h.flags = CAM_DIR_IN;
+ 	ccb->cgdai.flags = CGDAI_FLAG_PROTO;
+ 	ccb->cgdai.buftype = CGDAI_TYPE_SCSI_DEVID;
+ 	ccb->cgdai.bufsiz = item->device_id_len;
+ 	ccb->cgdai.buf = (uint8_t *)item->device_id;
+ 
+ 	if (cam_send_ccb(dev, ccb) < 0) {
+ 		warn("%s: error sending XPT_GDEV_ADVINFO CCB", __func__);
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ 		warnx("%s: CAM status %#x", __func__, ccb->ccb_h.status);
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ bailout:
+ 	if (dev != NULL)
+ 		cam_close_device(dev);
+ 
+ 	if (ccb != NULL)
+ 		cam_freeccb(ccb);
+ 
+ 	return (retval);
+ }
+ 
+ /*
+  * XXX KDM merge this code with getdevtree()?
+  */
+ static int
+ buildbusdevlist(struct cam_devlist *devlist)
+ {
+ 	union ccb ccb;
+ 	int bufsize, fd = -1;
+ 	struct dev_match_pattern *patterns;
+ 	struct cam_devitem *item = NULL;
+ 	int skip_device = 0;
+ 	int retval = 0;
+ 
+ 	if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
+ 		warn("couldn't open %s", XPT_DEVICE);
+ 		return(1);
+ 	}
+ 
+ 	bzero(&ccb, sizeof(union ccb));
+ 
+ 	ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
+ 	ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
+ 	ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
+ 
+ 	ccb.ccb_h.func_code = XPT_DEV_MATCH;
+ 	bufsize = sizeof(struct dev_match_result) * 100;
+ 	ccb.cdm.match_buf_len = bufsize;
+ 	ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
+ 	if (ccb.cdm.matches == NULL) {
+ 		warnx("can't malloc memory for matches");
+ 		close(fd);
+ 		return(1);
+ 	}
+ 	ccb.cdm.num_matches = 0;
+ 	ccb.cdm.num_patterns = 2;
+ 	ccb.cdm.pattern_buf_len = sizeof(struct dev_match_pattern) *
+ 		ccb.cdm.num_patterns;
+ 
+ 	patterns = (struct dev_match_pattern *)malloc(ccb.cdm.pattern_buf_len);
+ 	if (patterns == NULL) {
+ 		warnx("can't malloc memory for patterns");
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	ccb.cdm.patterns = patterns;
+ 	bzero(patterns, ccb.cdm.pattern_buf_len);
+ 
+ 	patterns[0].type = DEV_MATCH_DEVICE;
+ 	patterns[0].pattern.device_pattern.flags = DEV_MATCH_PATH;
+ 	patterns[0].pattern.device_pattern.path_id = devlist->path_id;
+ 	patterns[1].type = DEV_MATCH_PERIPH;
+ 	patterns[1].pattern.periph_pattern.flags = PERIPH_MATCH_PATH;
+ 	patterns[1].pattern.periph_pattern.path_id = devlist->path_id;
+ 
+ 	/*
+ 	 * We do the ioctl multiple times if necessary, in case there are
+ 	 * more than 100 nodes in the EDT.
+ 	 */
+ 	do {
+ 		unsigned int i;
+ 
+ 		if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+ 			warn("error sending CAMIOCOMMAND ioctl");
+ 			retval = 1;
+ 			goto bailout;
+ 		}
+ 
+ 		if ((ccb.ccb_h.status != CAM_REQ_CMP)
+ 		 || ((ccb.cdm.status != CAM_DEV_MATCH_LAST)
+ 		    && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
+ 			warnx("got CAM error %#x, CDM error %d\n",
+ 			      ccb.ccb_h.status, ccb.cdm.status);
+ 			retval = 1;
+ 			goto bailout;
+ 		}
+ 
+ 		for (i = 0; i < ccb.cdm.num_matches; i++) {
+ 			switch (ccb.cdm.matches[i].type) {
+ 			case DEV_MATCH_DEVICE: {
+ 				struct device_match_result *dev_result;
+ 
+ 				dev_result = 
+ 				     &ccb.cdm.matches[i].result.device_result;
+ 
+ 				if ((dev_result->flags
+ 				     & DEV_RESULT_UNCONFIGURED)
+ 				 && ((arglist & CAM_ARG_VERBOSE) == 0)) {
+ 					skip_device = 1;
+ 					break;
+ 				} else
+ 					skip_device = 0;
+ 
+ 				item = malloc(sizeof(*item));
+ 				if (item == NULL) {
+ 					warn("%s: unable to allocate %zd bytes",
+ 					     __func__, sizeof(*item));
+ 					retval = 1;
+ 					goto bailout;
+ 				}
+ 				bzero(item, sizeof(*item));
+ 				bcopy(dev_result, &item->dev_match,
+ 				      sizeof(*dev_result));
+ 				STAILQ_INSERT_TAIL(&devlist->dev_queue, item,
+ 						   links);
+ 
+ 				if (getdevid(item) != 0) {
+ 					retval = 1;
+ 					goto bailout;
+ 				}
+ 				break;
+ 			}
+ 			case DEV_MATCH_PERIPH: {
+ 				struct periph_match_result *periph_result;
+ 
+ 				periph_result =
+ 				      &ccb.cdm.matches[i].result.periph_result;
+ 
+ 				if (skip_device != 0)
+ 					break;
+ 				item->num_periphs++;
+ 				item->periph_matches = realloc(
+ 					item->periph_matches,
+ 					item->num_periphs *
+ 					sizeof(struct periph_match_result));
+ 				if (item->periph_matches == NULL) {
+ 					warn("%s: error allocating periph "
+ 					     "list", __func__);
+ 					retval = 1;
+ 					goto bailout;
+ 				}
+ 				bcopy(periph_result, &item->periph_matches[
+ 				      item->num_periphs - 1],
+ 				      sizeof(*periph_result));
+ 				break;
+ 			}
+ 			default:
+ 				fprintf(stderr, "%s: unexpected match "
+ 					"type %d\n", __func__,
+ 					ccb.cdm.matches[i].type);
+ 				retval = 1;
+ 				goto bailout;
+ 				break; /*NOTREACHED*/
+ 			}
+ 		}
+ 	} while ((ccb.ccb_h.status == CAM_REQ_CMP)
+ 		&& (ccb.cdm.status == CAM_DEV_MATCH_MORE));
+ bailout:
+ 
+ 	if (fd != -1)
+ 		close(fd);
+ 
+ 	free(patterns);
+ 
+ 	free(ccb.cdm.matches);
+ 
+ 	if (retval != 0)
+ 		freebusdevlist(devlist);
+ 
+ 	return (retval);
+ }
+ 
+ static void
+ freebusdevlist(struct cam_devlist *devlist)
+ {
+ 	struct cam_devitem *item, *item2;
+ 
+ 	STAILQ_FOREACH_SAFE(item, &devlist->dev_queue, links, item2) {
+ 		STAILQ_REMOVE(&devlist->dev_queue, item, cam_devitem,
+ 			      links);
+ 		free(item->device_id);
+ 		free(item->periph_matches);
+ 		free(item);
+ 	}
+ }
+ 
+ static struct cam_devitem *
+ findsasdevice(struct cam_devlist *devlist, uint64_t sasaddr)
+ {
+ 	struct cam_devitem *item;
+ 
+ 	STAILQ_FOREACH(item, &devlist->dev_queue, links) {
+ 		uint8_t *item_addr;
+ 
+ 		/*
+ 		 * XXX KDM look for LUN IDs as well?
+ 		 */
+ 		item_addr = scsi_get_sas_addr(item->device_id,
+ 					      item->device_id_len);
+ 		if (item_addr == NULL)
+ 			continue;
+ 
+ 		if (scsi_8btou64(item_addr) == sasaddr)
+ 			return (item);
+ 	}
+ 
+ 	return (NULL);
+ }
+ 
+ static int
+ smpphylist(struct cam_device *device, int argc, char **argv,
+ 	   char *combinedopt, int retry_count, int timeout)
+ {
+ 	struct smp_report_general_request *rgrequest = NULL;
+ 	struct smp_report_general_response *rgresponse = NULL;
+ 	struct smp_discover_request *disrequest = NULL;
+ 	struct smp_discover_response *disresponse = NULL;
+ 	struct cam_devlist devlist;
+ 	union ccb *ccb;
+ 	int long_response = 0;
+ 	int num_phys = 0;
+ 	int quiet = 0;
+ 	int retval;
+ 	int i, c;
+ 
+ 	/*
+ 	 * Note that at the moment we don't support sending SMP CCBs to
+ 	 * devices that aren't probed by CAM.
+ 	 */
+ 	ccb = cam_getccb(device);
+ 	if (ccb == NULL) {
+ 		warnx("%s: error allocating CCB", __func__);
+ 		return (1);
+ 	}
+ 
+ 	bzero(&(&ccb->ccb_h)[1],
+ 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
+ 
+ 	rgrequest = malloc(sizeof(*rgrequest));
+ 	if (rgrequest == NULL) {
+ 		warn("%s: unable to allocate %zd bytes", __func__,
+ 		     sizeof(*rgrequest));
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	rgresponse = malloc(sizeof(*rgresponse));
+ 	if (rgresponse == NULL) {
+ 		warn("%s: unable to allocate %zd bytes", __func__,
+ 		     sizeof(*rgresponse));
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ 		switch (c) {
+ 		case 'l':
+ 			long_response = 1;
+ 			break;
+ 		case 'q':
+ 			quiet = 1;
+ 			break;
+ 		default:
+ 			break;
+ 		}
+ 	}
+ 
+ 	smp_report_general(&ccb->smpio,
+ 			   retry_count,
+ 			   /*cbfcnp*/ NULL,
+ 			   rgrequest,
+ 			   /*request_len*/ sizeof(*rgrequest),
+ 			   (uint8_t *)rgresponse,
+ 			   /*response_len*/ sizeof(*rgresponse),
+ 			   /*long_response*/ long_response,
+ 			   timeout);
+ 
+ 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+ 
+ 	if (((retval = cam_send_ccb(device, ccb)) < 0)
+ 	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+ 		const char *warnstr = "error sending command";
+ 
+ 		if (retval < 0)
+ 			warn(warnstr);
+ 		else
+ 			warnx(warnstr);
+ 
+ 		if (arglist & CAM_ARG_VERBOSE) {
+ 			cam_error_print(device, ccb, CAM_ESF_ALL,
+ 					CAM_EPF_ALL, stderr);
+ 		}
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	num_phys = rgresponse->num_phys;
+ 
+ 	if (num_phys == 0) {
+ 		if (quiet == 0)
+ 			fprintf(stdout, "%s: No Phys reported\n", __func__);
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	STAILQ_INIT(&devlist.dev_queue);
+ 	devlist.path_id = device->path_id;
+ 
+ 	retval = buildbusdevlist(&devlist);
+ 	if (retval != 0)
+ 		goto bailout;
+ 
+ 	if (quiet == 0) {
+ 		fprintf(stdout, "%d PHYs:\n", num_phys);
+ 		fprintf(stdout, "PHY  Attached SAS Address\n");
+ 	}
+ 
+ 	disrequest = malloc(sizeof(*disrequest));
+ 	if (disrequest == NULL) {
+ 		warn("%s: unable to allocate %zd bytes", __func__,
+ 		     sizeof(*disrequest));
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	disresponse = malloc(sizeof(*disresponse));
+ 	if (disresponse == NULL) {
+ 		warn("%s: unable to allocate %zd bytes", __func__,
+ 		     sizeof(*disresponse));
+ 		retval = 1;
+ 		goto bailout;
+ 	}
+ 
+ 	for (i = 0; i < num_phys; i++) {
+ 		struct cam_devitem *item;
+ 		struct device_match_result *dev_match;
+ 		char vendor[16], product[48], revision[16];
+ 		char tmpstr[256];
+ 		int j;
+ 
+ 		bzero(&(&ccb->ccb_h)[1],
+ 		      sizeof(union ccb) - sizeof(struct ccb_hdr));
+ 
+ 		ccb->ccb_h.status = CAM_REQ_INPROG;
+ 		ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+ 
+ 		smp_discover(&ccb->smpio,
+ 			     retry_count,
+ 			     /*cbfcnp*/ NULL,
+ 			     disrequest,
+ 			     sizeof(*disrequest),
+ 			     (uint8_t *)disresponse,
+ 			     sizeof(*disresponse),
+ 			     long_response,
+ 			     /*ignore_zone_group*/ 0,
+ 			     /*phy*/ i,
+ 			     timeout);
+ 
+ 		if (((retval = cam_send_ccb(device, ccb)) < 0)
+ 		 || (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+ 		  && (disresponse->function_result != SMP_FR_PHY_VACANT))) {
+ 			const char *warnstr = "error sending command";
+ 
+ 			if (retval < 0)
+ 				warn(warnstr);
+ 			else
+ 				warnx(warnstr);
+ 
+ 			if (arglist & CAM_ARG_VERBOSE) {
+ 				cam_error_print(device, ccb, CAM_ESF_ALL,
+ 						CAM_EPF_ALL, stderr);
+ 			}
+ 			retval = 1;
+ 			goto bailout;
+ 		}
+ 
+ 		if (disresponse->function_result == SMP_FR_PHY_VACANT) {
+ 			if (quiet == 0)
+ 				fprintf(stdout, "%3d  <vacant>\n", i);
+ 			continue;
+ 		}
+ 
+ 		item = findsasdevice(&devlist,
+ 			scsi_8btou64(disresponse->attached_sas_address));
+ 
+ 		if ((quiet == 0)
+ 		 || (item != NULL)) {
+ 			fprintf(stdout, "%3d  0x%016jx", i,
+ 				(uintmax_t)scsi_8btou64(
+ 				disresponse->attached_sas_address));
+ 			if (item == NULL) {
+ 				fprintf(stdout, "\n");
+ 				continue;
+ 			}
+ 		} else if (quiet != 0)
+ 			continue;
+ 
+ 		dev_match = &item->dev_match;
+ 
+ 		if (dev_match->protocol == PROTO_SCSI) {
+ 			cam_strvis(vendor, dev_match->inq_data.vendor,
+ 				   sizeof(dev_match->inq_data.vendor),
+ 				   sizeof(vendor));
+ 			cam_strvis(product, dev_match->inq_data.product,
+ 				   sizeof(dev_match->inq_data.product),
+ 				   sizeof(product));
+ 			cam_strvis(revision, dev_match->inq_data.revision,
+ 				   sizeof(dev_match->inq_data.revision),
+ 				   sizeof(revision));
+ 			sprintf(tmpstr, "<%s %s %s>", vendor, product,
+ 				revision);
+ 		} else if ((dev_match->protocol == PROTO_ATA)
+ 			|| (dev_match->protocol == PROTO_SATAPM)) {
+ 			cam_strvis(product, dev_match->ident_data.model,
+ 				   sizeof(dev_match->ident_data.model),
+ 				   sizeof(product));
+ 			cam_strvis(revision, dev_match->ident_data.revision,
+ 				   sizeof(dev_match->ident_data.revision),
+ 				   sizeof(revision));
+ 			sprintf(tmpstr, "<%s %s>", product, revision);
+ 		} else {
+ 			sprintf(tmpstr, "<>");
+ 		}
+ 		fprintf(stdout, "   %-33s ", tmpstr);
+ 
+ 		/*
+ 		 * If we have 0 periphs, that's a bug...
+ 		 */
+ 		if (item->num_periphs == 0) {
+ 			fprintf(stdout, "\n");
+ 			continue;
+ 		}
+ 
+ 		fprintf(stdout, "(");
+ 		for (j = 0; j < item->num_periphs; j++) {
+ 			if (j > 0)
+ 				fprintf(stdout, ",");
+ 
+ 			fprintf(stdout, "%s%d",
+ 				item->periph_matches[j].periph_name,
+ 				item->periph_matches[j].unit_number);
+ 				
+ 		}
+ 		fprintf(stdout, ")\n");
+ 	}
+ bailout:
+ 	if (ccb != NULL)
+ 		cam_freeccb(ccb);
+ 
+ 	free(rgrequest);
+ 
+ 	free(rgresponse);
+ 
+ 	free(disrequest);
+ 
+ 	free(disresponse);
+ 
+ 	freebusdevlist(&devlist);
+ 
+ 	return (retval);
+ }
+ 
+ static int
  atapm(struct cam_device *device, int argc, char **argv,
  		 char *combinedopt, int retry_count, int timeout)
  {
***************
*** 4393,4398 ****
--- 5631,5646 ----
  "        camcontrol cmd        [dev_id][generic args]\n"
  "                              <-a cmd [args] | -c cmd [args]>\n"
  "                              [-d] [-f] [-i len fmt|-o len fmt [args]] [-r fmt]\n"
+ "        camcontrol smpcmd     [dev_id][generic args]\n"
+ "                              <-r len fmt [args]> <-R len fmt [args]>\n"
+ "        camcontrol smprg      [dev_id][generic args][-l]\n"
+ "        camcontrol smppc      [dev_id][generic args] <-p phy> [-l]\n"
+ "                              [-o operation][-d name][-m rate][-M rate]\n"
+ "                              [-T pp_timeout][-a enable|disable]\n"
+ "                              [-A enable|disable][-s enable|disable]\n"
+ "                              [-S enable|disable]\n"
+ "        camcontrol smpphylist [dev_id][generic args][-l][-q]\n"
+ "        camcontrol smpmaninfo [dev_id][generic args][-l]\n"
  "        camcontrol debug      [-I][-P][-T][-S][-X][-c]\n"
  "                              <all|bus[:target[:lun]]|off>\n"
  "        camcontrol tags       [dev_id][generic args] [-N tags] [-q] [-v]\n"
***************
*** 4426,4432 ****
  "reset       reset all busses, the given bus, or bus:target:lun\n"
  "defects     read the defect list of the specified device\n"
  "modepage    display or edit (-e) the given mode page\n"
! "cmd         send the given scsi command, may need -i or -o as well\n"
  "debug       turn debugging on/off for a bus, target, or lun, or all devices\n"
  "tags        report or set the number of transaction slots for a device\n"
  "negotiate   report or set device negotiation parameters\n"
--- 5674,5685 ----
  "reset       reset all busses, the given bus, or bus:target:lun\n"
  "defects     read the defect list of the specified device\n"
  "modepage    display or edit (-e) the given mode page\n"
! "cmd         send the given SCSI command, may need -i or -o as well\n"
! "smpcmd      send the given SMP command, requires -o and -i\n"
! "smprg       send the SMP Report General command\n"
! "smppc       send the SMP PHY Control command, requires -p\n"
! "smpphylist  display phys attached to a SAS expander\n"
! "smpmaninfo  send the SMP Report Manufacturer Info command\n"
  "debug       turn debugging on/off for a bus, target, or lun, or all devices\n"
  "tags        report or set the number of transaction slots for a device\n"
  "negotiate   report or set device negotiation parameters\n"
***************
*** 4476,4481 ****
--- 5729,5756 ----
  "-c cdb [args]     specify the SCSI CDB\n"
  "-i len fmt        specify input data and input data format\n"
  "-o len fmt [args] specify output data and output data fmt\n"
+ "smpcmd arguments:\n"
+ "-r len fmt [args] specify the SMP command to be sent\n"
+ "-R len fmt [args] specify SMP response format\n"
+ "smprg arguments:\n"
+ "-l                specify the long response format\n"
+ "smppc arguments:\n"
+ "-p phy            specify the PHY to operate on\n"
+ "-l                specify the long request/response format\n"
+ "-o operation      specify the phy control operation\n"
+ "-d name           set the attached device name\n"
+ "-m rate           set the minimum physical link rate\n"
+ "-M rate           set the maximum physical link rate\n"
+ "-T pp_timeout     set the partial pathway timeout value\n"
+ "-a enable|disable enable or disable SATA slumber\n"
+ "-A enable|disable enable or disable SATA partial phy power\n"
+ "-s enable|disable enable or disable SAS slumber\n"
+ "-S enable|disable enable or disable SAS partial phy power\n"
+ "smpphylist arguments:\n"
+ "-l                specify the long response format\n"
+ "-q                only print phys with attached devices\n"
+ "smpmaninfo arguments:\n"
+ "-l                specify the long response format\n"
  "debug arguments:\n"
  "-I                CAM_DEBUG_INFO -- scsi commands, errors, data\n"
  "-T                CAM_DEBUG_TRACE -- routine flow tracking\n"
***************
*** 4537,4543 ****
  	/*
  	 * Get the base option.
  	 */
! 	optreturn = getoption(argv[1], &cmdlist, &arglist, &subopt);
  
  	if (optreturn == CC_OR_AMBIGUOUS) {
  		warnx("ambiguous option %s", argv[1]);
--- 5812,5818 ----
  	/*
  	 * Get the base option.
  	 */
! 	optreturn = getoption(option_table,argv[1], &cmdlist, &arglist,&subopt);
  
  	if (optreturn == CC_OR_AMBIGUOUS) {
  		warnx("ambiguous option %s", argv[1]);
***************
*** 4767,4772 ****
--- 6042,6068 ----
  			error = scsicmd(cam_dev, argc, argv, combinedopt,
  					retry_count, timeout);
  			break;
+ 		case CAM_CMD_SMP_CMD:
+ 			error = smpcmd(cam_dev, argc, argv, combinedopt,
+ 				       retry_count, timeout);
+ 			break;
+ 		case CAM_CMD_SMP_RG:
+ 			error = smpreportgeneral(cam_dev, argc, argv,
+ 						 combinedopt, retry_count,
+ 						 timeout);
+ 			break;
+ 		case CAM_CMD_SMP_PC:
+ 			error = smpphycontrol(cam_dev, argc, argv, combinedopt, 
+ 					      retry_count, timeout);
+ 			break;
+ 		case CAM_CMD_SMP_PHYLIST:
+ 			error = smpphylist(cam_dev, argc, argv, combinedopt,
+ 					   retry_count, timeout);
+ 			break;
+ 		case CAM_CMD_SMP_MANINFO:
+ 			error = smpmaninfo(cam_dev, argc, argv, combinedopt,
+ 					   retry_count, timeout);
+ 			break;
  		case CAM_CMD_DEBUG:
  			error = camdebug(argc, argv, combinedopt);
  			break;
*** src/sys/cam/cam.c.orig
--- src/sys/cam/cam.c
***************
*** 37,48 ****
--- 37,50 ----
  #else /* _KERNEL */
  #include <stdlib.h>
  #include <stdio.h>
+ #include <string.h>
  #include <camlib.h>
  #endif /* _KERNEL */
  
  #include <cam/cam.h>
  #include <cam/cam_ccb.h>
  #include <cam/scsi/scsi_all.h>
+ #include <cam/scsi/smp_all.h>
  #include <sys/sbuf.h>
  
  #ifdef _KERNEL
***************
*** 83,88 ****
--- 85,92 ----
  	{ CAM_REQ_TOO_BIG,	 "The request was too large for this host"   },
  	{ CAM_REQUEUE_REQ,	 "Unconditionally Re-queue Request",	     },
  	{ CAM_ATA_STATUS_ERROR,	 "ATA Status Error"			     },
+ 	{ CAM_SCSI_IT_NEXUS_LOST,"Initiator/Target Nexus Lost"               },
+ 	{ 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"		     },
***************
*** 263,268 ****
--- 267,287 ----
  				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;
  	}
***************
*** 289,294 ****
--- 308,319 ----
  #endif /* _KERNEL/!_KERNEL */
  			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;
  		}
***************
*** 355,360 ****
--- 380,398 ----
  #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;
  		}
*** src/sys/cam/cam.h.orig
--- src/sys/cam/cam.h
***************
*** 147,152 ****
--- 147,153 ----
  				 */
  	CAM_ATA_STATUS_ERROR,	/* ATA error, look at error code in CCB */
  	CAM_SCSI_IT_NEXUS_LOST,	/* Initiator/Target Nexus lost. */
+ 	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 */
***************
*** 198,203 ****
--- 199,210 ----
  } 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;
+ 
+ typedef enum {
  	CAM_EAF_PRINT_NONE	= 0x00,
  	CAM_EAF_PRINT_STATUS	= 0x10,
  	CAM_EAF_PRINT_RESULT	= 0x20
*** src/sys/cam/cam_ccb.h.orig
--- src/sys/cam/cam_ccb.h
***************
*** 66,72 ****
  					      */
  	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_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)   */
--- 66,72 ----
  					      */
  	CAM_SCATTER_VALID	= 0x00000010,/* Scatter/gather list is valid  */
  	CAM_DIS_AUTOSENSE	= 0x00000020,/* Disable autosense feature     */
! 	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)   */
***************
*** 144,149 ****
--- 144,151 ----
  				/* Device statistics (error counts, etc.) */
  	XPT_FREEZE_QUEUE	= 0x0d,
  				/* Freeze device queue */
+ 	XPT_GDEV_ADVINFO	= 0x0e,
+ 				/* Advanced device information */
  /* SCSI Control Functions: 0x10->0x1F */
  	XPT_ABORT		= 0x10,
  				/* Abort the specified CCB */
***************
*** 185,190 ****
--- 187,195 ----
  				 * Set SIM specific knob values.
  				 */
  
+ 	XPT_SMP_IO		= 0x1b | XPT_FC_DEV_QUEUED,
+ 				/* Serial Management Protocol */
+ 
  	XPT_SCAN_TGT		= 0x1E | XPT_FC_QUEUED | XPT_FC_USER_CCB
  				       | XPT_FC_XPT_ONLY,
  				/* Scan Target */
***************
*** 608,613 ****
--- 613,644 ----
  	struct	timeval last_reset;	/* Time 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
***************
*** 1054,1059 ****
--- 1085,1110 ----
  #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
***************
*** 1087,1095 ****
--- 1138,1148 ----
  	struct	ccb_notify_acknowledge	cna2;
  	struct	ccb_eng_inq		cei;
  	struct	ccb_eng_exec		cee;
+ 	struct	ccb_smpio		smpio;
  	struct 	ccb_rescan		crcn;
  	struct  ccb_debug		cdbg;
  	struct	ccb_ataio		ataio;
+ 	struct	ccb_getdev_advinfo	cgdai;
  };
  
  __BEGIN_DECLS
***************
*** 1116,1121 ****
--- 1169,1181 ----
  	      u_int32_t timeout);
  
  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,
***************
*** 1172,1177 ****
--- 1232,1263 ----
  	ataio->tag_action = tag_action;
  }
  
+ 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
*** src/sys/cam/cam_periph.c.orig
--- src/sys/cam/cam_periph.c
***************
*** 648,653 ****
--- 648,668 ----
  		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;
+ 		dirs[0] = CAM_DIR_OUT;
+ 		data_ptrs[1] = &ccb->smpio.smp_response;
+ 		lengths[1] = ccb->smpio.smp_response_len;
+ 		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;
+ 		dirs[0] = CAM_DIR_IN;
+ 		numbufs = 1;
+ 		break;
  	default:
  		return(EINVAL);
  		break; /* NOTREACHED */
***************
*** 787,792 ****
--- 802,816 ----
  		data_ptrs[0] = &ccb->ataio.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 */
  		PRELE(curproc);
*** src/sys/cam/cam_xpt.c.orig
--- src/sys/cam/cam_xpt.c
***************
*** 2386,2391 ****
--- 2386,2392 ----
  		/* FALLTHROUGH */
  	case XPT_RESET_DEV:
  	case XPT_ENG_EXEC:
+ 	case XPT_SMP_IO:
  	{
  		struct cam_path *path = start_ccb->ccb_h.path;
  		int frozen;
*** src/sys/cam/cam_xpt.h.orig
--- src/sys/cam/cam_xpt.h
***************
*** 115,120 ****
--- 115,121 ----
  lun_id_t		xpt_path_lun_id(struct cam_path *path);
  struct cam_sim		*xpt_path_sim(struct cam_path *path);
  struct cam_periph	*xpt_path_periph(struct cam_path *path);
+ uint64_t		xpt_path_sas_addr(struct cam_path *path);
  void			xpt_async(u_int32_t async_code, struct cam_path *path,
  				  void *async_arg);
  void			xpt_rescan(union ccb *ccb);
*** src/sys/cam/cam_xpt_internal.h.orig
--- src/sys/cam/cam_xpt_internal.h
***************
*** 93,98 ****
--- 93,102 ----
  	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;
  	struct		 ata_params ident_data;
  	u_int8_t	 inq_flags;	/*
  					 * Current settings for inquiry flags.
*** src/sys/cam/scsi/scsi_all.c.orig
--- src/sys/cam/scsi/scsi_all.c
***************
*** 3552,3557 ****
--- 3552,3585 ----
  	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 *),
*** src/sys/cam/scsi/scsi_all.h.orig
--- src/sys/cam/scsi/scsi_all.h
***************
*** 796,808 ****
  {
  	u_int8_t device;
  	u_int8_t page_code;
! #define	SVPD_SUPPORTED_PAGE_LIST 0x00
  	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];
  };
  
  struct scsi_vpd_unit_serial_number
  {
  	u_int8_t device;
--- 796,824 ----
  {
  	u_int8_t device;
  	u_int8_t page_code;
! #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;
***************
*** 814,819 ****
--- 830,977 ----
  	u_int8_t serial_num[SVPD_SERIAL_NUM_SIZE];
  };
  
+ 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;
***************
*** 1164,1170 ****
  
  u_int		scsi_calc_syncsrate(u_int period_factor);
  u_int		scsi_calc_syncparam(u_int period);
! 	
  void		scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries,
  				     void (*cbfcnp)(struct cam_periph *, 
  						    union ccb *),
--- 1322,1329 ----
  
  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 *),
*** src/sys/cam/scsi/scsi_pass.c.orig
--- src/sys/cam/scsi/scsi_pass.c
***************
*** 524,531 ****
  	 * 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
! 	 * 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
  	 * without data are a reasonably common occurance (e.g. test unit
--- 524,531 ----
  	 * 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.
! 	 * cam_periph_mapmem() only supports SCSI, ATA, SMP and device
! 	 * match CCBs.  For the SCSI and ATA 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
  	 * without data are a reasonably common occurance (e.g. test unit
***************
*** 535,541 ****
  	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO ||
  	       ccb->ccb_h.func_code == XPT_ATA_IO)
  	    && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
! 	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) {
  
  		bzero(&mapinfo, sizeof(mapinfo));
  
--- 535,543 ----
  	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO ||
  	       ccb->ccb_h.func_code == XPT_ATA_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_SMP_IO)
! 	  || (ccb->ccb_h.func_code == XPT_GDEV_ADVINFO))) {
  
  		bzero(&mapinfo, sizeof(mapinfo));
  
*** src/sys/cam/scsi/scsi_xpt.c.orig
--- src/sys/cam/scsi/scsi_xpt.c
***************
*** 68,74 ****
  	struct scsi_inquiry_pattern inq_pat;
  	u_int8_t quirks;
  #define	CAM_QUIRK_NOLUNS	0x01
! #define	CAM_QUIRK_NOSERIAL	0x02
  #define	CAM_QUIRK_HILUNS	0x04
  #define	CAM_QUIRK_NOHILUNS	0x08
  #define	CAM_QUIRK_NORPTLUNS	0x10
--- 68,74 ----
  	struct scsi_inquiry_pattern inq_pat;
  	u_int8_t quirks;
  #define	CAM_QUIRK_NOLUNS	0x01
! #define	CAM_QUIRK_NOVPDS	0x02
  #define	CAM_QUIRK_HILUNS	0x04
  #define	CAM_QUIRK_NOHILUNS	0x08
  #define	CAM_QUIRK_NORPTLUNS	0x10
***************
*** 134,141 ****
  	PROBE_FULL_INQUIRY,
  	PROBE_REPORT_LUNS,
  	PROBE_MODE_SENSE,
! 	PROBE_SERIAL_NUM_0,
! 	PROBE_SERIAL_NUM_1,
  	PROBE_TUR_FOR_NEGOTIATION,
  	PROBE_INQUIRY_BASIC_DV1,
  	PROBE_INQUIRY_BASIC_DV2,
--- 134,142 ----
  	PROBE_FULL_INQUIRY,
  	PROBE_REPORT_LUNS,
  	PROBE_MODE_SENSE,
! 	PROBE_SUPPORTED_VPD_LIST,
! 	PROBE_DEVICE_ID,
! 	PROBE_SERIAL_NUM,
  	PROBE_TUR_FOR_NEGOTIATION,
  	PROBE_INQUIRY_BASIC_DV1,
  	PROBE_INQUIRY_BASIC_DV2,
***************
*** 149,156 ****
  	"PROBE_FULL_INQUIRY",
  	"PROBE_REPORT_LUNS",
  	"PROBE_MODE_SENSE",
! 	"PROBE_SERIAL_NUM_0",
! 	"PROBE_SERIAL_NUM_1",
  	"PROBE_TUR_FOR_NEGOTIATION",
  	"PROBE_INQUIRY_BASIC_DV1",
  	"PROBE_INQUIRY_BASIC_DV2",
--- 150,158 ----
  	"PROBE_FULL_INQUIRY",
  	"PROBE_REPORT_LUNS",
  	"PROBE_MODE_SENSE",
! 	"PROBE_SUPPORTED_VPD_LIST",
! 	"PROBE_DEVICE_ID",
! 	"PROBE_SERIAL_NUM",
  	"PROBE_TUR_FOR_NEGOTIATION",
  	"PROBE_INQUIRY_BASIC_DV1",
  	"PROBE_INQUIRY_BASIC_DV2",
***************
*** 463,469 ****
  			T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
  			" TDC 3600", "U07:"
  		},
! 		CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0
  	},
  	{
  		/*
--- 465,471 ----
  			T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
  			" TDC 3600", "U07:"
  		},
! 		CAM_QUIRK_NOVPDS, /*mintags*/0, /*maxtags*/0
  	},
  	{
  		/*
***************
*** 696,701 ****
--- 698,718 ----
  	xpt_schedule(periph, CAM_PRIORITY_XPT);
  }
  
+ 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)
  {
***************
*** 810,816 ****
  			if (INQ_DATA_TQ_ENABLED(inq_buf))
  				PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
  			else
! 				PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
  			goto again;
  		}
  		scsi_report_luns(csio, 5, probedone, MSG_SIMPLE_Q_TAG,
--- 827,834 ----
  			if (INQ_DATA_TQ_ENABLED(inq_buf))
  				PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
  			else
! 				PROBE_SET_ACTION(softc,
! 				    PROBE_SUPPORTED_VPD_LIST);
  			goto again;
  		}
  		scsi_report_luns(csio, 5, probedone, MSG_SIMPLE_Q_TAG,
***************
*** 843,861 ****
  		}
  		xpt_print(periph->path, "Unable to mode sense control page - "
  		    "malloc failure\n");
! 		PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
  	}
  	/* FALLTHROUGH */
! 	case PROBE_SERIAL_NUM_0:
  	{
! 		struct scsi_vpd_supported_page_list *vpd_list = NULL;
  		struct cam_ed *device;
  
  		device = periph->path->device;
! 		if ((SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOSERIAL) == 0) {
  			vpd_list = malloc(sizeof(*vpd_list), M_CAMXPT,
  			    M_NOWAIT | M_ZERO);
- 		}
  
  		if (vpd_list != NULL) {
  			scsi_inquiry(csio,
--- 861,880 ----
  		}
  		xpt_print(periph->path, "Unable to mode sense control page - "
  		    "malloc failure\n");
! 		PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST);
  	}
  	/* FALLTHROUGH */
! 	case PROBE_SUPPORTED_VPD_LIST:
  	{
! 		struct scsi_vpd_supported_page_list *vpd_list;
  		struct cam_ed *device;
  
+ 		vpd_list = NULL;
  		device = periph->path->device;
! 
! 		if ((SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOVPDS) == 0)
  			vpd_list = malloc(sizeof(*vpd_list), M_CAMXPT,
  			    M_NOWAIT | M_ZERO);
  
  		if (vpd_list != NULL) {
  			scsi_inquiry(csio,
***************
*** 878,884 ****
  		probedone(periph, start_ccb);
  		return;
  	}
! 	case PROBE_SERIAL_NUM_1:
  	{
  		struct scsi_vpd_unit_serial_number *serial_buf;
  		struct cam_ed* device;
--- 897,935 ----
  		probedone(periph, start_ccb);
  		return;
  	}
! 	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 = malloc(SVPD_DEVICE_ID_MAX_SIZE, M_CAMXPT,
! 			    M_NOWAIT | 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;
***************
*** 891,898 ****
  			device->serial_num_len = 0;
  		}
  
! 		serial_buf = (struct scsi_vpd_unit_serial_number *)
! 			malloc(sizeof(*serial_buf), M_CAMXPT, M_NOWAIT|M_ZERO);
  
  		if (serial_buf != NULL) {
  			scsi_inquiry(csio,
--- 942,951 ----
  			device->serial_num_len = 0;
  		}
  
! 		if (device_has_vpd(device, SVPD_UNIT_SERIAL_NUMBER))
! 			serial_buf = (struct scsi_vpd_unit_serial_number *)
! 				malloc(sizeof(*serial_buf), M_CAMXPT,
! 				    M_NOWAIT|M_ZERO);
  
  		if (serial_buf != NULL) {
  			scsi_inquiry(csio,
***************
*** 1046,1051 ****
--- 1099,1106 ----
  	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)
  {
***************
*** 1133,1139 ****
  					    PROBE_MODE_SENSE);
  				else
  					PROBE_SET_ACTION(softc,
! 					    PROBE_SERIAL_NUM_0);
  
  				if (path->device->flags & CAM_DEV_UNCONFIGURED) {
  					path->device->flags &= ~CAM_DEV_UNCONFIGURED;
--- 1188,1194 ----
  					    PROBE_MODE_SENSE);
  				else
  					PROBE_SET_ACTION(softc,
! 					    PROBE_SUPPORTED_VPD_LIST);
  
  				if (path->device->flags & CAM_DEV_UNCONFIGURED) {
  					path->device->flags &= ~CAM_DEV_UNCONFIGURED;
***************
*** 1290,1296 ****
  			if (INQ_DATA_TQ_ENABLED(inq_buf))
  				PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
  			else
! 				PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
  			xpt_release_ccb(done_ccb);
  			xpt_schedule(periph, priority);
  			return;
--- 1345,1352 ----
  			if (INQ_DATA_TQ_ENABLED(inq_buf))
  				PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
  			else
! 				PROBE_SET_ACTION(softc,
! 				    PROBE_SUPPORTED_VPD_LIST);
  			xpt_release_ccb(done_ccb);
  			xpt_schedule(periph, priority);
  			return;
***************
*** 1326,1361 ****
  		}
  		xpt_release_ccb(done_ccb);
  		free(mode_hdr, M_CAMXPT);
! 		PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
  		xpt_schedule(periph, priority);
  		return;
  	}
! 	case PROBE_SERIAL_NUM_0:
  	{
  		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 (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 (cam_periph_error(done_ccb, 0,
  					    SF_RETRY_UA|SF_NO_PRINT,
  					    &softc->saved_ccb) == ERESTART) {
--- 1382,1419 ----
  		}
  		xpt_release_ccb(done_ccb);
  		free(mode_hdr, M_CAMXPT);
! 		PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST);
  		xpt_schedule(periph, priority);
  		return;
  	}
! 	case PROBE_SUPPORTED_VPD_LIST:
  	{
  		struct ccb_scsiio *csio;
  		struct scsi_vpd_supported_page_list *page_list;
  
  		csio = &done_ccb->csio;
  		page_list =
  		    (struct scsi_vpd_supported_page_list *)csio->data_ptr;
  
+ 		if (path->device->supported_vpds != NULL) {
+ 			free(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 (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);
! 			PROBE_SET_ACTION(softc, 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) {
***************
*** 1366,1386 ****
  					 /*run_queue*/TRUE);
  		}
  
! 		if (page_list != NULL)
  			free(page_list, M_CAMXPT);
  
! 		if (serialnum_supported) {
! 			xpt_release_ccb(done_ccb);
! 			PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_1);
! 			xpt_schedule(periph, priority);
  			return;
  		}
  
! 		csio->data_ptr = NULL;
! 		/* FALLTHROUGH */
  	}
  
! 	case PROBE_SERIAL_NUM_1:
  	{
  		struct ccb_scsiio *csio;
  		struct scsi_vpd_unit_serial_number *serial_buf;
--- 1424,1485 ----
  					 /*run_queue*/TRUE);
  		}
  
! 		if (page_list)
  			free(page_list, M_CAMXPT);
+ 		/* 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;
  
! 		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;
! 			free(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)
! 			free(devid, M_CAMXPT);
! 		xpt_release_ccb(done_ccb);
! 		PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM);
! 		xpt_schedule(periph, priority);
! 		return;
  	}
  
! probe_device_check:
! 	case PROBE_SERIAL_NUM:
  	{
  		struct ccb_scsiio *csio;
  		struct scsi_vpd_unit_serial_number *serial_buf;
***************
*** 1395,1407 ****
  		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) {
- 			free(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
--- 1494,1499 ----
***************
*** 2227,2232 ****
--- 2319,2328 ----
  	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;
  
  	/*
  	 * XXX should be limited by number of CCBs this bus can
***************
*** 2337,2342 ****
--- 2433,2463 ----
  }
  
  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
  scsi_action(union ccb *start_ccb)
  {
  
***************
*** 2365,2370 ****
--- 2486,2496 ----
  		(*(sim->sim_action))(sim, start_ccb);
  		break;
  	}
+ 	case XPT_GDEV_ADVINFO:
+ 	{
+ 		scsi_getdev_advinfo(start_ccb);
+ 		break;
+ 	}
  	default:
  		xpt_action_default(start_ccb);
  		break;
*** /dev/null	Mon Nov 29 23:15:10 2010
--- //depot/vendor/FreeBSD/head/sys/cam/scsi/smp_all.c	Mon Nov 29 23:15:16 2010
***************
*** 0 ****
--- 1,620 ----
+ /*-
+  * 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 $
+  */
+ 
+ /*
+  * Serial Management Protocol helper functions.
+  */
+ 
+ #include <sys/cdefs.h>
+ __FBSDID("$FreeBSD$");
+ 
+ #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 <cam/cam.h>
+ #include <cam/cam_ccb.h>
+ #include <cam/cam_xpt.h>
+ #include <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;
+ }
+ 
==== <none> - //depot/users/kenm/FreeBSD-test/sys/cam/scsi/smp_all.c#4 ====
*** /dev/null	Mon Nov 29 23:15:10 2010
--- //depot/vendor/FreeBSD/head/sys/cam/scsi/smp_all.h	Mon Nov 29 23:15:16 2010
***************
*** 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$
+  */
+ 
+ /*
+  * 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*/
==== <none> - //depot/users/kenm/FreeBSD-test/sys/cam/scsi/smp_all.h#4 ====
*** src/sys/conf/files.orig
--- src/sys/conf/files
***************
*** 140,145 ****
--- 140,146 ----
  cam/scsi/scsi_sg.c		optional sg
  cam/scsi/scsi_targ_bh.c		optional targbh
  cam/scsi/scsi_target.c		optional targ
+ cam/scsi/smp_all.c		optional scbus
  contrib/altq/altq/altq_cbq.c	optional altq \
  	compile-with "${NORMAL_C} -I$S/contrib/pf"
  contrib/altq/altq/altq_cdnr.c	optional altq
*** src/sys/dev/mps/mps.c.orig
--- src/sys/dev/mps/mps.c
***************
*** 1569,1585 ****
  	sc = cm->cm_sc;
  
  	/*
! 	 * Set up DMA direction flags.  Note no support for
! 	 * bi-directional transactions.
  	 */
  	sflags = 0;
! 	if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
  		sflags |= MPI2_SGE_FLAGS_DIRECTION;
  		dir = BUS_DMASYNC_PREWRITE;
  	} else
  		dir = BUS_DMASYNC_PREREAD;
  
  	for (i = 0; i < nsegs; i++) {
  		error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len,
  		    sflags, nsegs - i);
  		if (error != 0) {
--- 1569,1621 ----
  	sc = cm->cm_sc;
  
  	/*
! 	 * In this case, just print out a warning and let the chip tell the
! 	 * user they did the wrong thing.
! 	 */
! 	if ((cm->cm_max_segs != 0) && (nsegs > cm->cm_max_segs)) {
! 		mps_printf(sc, "%s: warning: busdma returned %d segments, "
! 			   "more than the %d allowed\n", __func__, nsegs,
! 			   cm->cm_max_segs);
! 	}
! 
! 	/*
! 	 * Set up DMA direction flags.  Note that we don't support
! 	 * bi-directional transfers, with the exception of SMP passthrough.
  	 */
  	sflags = 0;
! 	if (cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) {
! 		/*
! 		 * We have to add a special case for SMP passthrough, there
! 		 * is no easy way to generically handle it.  The first
! 		 * S/G element is used for the command (therefore the
! 		 * direction bit needs to be set).  The second one is used
! 		 * for the reply.  We'll leave it to the caller to make
! 		 * sure we only have two buffers.
! 		 */
! 		/*
! 		 * Even though the busdma man page says it doesn't make
! 		 * sense to have both direction flags, it does in this case.
! 		 * We have one s/g element being accessed in each direction.
! 		 */
! 		dir = BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD;
! 
! 		/*
! 		 * Set the direction flag on the first buffer in the SMP
! 		 * passthrough request.  We'll clear it for the second one.
! 		 */
! 		sflags |= MPI2_SGE_FLAGS_DIRECTION |
! 			  MPI2_SGE_FLAGS_END_OF_BUFFER;
! 	} else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
  		sflags |= MPI2_SGE_FLAGS_DIRECTION;
  		dir = BUS_DMASYNC_PREWRITE;
  	} else
  		dir = BUS_DMASYNC_PREREAD;
  
  	for (i = 0; i < nsegs; i++) {
+ 		if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS)
+ 		 && (i != 0)) {
+ 			sflags &= ~MPI2_SGE_FLAGS_DIRECTION;
+ 		}
  		error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len,
  		    sflags, nsegs - i);
  		if (error != 0) {
***************
*** 1595,1600 ****
--- 1631,1643 ----
  	return;
  }
  
+ static void
+ mps_data_cb2(void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize,
+ 	     int error)
+ {
+ 	mps_data_cb(arg, segs, nsegs, error);
+ }
+ 
  /*
   * Note that the only error path here is from bus_dmamap_load(), which can
   * return EINPROGRESS if it is waiting for resources.
***************
*** 1605,1611 ****
  	MPI2_SGE_SIMPLE32 *sge;
  	int error = 0;
  
! 	if ((cm->cm_data != NULL) && (cm->cm_length != 0)) {
  		error = bus_dmamap_load(sc->buffer_dmat, cm->cm_dmamap,
  		    cm->cm_data, cm->cm_length, mps_data_cb, cm, 0);
  	} else {
--- 1648,1657 ----
  	MPI2_SGE_SIMPLE32 *sge;
  	int error = 0;
  
! 	if (cm->cm_flags & MPS_CM_FLAGS_USE_UIO) {
! 		error = bus_dmamap_load_uio(sc->buffer_dmat, cm->cm_dmamap,
! 		    &cm->cm_uio, mps_data_cb2, cm, 0);
! 	} else if ((cm->cm_data != NULL) && (cm->cm_length != 0)) {
  		error = bus_dmamap_load(sc->buffer_dmat, cm->cm_dmamap,
  		    cm->cm_data, cm->cm_length, mps_data_cb, cm, 0);
  	} else {
***************
*** 1619,1625 ****
  			    MPI2_SGE_FLAGS_SHIFT;
  			sge->Address = 0;
  		}
! 		mps_enqueue_request(sc, cm);	
  	}
  
  	return (error);
--- 1665,1671 ----
  			    MPI2_SGE_FLAGS_SHIFT;
  			sge->Address = 0;
  		}
! 		mps_enqueue_request(sc, cm);
  	}
  
  	return (error);
*** src/sys/dev/mps/mps_pci.c.orig
--- src/sys/dev/mps/mps_pci.c
***************
*** 38,43 ****
--- 38,44 ----
  #include <sys/conf.h>
  #include <sys/malloc.h>
  #include <sys/sysctl.h>
+ #include <sys/uio.h>
  
  #include <machine/bus.h>
  #include <machine/resource.h>
*** src/sys/dev/mps/mps_sas.c.orig
--- src/sys/dev/mps/mps_sas.c
***************
*** 41,46 ****
--- 41,48 ----
  #include <sys/malloc.h>
  #include <sys/uio.h>
  #include <sys/sysctl.h>
+ #include <sys/sglist.h>
+ #include <sys/endian.h>
  
  #include <machine/bus.h>
  #include <machine/resource.h>
***************
*** 55,60 ****
--- 57,65 ----
  #include <cam/cam_periph.h>
  #include <cam/scsi/scsi_all.h>
  #include <cam/scsi/scsi_message.h>
+ #if __FreeBSD_version >= 900026
+ #include <cam/scsi/smp_all.h>
+ #endif
  
  #include <dev/mps/mpi/mpi2_type.h>
  #include <dev/mps/mpi/mpi2.h>
***************
*** 69,77 ****
--- 74,84 ----
  	uint16_t	handle;
  	uint8_t		linkrate;
  	uint64_t	devname;
+ 	uint64_t	sasaddr;
  	uint32_t	devinfo;
  	uint16_t	encl_handle;
  	uint16_t	encl_slot;
+ 	uint16_t	parent_handle;
  	int		flags;
  #define MPSSAS_TARGET_INABORT	(1 << 0)
  #define MPSSAS_TARGET_INRESET	(1 << 1)
***************
*** 144,149 ****
--- 151,162 ----
  				      struct mps_command *cm, int free_cm);
  static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *);
  static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *);
+ #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(struct mpssas_softc *, struct mps_command *);
  static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
  static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *);
***************
*** 312,317 ****
--- 325,332 ----
  		probe->target.devinfo = buf->DeviceInfo;
  		probe->target.encl_handle = buf->EnclosureHandle;
  		probe->target.encl_slot = buf->Slot;
+ 		probe->target.sasaddr = mps_to_u64(&buf->SASAddress);
+ 		probe->target.parent_handle = buf->ParentDevHandle;
  
  		if (buf->DeviceInfo & MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH) {
  			params->page_address =
***************
*** 916,921 ****
--- 931,941 ----
  	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 /* __FreeBSD_version >= 900026 */
  	default:
  		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
  		break;
***************
*** 1361,1366 ****
--- 1381,1389 ----
  		bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len);
  	req->IoFlags = csio->cdb_len;
  
+ 	/*
+ 	 * XXX need to handle S/G lists and physical addresses here.
+ 	 */
  	cm->cm_data = csio->data_ptr;
  	cm->cm_length = csio->dxfer_len;
  	cm->cm_sge = &req->SGL;
***************
*** 1525,1530 ****
--- 1548,1876 ----
  	xpt_done(ccb);
  }
  
+ #if __FreeBSD_version >= 900026
+ static void
+ mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm)
+ {
+ 	MPI2_SMP_PASSTHROUGH_REPLY *rpl;
+ 	MPI2_SMP_PASSTHROUGH_REQUEST *req;
+ 	uint64_t sasaddr;
+ 	union ccb *ccb;
+ 
+ 	ccb = cm->cm_complete_data;
+ 	rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply;
+ 	if (rpl == NULL) {
+ 		mps_dprint(sc, MPS_INFO, "%s: NULL cm_reply!\n", __func__);
+ 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ 		goto bailout;
+ 	}
+ 
+ 	req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req;
+ 	sasaddr = le32toh(req->SASAddress.Low);
+ 	sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32;
+ 
+ 	if ((rpl->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS ||
+ 	    rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) {
+ 		mps_dprint(sc, MPS_INFO, "%s: IOCStatus %04x SASStatus %02x\n",
+ 		    __func__, rpl->IOCStatus, rpl->SASStatus);
+ 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ 		goto bailout;
+ 	}
+ 
+ 	mps_dprint(sc, MPS_INFO, "%s: SMP request to SAS address "
+ 		   "%#jx completed successfully\n", __func__,
+ 		   (uintmax_t)sasaddr);
+ 
+ 	if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED)
+ 		ccb->ccb_h.status = CAM_REQ_CMP;
+ 	else
+ 		ccb->ccb_h.status = CAM_SMP_STATUS_ERROR;
+ 
+ bailout:
+ 	/*
+ 	 * We sync in both directions because we had DMAs in the S/G list
+ 	 * in both directions.
+ 	 */
+ 	bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap,
+ 			BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ 	bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
+ 	mps_free_command(sc, cm);
+ 	xpt_done(ccb);
+ }
+ 
+ static void
+ mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, uint64_t sasaddr)
+ {
+ 	struct mps_command *cm;
+ 	uint8_t *request, *response;
+ 	MPI2_SMP_PASSTHROUGH_REQUEST *req;
+ 	struct mps_softc *sc;
+ 	struct sglist *sg;
+ 	int error;
+ 
+ 	sc = sassc->sc;
+ 	sg = NULL;
+ 	error = 0;
+ 
+ 	/*
+ 	 * XXX We don't yet support physical addresses here.
+ 	 */
+ 	if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) {
+ 		mps_printf(sc, "%s: physical addresses not supported\n",
+ 			   __func__);
+ 		ccb->ccb_h.status = CAM_REQ_INVALID;
+ 		xpt_done(ccb);
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * If the user wants to send an S/G list, check to make sure they
+ 	 * have single buffers.
+ 	 */
+ 	if (ccb->ccb_h.flags & CAM_SCATTER_VALID) {
+ 		/*
+ 		 * The chip does not support more than one buffer for the
+ 		 * request or response.
+ 		 */
+ 	 	if ((ccb->smpio.smp_request_sglist_cnt > 1)
+ 		  || (ccb->smpio.smp_response_sglist_cnt > 1)) {
+ 			mps_printf(sc, "%s: multiple request or response "
+ 				   "buffer segments not supported for SMP\n",
+ 				   __func__);
+ 			ccb->ccb_h.status = CAM_REQ_INVALID;
+ 			xpt_done(ccb);
+ 			return;
+ 		}
+ 
+ 		/*
+ 		 * The CAM_SCATTER_VALID flag was originally implemented
+ 		 * for the XPT_SCSI_IO CCB, which only has one data pointer.
+ 		 * We have two.  So, just take that flag to mean that we
+ 		 * might have S/G lists, and look at the S/G segment count
+ 		 * to figure out whether that is the case for each individual
+ 		 * buffer.
+ 		 */
+ 		if (ccb->smpio.smp_request_sglist_cnt != 0) {
+ 			bus_dma_segment_t *req_sg;
+ 
+ 			req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request;
+ 			request = (uint8_t *)req_sg[0].ds_addr;
+ 		} else
+ 			request = ccb->smpio.smp_request;
+ 
+ 		if (ccb->smpio.smp_response_sglist_cnt != 0) {
+ 			bus_dma_segment_t *rsp_sg;
+ 
+ 			rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response;
+ 			response = (uint8_t *)rsp_sg[0].ds_addr;
+ 		} else
+ 			response = ccb->smpio.smp_response;
+ 	} else {
+ 		request = ccb->smpio.smp_request;
+ 		response = ccb->smpio.smp_response;
+ 	}
+ 
+ 	cm = mps_alloc_command(sc);
+ 	if (cm == NULL) {
+ 		mps_printf(sc, "%s: cannot allocate command\n", __func__);
+ 		ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ 		xpt_done(ccb);
+ 		return;
+ 	}
+ 
+ 	req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req;
+ 	bzero(req, sizeof(*req));
+ 	req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ 
+ 	/* Allow the chip to use any route to this SAS address. */
+ 	req->PhysicalPort = 0xff;
+ 
+ 	req->RequestDataLength = ccb->smpio.smp_request_len;
+ 	req->SGLFlags = 
+ 	    MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI;
+ 
+ 	mps_dprint(sc, MPS_INFO, "%s: sending SMP request to SAS "
+ 		   "address %#jx\n", __func__, (uintmax_t)sasaddr);
+ 
+ 	mpi_init_sge(cm, req, &req->SGL);
+ 
+ 	/*
+ 	 * Set up a uio to pass into mps_map_command().  This allows us to
+ 	 * do one map command, and one busdma call in there.
+ 	 */
+ 	cm->cm_uio.uio_iov = cm->cm_iovec;
+ 	cm->cm_uio.uio_iovcnt = 2;
+ 	cm->cm_uio.uio_segflg = UIO_SYSSPACE;
+ 
+ 	/*
+ 	 * The read/write flag isn't used by busdma, but set it just in
+ 	 * case.  This isn't exactly accurate, either, since we're going in
+ 	 * both directions.
+ 	 */
+ 	cm->cm_uio.uio_rw = UIO_WRITE;
+ 
+ 	cm->cm_iovec[0].iov_base = request;
+ 	cm->cm_iovec[0].iov_len = req->RequestDataLength;
+ 	cm->cm_iovec[1].iov_base = response;
+ 	cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len;
+ 
+ 	cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len +
+ 			       cm->cm_iovec[1].iov_len;
+ 
+ 	/*
+ 	 * Trigger a warning message in mps_data_cb() for the user if we
+ 	 * wind up exceeding two S/G segments.  The chip expects one
+ 	 * segment for the request and another for the response.
+ 	 */
+ 	cm->cm_max_segs = 2;
+ 
+ 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ 	cm->cm_complete = mpssas_smpio_complete;
+ 	cm->cm_complete_data = ccb;
+ 
+ 	/*
+ 	 * Tell the mapping code that we're using a uio, and that this is
+ 	 * an SMP passthrough request.  There is a little special-case
+ 	 * logic there (in mps_data_cb()) to handle the bidirectional
+ 	 * transfer.  
+ 	 */
+ 	cm->cm_flags |= MPS_CM_FLAGS_USE_UIO | MPS_CM_FLAGS_SMP_PASS |
+ 			MPS_CM_FLAGS_DATAIN | MPS_CM_FLAGS_DATAOUT;
+ 
+ 	/* The chip data format is little endian. */
+ 	req->SASAddress.High = htole32(sasaddr >> 32);
+ 	req->SASAddress.Low = htole32(sasaddr);
+ 
+ 	/*
+ 	 * XXX Note that we don't have a timeout/abort mechanism here.
+ 	 * From the manual, it looks like task management requests only
+ 	 * work for SCSI IO and SATA passthrough requests.  We may need to
+ 	 * have a mechanism to retry requests in the event of a chip reset
+ 	 * at least.  Hopefully the chip will insure that any errors short
+ 	 * of that are relayed back to the driver.
+ 	 */
+ 	error = mps_map_command(sc, cm);
+ 	if ((error != 0) && (error != EINPROGRESS)) {
+ 		mps_printf(sc, "%s: error %d returned from mps_map_command()\n",
+ 			   __func__, error);
+ 		goto bailout_error;
+ 	}
+ 
+ 	return;
+ 
+ bailout_error:
+ 	mps_free_command(sc, cm);
+ 	ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ 	xpt_done(ccb);
+ 	return;
+ 
+ }
+ 
+ static void
+ mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb)
+ {
+ 	struct mps_softc *sc;
+ 	struct mpssas_target *targ;
+ 	uint64_t sasaddr = 0;
+ 
+ 	sc = sassc->sc;
+ 
+ 	/*
+ 	 * Make sure the target exists.
+ 	 */
+ 	targ = &sassc->targets[ccb->ccb_h.target_id];
+ 	if (targ->handle == 0x0) {
+ 		mps_printf(sc, "%s: target %d does not exist!\n", __func__,
+ 			   ccb->ccb_h.target_id);
+ 		ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+ 		xpt_done(ccb);
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * If this device has an embedded SMP target, we'll talk to it
+ 	 * directly.
+ 	 * figure out what the expander's address is.
+ 	 */
+ 	if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0)
+ 		sasaddr = targ->sasaddr;
+ 
+ 	/*
+ 	 * If we don't have a SAS address for the expander yet, try
+ 	 * grabbing it from the page 0x83 information cached in the
+ 	 * transport layer for this target.  LSI expanders report the
+ 	 * expander SAS address as the port-associated SAS address in
+ 	 * Inquiry VPD page 0x83.  Maxim expanders don't report it in page
+ 	 * 0x83.
+ 	 *
+ 	 * XXX KDM disable this for now, but leave it commented out so that
+ 	 * it is obvious that this is another possible way to get the SAS
+ 	 * address.
+ 	 *
+ 	 * The parent handle method below is a little more reliable, and
+ 	 * the other benefit is that it works for devices other than SES
+ 	 * devices.  So you can send a SMP request to a da(4) device and it
+ 	 * will get routed to the expander that device is attached to.
+ 	 * (Assuming the da(4) device doesn't contain an SMP target...)
+ 	 */
+ #if 0
+ 	if (sasaddr == 0)
+ 		sasaddr = xpt_path_sas_addr(ccb->ccb_h.path);
+ #endif
+ 
+ 	/*
+ 	 * If we still don't have a SAS address for the expander, look for
+ 	 * the parent device of this device, which is probably the expander.
+ 	 */
+ 	if (sasaddr == 0) {
+ 		struct mpssas_target *parent_target;
+ 
+ 		if (targ->parent_handle == 0x0) {
+ 			mps_printf(sc, "%s: handle %d does not have a valid "
+ 				   "parent handle!\n", __func__, targ->handle);
+ 			ccb->ccb_h.status = CAM_REQ_INVALID;
+ 			goto bailout;
+ 		}
+ 		parent_target = mpssas_find_target(sassc, 0,
+ 						   targ->parent_handle);
+ 
+ 		if (parent_target == NULL) {
+ 			mps_printf(sc, "%s: handle %d does not have a valid "
+ 				   "parent target!\n", __func__, targ->handle);
+ 			ccb->ccb_h.status = CAM_REQ_INVALID;
+ 			goto bailout;
+ 		}
+ 
+ 		if ((parent_target->devinfo &
+ 		     MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) {
+ 			mps_printf(sc, "%s: handle %d parent %d does not "
+ 				   "have an SMP target!\n", __func__,
+ 				   targ->handle, parent_target->handle);
+ 			ccb->ccb_h.status = CAM_REQ_INVALID;
+ 			goto bailout;
+ 
+ 		}
+ 
+ 		sasaddr = parent_target->sasaddr;
+ 	}
+ 
+ 	if (sasaddr == 0) {
+ 		mps_printf(sc, "%s: unable to find SAS address for handle %d\n",
+ 			   __func__, targ->handle);
+ 		ccb->ccb_h.status = CAM_REQ_INVALID;
+ 		goto bailout;
+ 	}
+ 	mpssas_send_smpcmd(sassc, ccb, sasaddr);
+ 
+ 	return;
+ 
+ bailout:
+ 	xpt_done(ccb);
+ 
+ }
+ 
+ #endif /* __FreeBSD_version >= 900026 */
+ 
  static void
  mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb)
  {
*** src/sys/dev/mps/mps_user.c.orig
--- src/sys/dev/mps/mps_user.c
***************
*** 322,328 ****
  	return (0);
  }
  
! static void
  mpi_init_sge(struct mps_command *cm, void *req, void *sge)
  {
  	int off, space;
--- 322,328 ----
  	return (0);
  }
  
! void
  mpi_init_sge(struct mps_command *cm, void *req, void *sge)
  {
  	int off, space;
*** src/sys/dev/mps/mpsvar.h.orig
--- src/sys/dev/mps/mpsvar.h
***************
*** 60,70 ****
--- 60,78 ----
  	uint32_t			chain_busaddr;
  };
  
+ /*
+  * This needs to be at least 2 to support SMP passthrough.
+  */
+ #define	MPS_IOVEC_COUNT	2
+ 
  struct mps_command {
  	TAILQ_ENTRY(mps_command)	cm_link;
  	struct mps_softc		*cm_sc;
  	void				*cm_data;
  	u_int				cm_length;
+ 	struct uio			cm_uio;
+ 	struct iovec			cm_iovec[MPS_IOVEC_COUNT];
+ 	u_int				cm_max_segs;
  	u_int				cm_sglsize;
  	MPI2_SGE_IO_UNION		*cm_sge;
  	uint8_t				*cm_req;
***************
*** 82,87 ****
--- 90,97 ----
  #define MPS_CM_FLAGS_DATAIN		(1 << 4)
  #define MPS_CM_FLAGS_WAKEUP		(1 << 5)
  #define MPS_CM_FLAGS_ACTIVE		(1 << 6)
+ #define MPS_CM_FLAGS_USE_UIO		(1 << 7)
+ #define MPS_CM_FLAGS_SMP_PASS		(1 << 8)
  	u_int				cm_state;
  #define MPS_CM_STATE_FREE		0
  #define MPS_CM_STATE_BUSY		1
***************
*** 238,249 ****
  {
  	struct mps_chain *chain, *chain_temp;
  
! 	if (cm->cm_reply != NULL)
  		mps_free_reply(sc, cm->cm_reply_data);
  	cm->cm_flags = 0;
  	cm->cm_complete = NULL;
  	cm->cm_complete_data = NULL;
  	cm->cm_targ = 0;
  	cm->cm_state = MPS_CM_STATE_FREE;
  	TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) {
  		TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link);
--- 248,262 ----
  {
  	struct mps_chain *chain, *chain_temp;
  
! 	if (cm->cm_reply != NULL) {
  		mps_free_reply(sc, cm->cm_reply_data);
+ 		cm->cm_reply = NULL;
+ 	}
  	cm->cm_flags = 0;
  	cm->cm_complete = NULL;
  	cm->cm_complete_data = NULL;
  	cm->cm_targ = 0;
+ 	cm->cm_max_segs = 0;
  	cm->cm_state = MPS_CM_STATE_FREE;
  	TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) {
  		TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link);
***************
*** 368,373 ****
--- 381,387 ----
  int mps_read_config_page(struct mps_softc *, struct mps_config_params *);
  int mps_write_config_page(struct mps_softc *, struct mps_config_params *);
  void mps_memaddr_cb(void *, bus_dma_segment_t *, int , int );
+ void mpi_init_sge(struct mps_command *cm, void *req, void *sge);
  int mps_attach_user(struct mps_softc *);
  void mps_detach_user(struct mps_softc *);
  
*** src/sys/sys/param.h.orig
--- src/sys/sys/param.h
***************
*** 58,64 ****
   *		in the range 5 to 9.
   */
  #undef __FreeBSD_version
! #define __FreeBSD_version 900025	/* Master, propagated to newvers */
  
  #ifdef _KERNEL
  #define	P_OSREL_SIGSEGV		700004
--- 58,64 ----
   *		in the range 5 to 9.
   */
  #undef __FreeBSD_version
! #define __FreeBSD_version 900026	/* Master, propagated to newvers */
  
  #ifdef _KERNEL
  #define	P_OSREL_SIGSEGV		700004


More information about the freebsd-scsi mailing list