svn commit: r216088 - in head: lib/libcam sbin/camcontrol sys/cam sys/cam/scsi sys/conf sys/dev/mps sys/sys

Kenneth D. Merry ken at FreeBSD.org
Tue Nov 30 22:39:46 UTC 2010


Author: ken
Date: Tue Nov 30 22:39:46 2010
New Revision: 216088
URL: http://svn.freebsd.org/changeset/base/216088

Log:
  Add Serial Management Protocol (SMP) passthrough support to CAM.
  
  This includes support in the kernel, camcontrol(8), libcam and the mps(4)
  driver for SMP passthrough.
  
  The CAM SCSI probe code has been modified to fetch Inquiry VPD page 0x00
  to determine supported pages, and will now fetch page 0x83 in addition to
  page 0x80 if supported.
  
  Add two new CAM CCBs, XPT_SMP_IO, and XPT_GDEV_ADVINFO.  The SMP CCB is
  intended for SMP requests and responses.  The ADVINFO is currently used to
  fetch cached VPD page 0x83 data from the transport layer, but is intended
  to be extensible to fetch other types of device-specific data.
  
  SMP-only devices are not currently represented in the CAM topology, and so
  the current semantics are that the SIM will route SMP CCBs to either the
  addressed device, if it contains an SMP target, or its parent, if it
  contains an SMP target.  (This is noted in cam_ccb.h, since it will change
  later once we have the ability to have SMP-only devices in CAM's topology.)
  
  smp_all.c,
  smp_all.h:		New helper routines for SMP.  This includes
  			SMP request building routines, response parsing
  			routines, error decoding routines, and structure
  			definitions for a number of SMP commands.
  
  libcam/Makefile:	Add smp_all.c to libcam, so that SMP functionality
  			is available to userland applications.
  
  camcontrol.8,
  camcontrol.c:		Add smp passthrough support to camcontrol.  Several
  			new subcommands are now available:
  
  			'smpcmd' functions much like 'cmd', except that it
  			allows the user to send generic SMP commands.
  
  			'smprg' sends the SMP report general command, and
  			displays the decoded output.  It will automatically
  			fetch extended output if it is available.
  
  			'smppc' sends the SMP phy control command, with any
  			number of potential options.  Among other things,
  			this allows the user to reset a phy on a SAS
  			expander, or disable a phy on an expander.
  
  			'smpmaninfo' sends the SMP report manufacturer
  			information and displays the decoded output.
  
  			'smpphylist' displays a list of phys on an
  			expander, and the CAM devices attached to those
  			phys, if any.
  
  cam.h,
  cam.c:			Add a status value for SMP errors
  			(CAM_SMP_STATUS_ERROR).
  
  			Add a missing description for CAM_SCSI_IT_NEXUS_LOST.
  
  			Add support for SMP commands to cam_error_string().
  
  cam_ccb.h:		Rename the CAM_DIR_RESV flag to CAM_DIR_BOTH.  SMP
  			commands are by nature bi-directional, and we may
  			need to support bi-directional SCSI commands later.
  
  			Add the XPT_SMP_IO CCB.  Since SMP commands are
  			bi-directional, there are pointers for both the
  			request and response.
  
  			Add a fill routine for SMP CCBs.
  
  			Add the XPT_GDEV_ADVINFO CCB.  This is currently
  			used to fetch cached page 0x83 data from the
  			transport later, but is extensible to fetch many
  			other types of data.
  
  cam_periph.c:		Add support in cam_periph_mapmem() for XPT_SMP_IO
  			and XPT_GDEV_ADVINFO CCBs.
  
  cam_xpt.c:		Add support for executing XPT_SMP_IO CCBs.
  
  cam_xpt_internal.h:	Add fields for VPD pages 0x00 and 0x83 in struct
  			cam_ed.
  
  scsi_all.c:		Add scsi_get_sas_addr(), a function that parses
  			VPD page 0x83 data and pulls out a SAS address.
  
  scsi_all.h:		Add VPD page 0x00 and 0x83 structures, and a
  			prototype for scsi_get_sas_addr().
  
  scsi_pass.c:		Add support for mapping buffers in XPT_SMP_IO and
  			XPT_GDEV_ADVINFO CCBs.
  
  scsi_xpt.c:		In the SCSI probe code, first ask the device for
  			VPD page 0x00.  If any VPD pages are supported,
  			that page is required to be implemented.  Based on
  			the response, we may probe for the serial number
  			(page 0x80) or device id (page 0x83).
  
  			Add support for the XPT_GDEV_ADVINFO CCB.
  
  sys/conf/files:		Add smp_all.c.
  
  mps.c:			Add support for passing in a uio in mps_map_command(),
  			so we can map a S/G list at once.
  
  			Add support for SMP passthrough commands in
  			mps_data_cb().  SMP is a special case, because the
  			first buffer in the S/G list is outbound and the
  			second buffer is inbound.
  
  			Add support for warning the user if the busdma code
  			comes back with more buffers than will work for the
  			command.  This will, for example, help the user
  			determine why an SMP command failed if busdma comes
  			back with three buffers.
  
  mps_pci.c:		Add sys/uio.h.
  
  mps_sas.c:		Add the SAS address and the parent handle to the
  			list of fields we pull from device page 0 and cache
  			in struct mpssas_target.  These are needed for SMP
  			passthrough.
  
  			Add support for the XPT_SMP_IO CCB.  For now, this
  			CCB is routed to the addressed device if it supports
  			SMP, or to its parent if it does not and the parent
  			does.  This is necessary because CAM does not
  			currently support SMP-only nodes in the topology.
  
  			Make SMP passthrough support conditional on
  			__FreeBSD_version >= 900026.  This will make it
  			easier to MFC this change to the driver without
  			MFCing the CAM changes as well.
  
  mps_user.c:		Un-staticize mpi_init_sge() so we can use it for
  			the SMP passthrough code.
  
  mpsvar.h:		Add a uio and iovecs into struct mps_command for
  			SMP passthrough commands.
  
  			Add a cm_max_segs field to struct mps_command so
  			that we can warn the user if busdma comes back with
  			too many segments.
  
  			Clear the cm_reply when a command gets freed.  If
  			it is not cleared, reply frames will eventually get
  			freed into the pool multiple times and corrupt the
  			pool.  (This fix is from scottl.)
  
  			Add a prototype for mpi_init_sge().
  
  sys/param.h:		Bump __FreeBSD_version to 900026 for the for the
  			inclusion of the XPT_GDEV_ADVINFO and XPT_SMP_IO
  			CAM CCBs.

Added:
  head/sys/cam/scsi/smp_all.c   (contents, props changed)
  head/sys/cam/scsi/smp_all.h   (contents, props changed)
Modified:
  head/lib/libcam/Makefile
  head/sbin/camcontrol/camcontrol.8
  head/sbin/camcontrol/camcontrol.c
  head/sys/cam/cam.c
  head/sys/cam/cam.h
  head/sys/cam/cam_ccb.h
  head/sys/cam/cam_periph.c
  head/sys/cam/cam_xpt.c
  head/sys/cam/cam_xpt_internal.h
  head/sys/cam/scsi/scsi_all.c
  head/sys/cam/scsi/scsi_all.h
  head/sys/cam/scsi/scsi_pass.c
  head/sys/cam/scsi/scsi_xpt.c
  head/sys/conf/files
  head/sys/dev/mps/mps.c
  head/sys/dev/mps/mps_pci.c
  head/sys/dev/mps/mps_sas.c
  head/sys/dev/mps/mps_user.c
  head/sys/dev/mps/mpsvar.h
  head/sys/sys/param.h

Modified: head/lib/libcam/Makefile
==============================================================================
--- head/lib/libcam/Makefile	Tue Nov 30 22:25:44 2010	(r216087)
+++ head/lib/libcam/Makefile	Tue Nov 30 22:39:46 2010	(r216088)
@@ -3,7 +3,7 @@
 LIB=		cam
 SHLIBDIR?=	/lib
 SRCS=		camlib.c scsi_cmdparse.c scsi_all.c scsi_da.c scsi_sa.c cam.c \
-		ata_all.c
+		ata_all.c smp_all.c
 INCS=		camlib.h
 
 DPADD=		${LIBSBUF}

Modified: head/sbin/camcontrol/camcontrol.8
==============================================================================
--- head/sbin/camcontrol/camcontrol.8	Tue Nov 30 22:25:44 2010	(r216087)
+++ head/sbin/camcontrol/camcontrol.8	Tue Nov 30 22:39:46 2010	(r216088)
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd July 1, 2010
+.Dd November 30, 2010
 .Dt CAMCONTROL 8
 .Os
 .Sh NAME
@@ -131,6 +131,43 @@
 .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,6 +591,177 @@ If the format is
 .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.
+If the SMP target supports the long response format, the additional data
+will be requested and displayed automatically.
+.Bl -tag -width 8n
+.It Fl l
+Request the long response format only.
+Not all SMP targets support the long response format.
+This option causes
+.Nm
+to skip sending the initial report general request without the long bit set
+and only issue a report general request with the long bit set.
+.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
@@ -965,6 +1173,14 @@ camcontrol negotiate -n da -u 3 -R 20.00
 Negotiate a sync rate of 20MHz and an offset of 15 with da3.
 Then send a
 Test Unit Ready command to make the settings take effect.
+.Pp
+.Bd -literal -offset indent
+camcontrol smpcmd ses0 -v -r 4 "40 0 00 0" -R 1020 "s9 i1"
+.Ed
+.Pp
+Send the SMP REPORT GENERAL command to ses0, and display the number of PHYs
+it contains.
+Display SMP errors if the command fails.
 .Sh SEE ALSO
 .Xr cam 3 ,
 .Xr cam_cdbparse 3 ,

Modified: head/sbin/camcontrol/camcontrol.c
==============================================================================
--- head/sbin/camcontrol/camcontrol.c	Tue Nov 30 22:25:44 2010	(r216087)
+++ head/sbin/camcontrol/camcontrol.c	Tue Nov 30 22:39:46 2010	(r216088)
@@ -33,11 +33,14 @@ __FBSDID("$FreeBSD$");
 #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,6 +53,7 @@ __FBSDID("$FreeBSD$");
 #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,7 +81,12 @@ typedef enum {
 	CAM_CMD_IDENTIFY	= 0x00000013,
 	CAM_CMD_IDLE		= 0x00000014,
 	CAM_CMD_STANDBY		= 0x00000015,
-	CAM_CMD_SLEEP		= 0x00000016
+	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,7 +126,7 @@ typedef enum {
 
 struct camcontrol_opts {
 	const char	*optname;
-	cam_cmdmask	cmdnum;
+	uint32_t	cmdnum;
 	cam_argmask	argnum;
 	const char	*subopt;
 };
@@ -126,6 +135,9 @@ struct camcontrol_opts {
 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,6 +157,14 @@ struct camcontrol_opts option_table[] = 
 #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,11 +193,25 @@ typedef enum {
 	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(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,
+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,6 +240,21 @@ static void modepage(struct cam_device *
 		     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,13 +283,13 @@ static int atapm(struct cam_device *devi
 #endif
 
 camcontrol_optret
-getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,
-	  const char **subopt)
+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 = option_table; (opts != NULL) && (opts->optname != NULL);
+	for (opts = table; (opts != NULL) && (opts->optname != NULL);
 	     opts++) {
 		if (strncmp(opts->optname, arg, strlen(arg)) == 0) {
 			*cmdnum = opts->cmdnum;
@@ -2455,10 +2504,12 @@ scsicmd(struct cam_device *device, int a
 
 	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("error sending command");
+			warn(warnstr);
 		else
-			warnx("error sending command");
+			warnx(warnstr);
 
 		if (arglist & CAM_ARG_VERBOSE) {
 			cam_error_print(device, ccb, CAM_ESF_ALL,
@@ -4274,125 +4325,1336 @@ bailout:
 }
 
 static int
-atapm(struct cam_device *device, int argc, char **argv,
-		 char *combinedopt, int retry_count, int timeout)
+smpcmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
+       int retry_count, int timeout)
 {
+	int c, error;
 	union ccb *ccb;
-	int retval = 0;
-	int t = -1;
-	int c;
-	u_char cmd, sc;
+	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__);
+		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 't':
-			t = atoi(optarg);
+		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 (strcmp(argv[1], "idle") == 0) {
-		if (t == -1)
-			cmd = ATA_IDLE_IMMEDIATE;
-		else
-			cmd = ATA_IDLE_CMD;
-	} else if (strcmp(argv[1], "standby") == 0) {
-		if (t == -1)
-			cmd = ATA_STANDBY_IMMEDIATE;
-		else
-			cmd = ATA_STANDBY_CMD;
-	} else {
-		cmd = ATA_SLEEP;
-		t = -1;
+
+	/*
+	 * 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 (t < 0)
-		sc = 0;
-	else if (t <= (240 * 5))
-		sc = (t + 4) / 5;
-	else if (t <= (252 * 5))
-		/* special encoding for 21 minutes */
-		sc = 252;
-	else if (t <= (11 * 30 * 60))
-		sc = (t - 1) / (30 * 60) + 241;
-	else
-		sc = 253;
+	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;
+	}
 
-	cam_fill_ataio(&ccb->ataio,
-		      retry_count,
-		      NULL,
-		      /*flags*/CAM_DIR_NONE,
-		      MSG_SIMPLE_Q_TAG,
-		      /*data_ptr*/NULL,
-		      /*dxfer_len*/0,
-		      timeout ? timeout : 30 * 1000);
-	ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, sc);
+	flags |= CAM_DEV_QFRZDIS;
 
-	/* Disable freezing the device queue */
-	ccb->ccb_h.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);
 
-	if (arglist & CAM_ARG_ERR_RECOVER)
-		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+	ccb->smpio.flags = SMP_FLAG_NONE;
 
-	if (cam_send_ccb(device, ccb) < 0) {
-		warn("error sending command");
+	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 (arglist & CAM_ARG_VERBOSE)
+		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;
+	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;
 	}
 
-	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
-		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
-		retval = 1;
+	response = malloc(sizeof(*response));
+	if (response == NULL) {
+		warn("%s: unable to allocate %zd bytes", __func__,
+		     sizeof(*response));
+		error = 1;
+		goto bailout;
+	}
+
+try_long:
+	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;
+	}
+
+	/*
+	 * If the device supports the long response bit, try again and see
+	 * if we can get all of the data.
+	 */
+	if ((response->long_response & SMP_RG_LONG_RESPONSE)
+	 && (long_response == 0)) {
+		ccb->ccb_h.status = CAM_REQ_INPROG;
+		bzero(&(&ccb->ccb_h)[1],
+		      sizeof(union ccb) - sizeof(struct ccb_hdr));
+		long_response = 1;
+		goto try_long;
+	}
+
+	/*
+	 * 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:
-	cam_freeccb(ccb);
-	return (retval);
+	if (ccb != NULL)
+		cam_freeccb(ccb);
+
+	if (request != NULL)
+		free(request);
+
+	if (response != NULL)
+		free(response);
+
+	if (sb != NULL)
+		sbuf_delete(sb);
+
+	return (error);
 }
 
-#endif /* MINIMALISTIC */
+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}
+};
 
-void
-usage(int verbose)
+static int
+smpphycontrol(struct cam_device *device, int argc, char **argv,
+	      char *combinedopt, int retry_count, int timeout)
 {
-	fprintf(verbose ? stdout : stderr,
-"usage:  camcontrol <command>  [device id][generic args][command args]\n"
-"        camcontrol devlist    [-v]\n"
-#ifndef MINIMALISTIC
-"        camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n"
-"        camcontrol tur        [dev_id][generic args]\n"
-"        camcontrol inquiry    [dev_id][generic args] [-D] [-S] [-R]\n"
-"        camcontrol identify   [dev_id][generic args] [-v]\n"
-"        camcontrol reportluns [dev_id][generic args] [-c] [-l] [-r report]\n"
-"        camcontrol readcap    [dev_id][generic args] [-b] [-h] [-H] [-N]\n"
-"                              [-q] [-s]\n"
-"        camcontrol start      [dev_id][generic args]\n"
-"        camcontrol stop       [dev_id][generic args]\n"
-"        camcontrol load       [dev_id][generic args]\n"
-"        camcontrol eject      [dev_id][generic args]\n"
-#endif /* MINIMALISTIC */
-"        camcontrol rescan     <all | bus[:target:lun]>\n"
-"        camcontrol reset      <all | bus[:target:lun]>\n"
-#ifndef MINIMALISTIC
-"        camcontrol defects    [dev_id][generic args] <-f format> [-P][-G]\n"
-"        camcontrol modepage   [dev_id][generic args] <-m page | -l>\n"
-"                              [-P pagectl][-e | -b][-d]\n"
-"        camcontrol cmd        [dev_id][generic args]\n"
+	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);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list