summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libcam/Makefile2
-rw-r--r--sbin/camcontrol/camcontrol.8218
-rw-r--r--sbin/camcontrol/camcontrol.c1332
-rw-r--r--sys/cam/cam.c38
-rw-r--r--sys/cam/cam.h7
-rw-r--r--sys/cam/cam_ccb.h88
-rw-r--r--sys/cam/cam_periph.c24
-rw-r--r--sys/cam/cam_xpt.c1
-rw-r--r--sys/cam/cam_xpt_internal.h4
-rw-r--r--sys/cam/scsi/scsi_all.c28
-rw-r--r--sys/cam/scsi/scsi_all.h163
-rw-r--r--sys/cam/scsi/scsi_pass.c8
-rw-r--r--sys/cam/scsi/scsi_xpt.c220
-rw-r--r--sys/cam/scsi/smp_all.c620
-rw-r--r--sys/cam/scsi/smp_all.h520
-rw-r--r--sys/conf/files1
-rw-r--r--sys/dev/mps/mps.c56
-rw-r--r--sys/dev/mps/mps_pci.c1
-rw-r--r--sys/dev/mps/mps_sas.c346
-rw-r--r--sys/dev/mps/mps_user.c2
-rw-r--r--sys/dev/mps/mpsvar.h16
-rw-r--r--sys/sys/param.h2
22 files changed, 3623 insertions, 74 deletions
diff --git a/lib/libcam/Makefile b/lib/libcam/Makefile
index 1f819d0..c5773ea 100644
--- a/lib/libcam/Makefile
+++ b/lib/libcam/Makefile
@@ -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}
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index b85f81d..fc0609a 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -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.000 -O 15 -a
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 ,
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index 2379a8e..88ae1ac 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -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 *device, int argc, char **argv,
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 *device, int argc, char **argv,
#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 argc, char **argv, char *combinedopt,
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,6 +4325,1207 @@ bailout:
}
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;
+ }
+
+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:
+ 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,6 +5645,16 @@ usage(int verbose)
" 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,7 +5688,12 @@ usage(int verbose)
"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"
+"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,6 +5743,28 @@ usage(int verbose)
"-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,7 +5826,7 @@ main(int argc, char **argv)
/*
* Get the base option.
*/
- optreturn = getoption(argv[1], &cmdlist, &arglist, &subopt);
+ optreturn = getoption(option_table,argv[1], &cmdlist, &arglist,&subopt);
if (optreturn == CC_OR_AMBIGUOUS) {
warnx("ambiguous option %s", argv[1]);
@@ -4767,6 +6056,27 @@ main(int argc, char **argv)
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;
diff --git a/sys/cam/cam.c b/sys/cam/cam.c
index 452c9c9..b3c9023 100644
--- a/sys/cam/cam.c
+++ b/sys/cam/cam.c
@@ -37,12 +37,14 @@ __FBSDID("$FreeBSD$");
#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,6 +85,8 @@ const struct cam_status_entry cam_status_table[] = {
{ 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,6 +267,21 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str,
break;
}
break;
+ case XPT_SMP_IO:
+ switch (proto_flags & CAM_EPF_LEVEL_MASK) {
+ case CAM_EPF_NONE:
+ break;
+ case CAM_EPF_ALL:
+ proto_flags |= CAM_ESMF_PRINT_FULL_CMD;
+ /* FALLTHROUGH */
+ case CAM_EPF_NORMAL:
+ case CAM_EPF_MINIMAL:
+ proto_flags |= CAM_ESMF_PRINT_STATUS;
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ break;
default:
break;
}
@@ -289,6 +308,12 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str,
#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,6 +380,19 @@ cam_error_string(struct cam_device *device, union ccb *ccb, char *str,
#endif /* _KERNEL/!_KERNEL */
}
break;
+ case XPT_SMP_IO:
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=
+ CAM_SMP_STATUS_ERROR)
+ break;
+
+ if (proto_flags & CAM_ESF_PRINT_STATUS) {
+ sbuf_cat(&sb, path_str);
+ sbuf_printf(&sb, "SMP status: %s (%#x)\n",
+ smp_error_desc(ccb->smpio.smp_response[2]),
+ ccb->smpio.smp_response[2]);
+ }
+ /* There is no SMP equivalent to SCSI sense. */
+ break;
default:
break;
}
diff --git a/sys/cam/cam.h b/sys/cam/cam.h
index 2b3b98c..7f99a9c 100644
--- a/sys/cam/cam.h
+++ b/sys/cam/cam.h
@@ -147,6 +147,7 @@ typedef enum {
*/
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,6 +199,12 @@ typedef enum {
} 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
diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h
index bbb5069..9c1e3ab 100644
--- a/sys/cam/cam_ccb.h
+++ b/sys/cam/cam_ccb.h
@@ -66,7 +66,7 @@ typedef enum {
*/
CAM_SCATTER_VALID = 0x00000010,/* Scatter/gather list is valid */
CAM_DIS_AUTOSENSE = 0x00000020,/* Disable autosense feature */
- CAM_DIR_RESV = 0x00000000,/* Data direction (00:reserved) */
+ CAM_DIR_BOTH = 0x00000000,/* Data direction (00:IN/OUT) */
CAM_DIR_IN = 0x00000040,/* Data direction (01:DATA IN) */
CAM_DIR_OUT = 0x00000080,/* Data direction (10:DATA OUT) */
CAM_DIR_NONE = 0x000000C0,/* Data direction (11:no data) */
@@ -144,6 +144,8 @@ typedef enum {
/* 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,6 +187,9 @@ typedef enum {
* 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,6 +613,32 @@ struct ccb_pathstats {
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,6 +1085,26 @@ struct ccb_eng_exec { /* This structure must match SCSIIO size */
#define XPT_CCB_INVALID -1 /* for signaling a bad CCB to free */
/*
+ * CCB for getting advanced device information. This operates in a fashion
+ * similar to XPT_GDEV_TYPE. Specify the target in ccb_h, the buffer
+ * type requested, and provide a buffer size/buffer to write to. If the
+ * buffer is too small, the handler will set GDEVAI_FLAG_MORE.
+ */
+struct ccb_getdev_advinfo {
+ struct ccb_hdr ccb_h;
+ uint32_t flags;
+#define CGDAI_FLAG_TRANSPORT 0x1
+#define CGDAI_FLAG_PROTO 0x2
+ uint32_t buftype; /* IN: Type of data being requested */
+ /* NB: buftype is interpreted on a per-transport basis */
+#define CGDAI_TYPE_SCSI_DEVID 1
+ off_t bufsiz; /* IN: Size of external buffer */
+#define CAM_SCSI_DEVID_MAXLEN 65536 /* length in buffer is an uint16_t */
+ off_t provsiz; /* OUT: Size required/used */
+ uint8_t *buf; /* IN/OUT: Buffer for requested data */
+};
+
+/*
* Union of all CCB types for kernel space allocation. This union should
* never be used for manipulating CCBs - its only use is for the allocation
* and deallocation of raw CCB space and is the return type of xpt_ccb_alloc
@@ -1087,9 +1138,11 @@ union ccb {
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,6 +1169,13 @@ cam_fill_ataio(struct ccb_ataio *ataio, u_int32_t retries,
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,6 +1232,32 @@ cam_fill_ataio(struct ccb_ataio *ataio, u_int32_t retries,
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
diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c
index 83bd075..14f3beb 100644
--- a/sys/cam/cam_periph.c
+++ b/sys/cam/cam_periph.c
@@ -648,6 +648,21 @@ cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo)
dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK;
numbufs = 1;
break;
+ case XPT_SMP_IO:
+ data_ptrs[0] = &ccb->smpio.smp_request;
+ lengths[0] = ccb->smpio.smp_request_len;
+ 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,6 +802,15 @@ cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo)
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);
diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c
index dc16e9f..a6298aa 100644
--- a/sys/cam/cam_xpt.c
+++ b/sys/cam/cam_xpt.c
@@ -2386,6 +2386,7 @@ xpt_action_default(union ccb *start_ccb)
/* FALLTHROUGH */
case XPT_RESET_DEV:
case XPT_ENG_EXEC:
+ case XPT_SMP_IO:
{
struct cam_path *path = start_ccb->ccb_h.path;
int frozen;
diff --git a/sys/cam/cam_xpt_internal.h b/sys/cam/cam_xpt_internal.h
index aa6161c..f485e37 100644
--- a/sys/cam/cam_xpt_internal.h
+++ b/sys/cam/cam_xpt_internal.h
@@ -93,6 +93,10 @@ struct cam_ed {
cam_xport transport;
u_int transport_version;
struct scsi_inquiry_data inq_data;
+ uint8_t *supported_vpds;
+ uint8_t supported_vpds_len;
+ uint32_t device_id_len;
+ uint8_t *device_id;
struct ata_params ident_data;
u_int8_t inq_flags; /*
* Current settings for inquiry flags.
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c
index cddac5c..7ededa1 100644
--- a/sys/cam/scsi/scsi_all.c
+++ b/sys/cam/scsi/scsi_all.c
@@ -3552,6 +3552,34 @@ scsi_calc_syncparam(u_int period)
return (period/400);
}
+uint8_t *
+scsi_get_sas_addr(struct scsi_vpd_device_id *id, uint32_t len)
+{
+ uint8_t *bufp, *buf_end;
+ struct scsi_vpd_id_descriptor *descr;
+ struct scsi_vpd_id_naa_basic *naa;
+
+ bufp = buf_end = (uint8_t *)id;
+ bufp += SVPD_DEVICE_ID_HDR_LEN;
+ buf_end += len;
+ while (bufp < buf_end) {
+ descr = (struct scsi_vpd_id_descriptor *)bufp;
+ bufp += SVPD_DEVICE_ID_DESC_HDR_LEN;
+ /* Right now, we only care about SAS NAA IEEE Reg addrs */
+ if (((descr->id_type & SVPD_ID_PIV) != 0)
+ && (descr->proto_codeset >> SVPD_ID_PROTO_SHIFT) ==
+ SCSI_PROTO_SAS
+ && (descr->id_type & SVPD_ID_TYPE_MASK) == SVPD_ID_TYPE_NAA){
+ naa = (struct scsi_vpd_id_naa_basic *)bufp;
+ if ((naa->naa >> 4) == SVPD_ID_NAA_IEEE_REG)
+ return bufp;
+ }
+ bufp += descr->length;
+ }
+
+ return NULL;
+}
+
void
scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h
index cddf4f9..0a7a58f 100644
--- a/sys/cam/scsi/scsi_all.h
+++ b/sys/cam/scsi/scsi_all.h
@@ -796,13 +796,29 @@ struct scsi_vpd_supported_page_list
{
u_int8_t device;
u_int8_t page_code;
-#define SVPD_SUPPORTED_PAGE_LIST 0x00
+#define SVPD_SUPPORTED_PAGE_LIST 0x00
+#define SVPD_SUPPORTED_PAGES_HDR_LEN 4
u_int8_t reserved;
u_int8_t length; /* number of VPD entries */
#define SVPD_SUPPORTED_PAGES_SIZE 251
u_int8_t list[SVPD_SUPPORTED_PAGES_SIZE];
};
+/*
+ * This structure is more suited to target operation, because the
+ * number of supported pages is left to the user to allocate.
+ */
+struct scsi_vpd_supported_pages
+{
+ u_int8_t device;
+ u_int8_t page_code;
+ u_int8_t reserved;
+#define SVPD_SUPPORTED_PAGES 0x00
+ u_int8_t length;
+ u_int8_t page_list[0];
+};
+
+
struct scsi_vpd_unit_serial_number
{
u_int8_t device;
@@ -814,6 +830,148 @@ struct scsi_vpd_unit_serial_number
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,7 +1322,8 @@ void scsi_print_inquiry(struct scsi_inquiry_data *inq_data);
u_int scsi_calc_syncsrate(u_int period_factor);
u_int scsi_calc_syncparam(u_int period);
-
+uint8_t * scsi_get_sas_addr(struct scsi_vpd_device_id *id, uint32_t len);
+
void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *,
union ccb *),
diff --git a/sys/cam/scsi/scsi_pass.c b/sys/cam/scsi/scsi_pass.c
index dddfc7b..00cf61f 100644
--- a/sys/cam/scsi/scsi_pass.c
+++ b/sys/cam/scsi/scsi_pass.c
@@ -524,8 +524,8 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
* We only attempt to map the user memory into kernel space
* if they haven't passed in a physical memory pointer,
* and if there is actually an I/O operation to perform.
- * Right now cam_periph_mapmem() only supports SCSI and device
- * match CCBs. For the SCSI CCBs, we only pass the CCB in if
+ * cam_periph_mapmem() supports SCSI, ATA, SMP, ADVINFO 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,7 +535,9 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
&& (((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_DEV_MATCH)
+ || (ccb->ccb_h.func_code == XPT_SMP_IO)
+ || (ccb->ccb_h.func_code == XPT_GDEV_ADVINFO))) {
bzero(&mapinfo, sizeof(mapinfo));
diff --git a/sys/cam/scsi/scsi_xpt.c b/sys/cam/scsi/scsi_xpt.c
index 979e0a0..2a38128 100644
--- a/sys/cam/scsi/scsi_xpt.c
+++ b/sys/cam/scsi/scsi_xpt.c
@@ -68,7 +68,7 @@ struct scsi_quirk_entry {
struct scsi_inquiry_pattern inq_pat;
u_int8_t quirks;
#define CAM_QUIRK_NOLUNS 0x01
-#define CAM_QUIRK_NOSERIAL 0x02
+#define CAM_QUIRK_NOVPDS 0x02
#define CAM_QUIRK_HILUNS 0x04
#define CAM_QUIRK_NOHILUNS 0x08
#define CAM_QUIRK_NORPTLUNS 0x10
@@ -134,8 +134,9 @@ typedef enum {
PROBE_FULL_INQUIRY,
PROBE_REPORT_LUNS,
PROBE_MODE_SENSE,
- PROBE_SERIAL_NUM_0,
- PROBE_SERIAL_NUM_1,
+ PROBE_SUPPORTED_VPD_LIST,
+ PROBE_DEVICE_ID,
+ PROBE_SERIAL_NUM,
PROBE_TUR_FOR_NEGOTIATION,
PROBE_INQUIRY_BASIC_DV1,
PROBE_INQUIRY_BASIC_DV2,
@@ -149,8 +150,9 @@ static char *probe_action_text[] = {
"PROBE_FULL_INQUIRY",
"PROBE_REPORT_LUNS",
"PROBE_MODE_SENSE",
- "PROBE_SERIAL_NUM_0",
- "PROBE_SERIAL_NUM_1",
+ "PROBE_SUPPORTED_VPD_LIST",
+ "PROBE_DEVICE_ID",
+ "PROBE_SERIAL_NUM",
"PROBE_TUR_FOR_NEGOTIATION",
"PROBE_INQUIRY_BASIC_DV1",
"PROBE_INQUIRY_BASIC_DV2",
@@ -463,7 +465,7 @@ static struct scsi_quirk_entry scsi_quirk_table[] =
T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
" TDC 3600", "U07:"
},
- CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0
+ CAM_QUIRK_NOVPDS, /*mintags*/0, /*maxtags*/0
},
{
/*
@@ -696,6 +698,21 @@ probeschedule(struct cam_periph *periph)
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,7 +827,8 @@ again:
if (INQ_DATA_TQ_ENABLED(inq_buf))
PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
else
- PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
+ PROBE_SET_ACTION(softc,
+ PROBE_SUPPORTED_VPD_LIST);
goto again;
}
scsi_report_luns(csio, 5, probedone, MSG_SIMPLE_Q_TAG,
@@ -843,19 +861,20 @@ again:
}
xpt_print(periph->path, "Unable to mode sense control page - "
"malloc failure\n");
- PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
+ PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST);
}
/* FALLTHROUGH */
- case PROBE_SERIAL_NUM_0:
+ case PROBE_SUPPORTED_VPD_LIST:
{
- struct scsi_vpd_supported_page_list *vpd_list = NULL;
+ struct scsi_vpd_supported_page_list *vpd_list;
struct cam_ed *device;
+ vpd_list = NULL;
device = periph->path->device;
- if ((SCSI_QUIRK(device)->quirks & CAM_QUIRK_NOSERIAL) == 0) {
+
+ 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,7 +897,39 @@ again:
probedone(periph, start_ccb);
return;
}
- case PROBE_SERIAL_NUM_1:
+ case PROBE_DEVICE_ID:
+ {
+ struct scsi_vpd_device_id *devid;
+ struct cam_ed *device;
+
+ devid = NULL;
+ device = periph->path->device;
+ if (device_has_vpd(device, SVPD_DEVICE_ID))
+ devid = 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,8 +942,10 @@ again:
device->serial_num_len = 0;
}
- serial_buf = (struct scsi_vpd_unit_serial_number *)
- malloc(sizeof(*serial_buf), M_CAMXPT, M_NOWAIT|M_ZERO);
+ 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,6 +1099,8 @@ proberequestbackoff(struct cam_periph *periph, struct cam_ed *device)
return (1);
}
+#define CCB_COMPLETED_OK(ccb) (((ccb).status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+
static void
probedone(struct cam_periph *periph, union ccb *done_ccb)
{
@@ -1133,7 +1188,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
PROBE_MODE_SENSE);
else
PROBE_SET_ACTION(softc,
- PROBE_SERIAL_NUM_0);
+ PROBE_SUPPORTED_VPD_LIST);
if (path->device->flags & CAM_DEV_UNCONFIGURED) {
path->device->flags &= ~CAM_DEV_UNCONFIGURED;
@@ -1290,7 +1345,8 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
if (INQ_DATA_TQ_ENABLED(inq_buf))
PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
else
- PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
+ PROBE_SET_ACTION(softc,
+ PROBE_SUPPORTED_VPD_LIST);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
@@ -1326,36 +1382,38 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
}
xpt_release_ccb(done_ccb);
free(mode_hdr, M_CAMXPT);
- PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
+ PROBE_SET_ACTION(softc, PROBE_SUPPORTED_VPD_LIST);
xpt_schedule(periph, priority);
return;
}
- case PROBE_SERIAL_NUM_0:
+ case PROBE_SUPPORTED_VPD_LIST:
{
struct ccb_scsiio *csio;
struct scsi_vpd_supported_page_list *page_list;
- int length, serialnum_supported, i;
- serialnum_supported = 0;
csio = &done_ccb->csio;
page_list =
(struct scsi_vpd_supported_page_list *)csio->data_ptr;
+ if (path->device->supported_vpds != NULL) {
+ 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 ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP
- && (page_list->length > 0)) {
- length = min(page_list->length,
- SVPD_SUPPORTED_PAGES_SIZE);
- for (i = 0; i < length; i++) {
- if (page_list->list[i] ==
- SVPD_UNIT_SERIAL_NUMBER) {
- serialnum_supported = 1;
- break;
- }
- }
+ } else if (CCB_COMPLETED_OK(csio->ccb_h)) {
+ /* Got vpd list */
+ path->device->supported_vpds_len = page_list->length +
+ SVPD_SUPPORTED_PAGES_HDR_LEN;
+ path->device->supported_vpds = (uint8_t *)page_list;
+ xpt_release_ccb(done_ccb);
+ 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,21 +1424,62 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
/*run_queue*/TRUE);
}
- if (page_list != NULL)
+ 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;
- if (serialnum_supported) {
- xpt_release_ccb(done_ccb);
- PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_1);
- xpt_schedule(periph, priority);
+ csio = &done_ccb->csio;
+ devid = (struct scsi_vpd_device_id *)csio->data_ptr;
+
+ /* Clean up from previous instance of this device */
+ if (path->device->device_id != NULL) {
+ path->device->device_id_len = 0;
+ 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);
}
- csio->data_ptr = NULL;
- /* FALLTHROUGH */
+ /* 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;
}
- case PROBE_SERIAL_NUM_1:
+probe_device_check:
+ case PROBE_SERIAL_NUM:
{
struct ccb_scsiio *csio;
struct scsi_vpd_unit_serial_number *serial_buf;
@@ -1395,13 +1494,6 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
serial_buf =
(struct scsi_vpd_unit_serial_number *)csio->data_ptr;
- /* Clean up from previous instance of this device */
- if (path->device->serial_num != NULL) {
- 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
@@ -2227,6 +2319,10 @@ scsi_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
device->queue_flags = 0;
device->serial_num = NULL;
device->serial_num_len = 0;
+ device->device_id = NULL;
+ device->device_id_len = 0;
+ device->supported_vpds = NULL;
+ device->supported_vpds_len = 0;
/*
* XXX should be limited by number of CCBs this bus can
@@ -2337,6 +2433,31 @@ scsi_devise_transport(struct cam_path *path)
}
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,6 +2486,11 @@ scsi_action(union ccb *start_ccb)
(*(sim->sim_action))(sim, start_ccb);
break;
}
+ case XPT_GDEV_ADVINFO:
+ {
+ scsi_getdev_advinfo(start_ccb);
+ break;
+ }
default:
xpt_action_default(start_ccb);
break;
diff --git a/sys/cam/scsi/smp_all.c b/sys/cam/scsi/smp_all.c
new file mode 100644
index 0000000..064f1d5
--- /dev/null
+++ b/sys/cam/scsi/smp_all.c
@@ -0,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;
+}
+
diff --git a/sys/cam/scsi/smp_all.h b/sys/cam/scsi/smp_all.h
new file mode 100644
index 0000000..99e05de
--- /dev/null
+++ b/sys/cam/scsi/smp_all.h
@@ -0,0 +1,520 @@
+/*-
+ * Copyright (c) 2010 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/users/kenm/FreeBSD-test/sys/cam/scsi/smp_all.h#4 $
+ * $FreeBSD$
+ */
+
+/*
+ * Serial Management Protocol definitions.
+ */
+
+#ifndef _SCSI_SMP_ALL_H
+#define _SCSI_SMP_ALL_H 1
+
+#define SMP_FRAME_TYPE_REQUEST 0x40
+#define SMP_FRAME_TYPE_RESPONSE 0x41
+#define SMP_WORD_LEN 4
+#define SMP_CRC_LEN 4
+
+/*
+ * SMP Functions (current as of SPL Revision 7)
+ */
+/* 0x00 to 0x7f: SMP input functions */
+/* 0x00 to 0x0f: General SMP input functions */
+#define SMP_FUNC_REPORT_GENERAL 0x00
+#define SMP_FUNC_REPORT_MANUF_INFO 0x01
+#define SMP_FUNC_REPORT_SC_STATUS 0x03
+#define SMP_FUNC_REPORT_ZONE_PERM_TBL 0x04
+#define SMP_FUNC_REPORT_ZONE_MAN_PWD 0x05
+#define SMP_FUNC_REPORT_BROADCAST 0x06
+
+/* 0x10 to 0x1f: Phy-based SMP input functions */
+#define SMP_FUNC_DISCOVER 0x10
+#define SMP_FUNC_REPORT_PHY_ERR_LOG 0x11
+#define SMP_FUNC_REPORT_PHY_SATA 0x12
+#define SMP_FUNC_REPORT_ROUTE_INFO 0x13
+#define SMP_FUNC_REPORT_PHY_EVENT 0x14
+
+/* 0x20 to 0x2f: Descriptor list-based SMP input functions */
+#define SMP_FUNC_DISCOVER_LIST 0x20
+#define SMP_FUNC_REPORT_PHY_EVENT_LIST 0x21
+#define SMP_FUNC_REPORT_EXP_RTL 0x22
+
+/* 0x30 to 0x3f: Reserved for SMP input functions */
+/* 0x40 to 0x7f: Vendor specific */
+
+/* 0x80 to 0xff: SMP output functions */
+/* 0x80 to 0x8f: General SMP output functions */
+#define SMP_FUNC_CONFIG_GENERAL 0x80
+#define SMP_FUNC_ENABLE_DISABLE_ZONING 0x81
+#define SMP_FUNC_ZONED_BROADCAST 0x85
+#define SMP_FUNC_ZONE_LOCK 0x86
+#define SMP_FUNC_ZONE_ACTIVATE 0x87
+#define SMP_FUNC_ZONE_UNLOCK 0x88
+#define SMP_FUNC_CONFIG_ZM_PWD 0x89
+#define SMP_FUNC_CONFIG_ZONE_PHY_INFO 0x8a
+#define SMP_FUNC_CONFIG_ZONE_PERM_TBL 0x8b
+
+/* 0x90 to 0x9f: Phy-based SMP output functions */
+#define SMP_FUNC_CONFIG_ROUTE_INFO 0x90
+#define SMP_FUNC_PHY_CONTROL 0x91
+#define SMP_FUNC_PHY_TEST_FUNC 0x92
+#define SMP_FUNC_CONFIG_PHY_EVENT 0x93
+
+/* 0xa0 to 0xbf: Reserved for SMP output functions */
+/* 0xc0 to 0xff: Vendor specific */
+
+/*
+ * Function Results (current as of SPL Revision 7)
+ */
+#define SMP_FR_ACCEPTED 0x00
+#define SMP_FR_UNKNOWN_FUNC 0x01
+#define SMP_FR_FUNCTION_FAILED 0x02
+#define SMP_FR_INVALID_REQ_FRAME_LEN 0x03
+#define SMP_FR_INVALID_EXP_CHG_CNT 0x04
+#define SMP_FR_BUSY 0x05
+#define SMP_FR_INCOMPLETE_DESC_LIST 0x06
+#define SMP_FR_PHY_DOES_NOT_EXIST 0x10
+#define SMP_FR_INDEX_DOES_NOT_EXIST 0x11
+#define SMP_FR_PHY_DOES_NOT_SUP_SATA 0x12
+#define SMP_FR_UNKNOWN_PHY_OP 0x13
+#define SMP_FR_UNKNOWN_PHY_TEST_FUNC 0x14
+#define SMP_FR_PHY_TEST_FUNC_INPROG 0x15
+#define SMP_FR_PHY_VACANT 0x16
+#define SMP_FR_UNKNOWN_PHY_EVENT_SRC 0x17
+#define SMP_FR_UNKNOWN_DESC_TYPE 0x18
+#define SMP_FR_UNKNOWN_PHY_FILTER 0x19
+#define SMP_FR_AFFILIATION_VIOLATION 0x1a
+#define SMP_FR_SMP_ZONE_VIOLATION 0x20
+#define SMP_FR_NO_MGMT_ACCESS_RIGHTS 0x21
+#define SMP_FR_UNKNOWN_ED_ZONING_VAL 0x22
+#define SMP_FR_ZONE_LOCK_VIOLATION 0x23
+#define SMP_FR_NOT_ACTIVATED 0x24
+#define SMP_FR_ZG_OUT_OF_RANGE 0x25
+#define SMP_FR_NO_PHYS_PRESENCE 0x26
+#define SMP_FR_SAVING_NOT_SUP 0x27
+#define SMP_FR_SRC_ZONE_DNE 0x28
+#define SMP_FR_DISABLED_PWD_NOT_SUP 0x29
+
+/*
+ * REPORT GENERAL request and response, current as of SPL Revision 7.
+ */
+struct smp_report_general_request
+{
+ uint8_t frame_type;
+ uint8_t function;
+ uint8_t response_len;
+ uint8_t request_len;
+ uint8_t crc[4];
+};
+
+struct smp_report_general_response
+{
+ uint8_t frame_type;
+ uint8_t function;
+ uint8_t function_result;
+ uint8_t response_len;
+#define SMP_RG_RESPONSE_LEN 0x11
+ uint8_t expander_change_count[2];
+ uint8_t expander_route_indexes[2];
+ uint8_t long_response;
+#define SMP_RG_LONG_RESPONSE 0x80
+ uint8_t num_phys;
+ uint8_t config_bits0;
+#define SMP_RG_TABLE_TO_TABLE_SUP 0x80
+#define SMP_RG_ZONE_CONFIGURING 0x40
+#define SMP_RG_SELF_CONFIGURING 0x20
+#define SMP_RG_STP_CONTINUE_AWT 0x10
+#define SMP_RG_OPEN_REJECT_RETRY_SUP 0x08
+#define SMP_RG_CONFIGURES_OTHERS 0x04
+#define SMP_RG_CONFIGURING 0x02
+#define SMP_RG_EXT_CONFIG_ROUTE_TABLE 0x01
+ uint8_t reserved0;
+ uint8_t encl_logical_id[8];
+ uint8_t reserved1[8];
+ uint8_t reserved2[2];
+ uint8_t stp_bus_inact_time_limit[2];
+ uint8_t stp_max_conn_time_limit[2];
+ uint8_t stp_smp_it_nexus_loss_time[2];
+ uint8_t config_bits1;
+#define SMP_RG_NUM_ZONE_GROUPS_MASK 0xc0
+#define SMP_RG_NUM_ZONE_GROUPS_SHIFT 6
+#define SMP_RG_ZONE_LOCKED 0x10
+#define SMP_RG_PP_SUPPORTED 0x08
+#define SMP_RG_PP_ASSERTED 0x04
+#define SMP_RG_ZONING_SUPPORTED 0x02
+#define SMP_RG_ZONING_ENABLED 0x01
+ uint8_t config_bits2;
+#define SMP_RG_SAVING 0x10
+#define SMP_RG_SAVING_ZM_PWD_SUP 0x08
+#define SMP_RG_SAVING_PHY_INFO_SUP 0x04
+#define SMP_RG_SAVING_ZPERM_TAB_SUP 0x02
+#define SMP_RG_SAVING_ZENABLED_SUP 0x01
+ uint8_t max_num_routed_addrs[2];
+ uint8_t active_zm_address[8];
+ uint8_t zone_lock_inact_time_limit[2];
+ uint8_t reserved3[2];
+ uint8_t reserved4;
+ uint8_t first_encl_conn_el_index;
+ uint8_t num_encl_conn_el_indexes;
+ uint8_t reserved5;
+ uint8_t reduced_functionality;
+#define SMP_RG_REDUCED_FUNCTIONALITY 0x80
+ uint8_t time_to_reduced_func;
+ uint8_t initial_time_to_reduced_func;
+ uint8_t max_reduced_func_time;
+ uint8_t last_sc_stat_desc_index[2];
+ uint8_t max_sc_stat_descs[2];
+ uint8_t last_phy_evl_desc_index[2];
+ uint8_t max_stored_pel_descs[2];
+ uint8_t stp_reject_to_open_limit[2];
+ uint8_t reserved6[2];
+ uint8_t crc[4];
+};
+
+/*
+ * REPORT MANUFACTURER INFORMATION request and response, current as of SPL
+ * Revision 7.
+ */
+struct smp_report_manuf_info_request
+{
+ uint8_t frame_type;
+ uint8_t function;
+ uint8_t response_len;
+ uint8_t request_len;
+#define SMP_RMI_REQUEST_LEN 0x00
+ uint8_t crc[4];
+};
+
+struct smp_report_manuf_info_response
+{
+ uint8_t frame_type;
+ uint8_t function;
+ uint8_t function_result;
+ uint8_t response_len;
+#define SMP_RMI_RESPONSE_LEN 0x0e
+ uint8_t expander_change_count[2];
+ uint8_t reserved0[2];
+ uint8_t sas_11_format;
+#define SMP_RMI_SAS11_FORMAT 0x01
+ uint8_t reserved1[3];
+ uint8_t vendor[8];
+ uint8_t product[16];
+ uint8_t revision[4];
+ uint8_t comp_vendor[8];
+ uint8_t comp_id[2];
+ uint8_t comp_revision;
+ uint8_t reserved2;
+ uint8_t vendor_specific[8];
+ uint8_t crc[4];
+};
+
+/*
+ * DISCOVER request and response, current as of SPL Revision 7.
+ */
+struct smp_discover_request
+{
+ uint8_t frame_type;
+ uint8_t function;
+ uint8_t response_len;
+ uint8_t request_len;
+#define SMP_DIS_REQUEST_LEN 0x02
+ uint8_t reserved0[4];
+ uint8_t ignore_zone_group;
+#define SMP_DIS_IGNORE_ZONE_GROUP 0x01
+ uint8_t phy;
+ uint8_t reserved1[2];
+ uint8_t crc[4];
+};
+
+struct smp_discover_response
+{
+ uint8_t frame_type;
+ uint8_t function;
+ uint8_t function_result;
+ uint8_t response_len;
+#define SMP_DIS_RESPONSE_LEN 0x20
+ uint8_t expander_change_count[2];
+ uint8_t reserved0[3];
+ uint8_t phy;
+ uint8_t reserved1[2];
+ uint8_t attached_device;
+#define SMP_DIS_AD_TYPE_MASK 0x70
+#define SMP_DIS_AD_TYPE_NONE 0x00
+#define SMP_DIS_AD_TYPE_SAS_SATA 0x10
+#define SMP_DIS_AD_TYPE_EXP 0x20
+#define SMP_DIS_AD_TYPE_EXP_OLD 0x30
+#define SMP_DIS_ATTACH_REASON_MASK 0x0f
+ uint8_t neg_logical_link_rate;
+#define SMP_DIS_LR_MASK 0x0f
+#define SMP_DIS_LR_DISABLED 0x01
+#define SMP_DIS_LR_PHY_RES_PROB 0x02
+#define SMP_DIS_LR_SPINUP_HOLD 0x03
+#define SMP_DIS_LR_PORT_SEL 0x04
+#define SMP_DIS_LR_RESET_IN_PROG 0x05
+#define SMP_DIS_LR_UNSUP_PHY_ATTACHED 0x06
+#define SMP_DIS_LR_G1_15GBPS 0x08
+#define SMP_DIS_LR_G2_30GBPS 0x09
+#define SMP_DIS_LR_G3_60GBPS 0x0a
+ uint8_t config_bits0;
+#define SMP_DIS_ATTACHED_SSP_INIT 0x08
+#define SMP_DIS_ATTACHED_STP_INIT 0x04
+#define SMP_DIS_ATTACHED_SMP_INIT 0x02
+#define SMP_DIS_ATTACHED_SATA_HOST 0x01
+ uint8_t config_bits1;
+#define SMP_DIS_ATTACHED_SATA_PORTSEL 0x80
+#define SMP_DIS_STP_BUFFER_TOO_SMALL 0x10
+#define SMP_DIS_ATTACHED_SSP_TARG 0x08
+#define SMP_DIS_ATTACHED_STP_TARG 0x04
+#define SMP_DIS_ATTACHED_SMP_TARG 0x02
+#define SMP_DIS_ATTACHED_SATA_DEV 0x01
+ uint8_t sas_address[8];
+ uint8_t attached_sas_address[8];
+ uint8_t attached_phy_id;
+ uint8_t config_bits2;
+#define SMP_DIS_ATT_SLUMB_CAP 0x10
+#define SMP_DIS_ATT_PAR_CAP 0x08
+#define SMP_DIS_ATT_IN_ZPSDS_PER 0x04
+#define SMP_DIS_ATT_REQ_IN_ZPSDS 0x02
+#define SMP_DIS_ATT_BREAK_RPL_CAP 0x01
+ uint8_t reserved2[6];
+ uint8_t link_rate0;
+#define SMP_DIS_PROG_MIN_LR_MASK 0xf0
+#define SMP_DIS_PROG_MIN_LR_SHIFT 4
+#define SMP_DIS_HARD_MIN_LR_MASK 0x0f
+ uint8_t link_rate1;
+#define SMP_DIS_PROG_MAX_LR_MAX 0xf0
+#define SMP_DIS_PROG_MAX_LR_SHIFT 4
+#define SMP_DIS_HARD_MAX_LR_MASK 0x0f
+ uint8_t phy_change_count;
+ uint8_t pp_timeout;
+#define SMP_DIS_VIRTUAL_PHY 0x80
+#define SMP_DIS_PP_TIMEOUT_MASK 0x0f
+ uint8_t routing_attr;
+ uint8_t conn_type;
+ uint8_t conn_el_index;
+ uint8_t conn_phys_link;
+ uint8_t config_bits3;
+#define SMP_DIS_PHY_POW_COND_MASK 0xc0
+#define SMP_DIS_PHY_POW_COND_SHIFT 6
+#define SMP_DIS_SAS_SLUMB_CAP 0x08
+#define SMP_DIS_SAS_PART_CAP 0x04
+#define SMP_DIS_SATA_SLUMB_CAP 0x02
+#define SMP_DIS_SATA_PART_CAP 0x01
+ uint8_t config_bits4;
+#define SMP_DIS_SAS_SLUMB_ENB 0x08
+#define SMP_DIS_SAS_PART_ENB 0x04
+#define SMP_DIS_SATA_SLUMB_ENB 0x02
+#define SMP_DIS_SATA_PART_ENB 0x01
+ uint8_t vendor_spec[2];
+ uint8_t attached_dev_name[8];
+ uint8_t config_bits5;
+#define SMP_DIS_REQ_IN_ZPSDS_CHG 0x40
+#define SMP_DIS_IN_ZPSDS_PER 0x20
+#define SMP_DIS_REQ_IN_ZPSDS 0x10
+#define SMP_DIS_ZG_PER 0x04
+#define SMP_DIS_IN_ZPSDS 0x02
+#define SMP_DIS_ZONING_ENB 0x01
+ uint8_t reserved3[2];
+ uint8_t zone_group;
+ uint8_t self_config_status;
+ uint8_t self_config_levels_comp;
+ uint8_t reserved4[2];
+ uint8_t self_config_sas_addr[8];
+ uint8_t prog_phy_cap[4];
+ uint8_t current_phy_cap[4];
+ uint8_t attached_phy_cap[4];
+ uint8_t reserved5[6];
+ uint8_t neg_phys_link_rate;
+#define SMP_DIS_REASON_MASK 0xf0
+#define SMP_DIS_REASON_SHIFT 4
+#define SMP_DIS_PHYS_LR_MASK 0x0f
+ uint8_t config_bits6;
+#define SMP_DIS_OPTICAL_MODE_ENB 0x04
+#define SMP_DIS_NEG_SSC 0x02
+#define SMP_DIS_HW_MUX_SUP 0x01
+ uint8_t config_bits7;
+#define SMP_DIS_DEF_IN_ZPSDS_PER 0x20
+#define SMP_DIS_DEF_REQ_IN_ZPSDS 0x10
+#define SMP_DIS_DEF_ZG_PER 0x04
+#define SMP_DIS_DEF_ZONING_ENB 0x01
+ uint8_t reserved6;
+ uint8_t reserved7;
+ uint8_t default_zone_group;
+ uint8_t config_bits8;
+#define SMP_DIS_SAVED_IN_ZPSDS_PER 0x20
+#define SMP_DIS_SAVED_REQ_IN_SPSDS 0x10
+#define SMP_DIS_SAVED_ZG_PER 0x04
+#define SMP_DIS_SAVED_ZONING_ENB 0x01
+ uint8_t reserved8;
+ uint8_t reserved9;
+ uint8_t saved_zone_group;
+ uint8_t config_bits9;
+#define SMP_DIS_SHADOW_IN_ZPSDS_PER 0x20
+#define SMP_DIS_SHADOW_IN_REQ_IN_ZPSDS 0x10
+#define SMP_DIS_SHADOW_ZG_PER 0x04
+ uint8_t reserved10;
+ uint8_t reserved11;
+ uint8_t shadow_zone_group;
+ uint8_t device_slot_num;
+ uint8_t device_slot_group_num;
+ uint8_t device_slot_group_out_conn[6];
+ uint8_t stp_buffer_size[2];
+ uint8_t reserved12;
+ uint8_t reserved13;
+ uint8_t crc[4];
+};
+
+/*
+ * PHY CONTROL request and response. Current as of SPL Revision 7.
+ */
+struct smp_phy_control_request
+{
+ uint8_t frame_type;
+ uint8_t function;
+ uint8_t response_len;
+#define SMP_PC_RESPONSE_LEN 0x00
+ uint8_t request_len;
+#define SMP_PC_REQUEST_LEN 0x09
+ uint8_t expected_exp_chg_cnt[2];
+ uint8_t reserved0[3];
+ uint8_t phy;
+ uint8_t phy_operation;
+#define SMP_PC_PHY_OP_NOP 0x00
+#define SMP_PC_PHY_OP_LINK_RESET 0x01
+#define SMP_PC_PHY_OP_HARD_RESET 0x02
+#define SMP_PC_PHY_OP_DISABLE 0x03
+#define SMP_PC_PHY_OP_CLEAR_ERR_LOG 0x05
+#define SMP_PC_PHY_OP_CLEAR_AFFILIATON 0x06
+#define SMP_PC_PHY_OP_TRANS_SATA_PSS 0x07
+#define SMP_PC_PHY_OP_CLEAR_STP_ITN_LS 0x08
+#define SMP_PC_PHY_OP_SET_ATT_DEV_NAME 0x09
+ uint8_t update_pp_timeout;
+#define SMP_PC_UPDATE_PP_TIMEOUT 0x01
+ uint8_t reserved1[12];
+ uint8_t attached_device_name[8];
+ uint8_t prog_min_phys_link_rate;
+#define SMP_PC_PROG_MIN_PL_RATE_MASK 0xf0
+#define SMP_PC_PROG_MIN_PL_RATE_SHIFT 4
+ uint8_t prog_max_phys_link_rate;
+#define SMP_PC_PROG_MAX_PL_RATE_MASK 0xf0
+#define SMP_PC_PROG_MAX_PL_RATE_SHIFT 4
+ uint8_t config_bits0;
+#define SMP_PC_SP_NC 0x00
+#define SMP_PC_SP_DISABLE 0x02
+#define SMP_PC_SP_ENABLE 0x01
+#define SMP_PC_SAS_SLUMBER_NC 0x00
+#define SMP_PC_SAS_SLUMBER_DISABLE 0x80
+#define SMP_PC_SAS_SLUMBER_ENABLE 0x40
+#define SMP_PC_SAS_SLUMBER_MASK 0xc0
+#define SMP_PC_SAS_SLUMBER_SHIFT 6
+#define SMP_PC_SAS_PARTIAL_NC 0x00
+#define SMP_PC_SAS_PARTIAL_DISABLE 0x20
+#define SMP_PC_SAS_PARTIAL_ENABLE 0x10
+#define SMP_PC_SAS_PARTIAL_MASK 0x30
+#define SMP_PC_SAS_PARTIAL_SHIFT 4
+#define SMP_PC_SATA_SLUMBER_NC 0x00
+#define SMP_PC_SATA_SLUMBER_DISABLE 0x08
+#define SMP_PC_SATA_SLUMBER_ENABLE 0x04
+#define SMP_PC_SATA_SLUMBER_MASK 0x0c
+#define SMP_PC_SATA_SLUMBER_SHIFT 2
+#define SMP_PC_SATA_PARTIAL_NC 0x00
+#define SMP_PC_SATA_PARTIAL_DISABLE 0x02
+#define SMP_PC_SATA_PARTIAL_ENABLE 0x01
+#define SMP_PC_SATA_PARTIAL_MASK 0x03
+#define SMP_PC_SATA_PARTIAL_SHIFT 0
+ uint8_t reserved2;
+ uint8_t pp_timeout_value;
+#define SMP_PC_PP_TIMEOUT_MASK 0x0f
+ uint8_t reserved3[3];
+ uint8_t crc[4];
+};
+
+struct smp_phy_control_response
+{
+ uint8_t frame_type;
+ uint8_t function;
+ uint8_t function_result;
+ uint8_t response_len;
+#define SMP_PC_RESPONSE_LEN 0x00
+ uint8_t crc[4];
+};
+
+__BEGIN_DECLS
+
+const char *smp_error_desc(int function_result);
+const char *smp_command_desc(uint8_t cmd_num);
+void smp_command_decode(uint8_t *smp_request, int request_len, struct sbuf *sb,
+ char *line_prefix, int first_line_len, int line_len);
+void smp_command_sbuf(struct ccb_smpio *smpio, struct sbuf *sb,
+ char *line_prefix, int first_line_len, int line_len);
+
+#ifdef _KERNEL
+void smp_error_sbuf(struct ccb_smpio *smpio, struct sbuf *sb);
+#else /* !_KERNEL*/
+void smp_error_sbuf(struct cam_device *device, struct ccb_smpio *smpio,
+ struct sbuf *sb);
+#endif /* _KERNEL/!_KERNEL */
+
+void smp_report_general_sbuf(struct smp_report_general_response *response,
+ int response_len, struct sbuf *sb);
+
+void smp_report_manuf_info_sbuf(struct smp_report_manuf_info_response *response,
+ int response_len, struct sbuf *sb);
+
+void smp_report_general(struct ccb_smpio *smpio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ struct smp_report_general_request *request,
+ int request_len, uint8_t *response, int response_len,
+ int long_response, uint32_t timeout);
+
+void smp_discover(struct ccb_smpio *smpio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ struct smp_discover_request *request, int request_len,
+ uint8_t *response, int response_len, int long_response,
+ int ignore_zone_group, int phy, uint32_t timeout);
+
+void smp_report_manuf_info(struct ccb_smpio *smpio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ struct smp_report_manuf_info_request *request,
+ int request_len, uint8_t *response, int response_len,
+ int long_response, uint32_t timeout);
+
+void smp_phy_control(struct ccb_smpio *smpio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ struct smp_phy_control_request *request, int request_len,
+ uint8_t *response, int response_len, int long_response,
+ uint32_t expected_exp_change_count, int phy, int phy_op,
+ int update_pp_timeout_val, uint64_t attached_device_name,
+ int prog_min_prl, int prog_max_prl, int slumber_partial,
+ int pp_timeout_value, uint32_t timeout);
+__END_DECLS
+
+#endif /*_SCSI_SMP_ALL_H*/
diff --git a/sys/conf/files b/sys/conf/files
index b880c53..f85df8d 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -140,6 +140,7 @@ cam/scsi/scsi_ses.c optional ses
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
diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c
index 404b12e..1fb37e2 100644
--- a/sys/dev/mps/mps.c
+++ b/sys/dev/mps/mps.c
@@ -1569,17 +1569,53 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
sc = cm->cm_sc;
/*
- * Set up DMA direction flags. Note no support for
- * bi-directional transactions.
+ * 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_DATAOUT) {
+ 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,6 +1631,13 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
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,7 +1648,10 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm)
MPI2_SGE_SIMPLE32 *sge;
int error = 0;
- if ((cm->cm_data != NULL) && (cm->cm_length != 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,7 +1665,7 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm)
MPI2_SGE_FLAGS_SHIFT;
sge->Address = 0;
}
- mps_enqueue_request(sc, cm);
+ mps_enqueue_request(sc, cm);
}
return (error);
diff --git a/sys/dev/mps/mps_pci.c b/sys/dev/mps/mps_pci.c
index 2b33ba8..2c57286 100644
--- a/sys/dev/mps/mps_pci.c
+++ b/sys/dev/mps/mps_pci.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
+#include <sys/uio.h>
#include <machine/bus.h>
#include <machine/resource.h>
diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c
index 5883e49..0e7ce03 100644
--- a/sys/dev/mps/mps_sas.c
+++ b/sys/dev/mps/mps_sas.c
@@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$");
#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,6 +57,9 @@ __FBSDID("$FreeBSD$");
#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,9 +74,11 @@ struct mpssas_target {
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,6 +151,12 @@ static int mpssas_complete_tm_request(struct mps_softc *sc,
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,6 +325,8 @@ mpssas_probe_device_complete(struct mps_softc *sc,
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,6 +931,11 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
case XPT_SCSI_IO:
mpssas_action_scsiio(sassc, ccb);
return;
+#if __FreeBSD_version >= 900026
+ case XPT_SMP_IO:
+ mpssas_action_smpio(sassc, ccb);
+ return;
+#endif /* __FreeBSD_version >= 900026 */
default:
ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
break;
@@ -1361,6 +1381,9 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
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,6 +1548,329 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
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)
{
diff --git a/sys/dev/mps/mps_user.c b/sys/dev/mps/mps_user.c
index ede4c02..7ca90c1 100644
--- a/sys/dev/mps/mps_user.c
+++ b/sys/dev/mps/mps_user.c
@@ -322,7 +322,7 @@ mps_user_write_cfg_page(struct mps_softc *sc,
return (0);
}
-static void
+void
mpi_init_sge(struct mps_command *cm, void *req, void *sge)
{
int off, space;
diff --git a/sys/dev/mps/mpsvar.h b/sys/dev/mps/mpsvar.h
index db91030..f578347 100644
--- a/sys/dev/mps/mpsvar.h
+++ b/sys/dev/mps/mpsvar.h
@@ -60,11 +60,19 @@ struct mps_chain {
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,6 +90,8 @@ struct mps_command {
#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,12 +248,15 @@ mps_free_command(struct mps_softc *sc, struct mps_command *cm)
{
struct mps_chain *chain, *chain_temp;
- if (cm->cm_reply != NULL)
+ 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,6 +381,7 @@ int mps_map_command(struct mps_softc *sc, struct mps_command *cm);
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 *);
diff --git a/sys/sys/param.h b/sys/sys/param.h
index ea90308..257d272 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -58,7 +58,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
-#define __FreeBSD_version 900025 /* Master, propagated to newvers */
+#define __FreeBSD_version 900026 /* Master, propagated to newvers */
#ifdef _KERNEL
#define P_OSREL_SIGSEGV 700004
OpenPOWER on IntegriCloud