Add Serial Management Protocol (SMP) passthrough support to CAM.

This includes support in the kernel, camcontrol(8), libcam and the mps(4)
driver for SMP passthrough.

The CAM SCSI probe code has been modified to fetch Inquiry VPD page 0x00
to determine supported pages, and will now fetch page 0x83 in addition to
page 0x80 if supported.

Add two new CAM CCBs, XPT_SMP_IO, and XPT_GDEV_ADVINFO.  The SMP CCB is
intended for SMP requests and responses.  The ADVINFO is currently used to
fetch cached VPD page 0x83 data from the transport layer, but is intended
to be extensible to fetch other types of device-specific data.

SMP-only devices are not currently represented in the CAM topology, and so
the current semantics are that the SIM will route SMP CCBs to either the
addressed device, if it contains an SMP target, or its parent, if it
contains an SMP target.  (This is noted in cam_ccb.h, since it will change
later once we have the ability to have SMP-only devices in CAM's topology.)

smp_all.c,
smp_all.h:		New helper routines for SMP.  This includes
			SMP request building routines, response parsing
			routines, error decoding routines, and structure
			definitions for a number of SMP commands.

libcam/Makefile:	Add smp_all.c to libcam, so that SMP functionality
			is available to userland applications.

camcontrol.8,
camcontrol.c:		Add smp passthrough support to camcontrol.  Several
			new subcommands are now available:

			'smpcmd' functions much like 'cmd', except that it
			allows the user to send generic SMP commands.

			'smprg' sends the SMP report general command, and
			displays the decoded output.  It will automatically
			fetch extended output if it is available.

			'smppc' sends the SMP phy control command, with any
			number of potential options.  Among other things,
			this allows the user to reset a phy on a SAS
			expander, or disable a phy on an expander.

			'smpmaninfo' sends the SMP report manufacturer
			information and displays the decoded output.

			'smpphylist' displays a list of phys on an
			expander, and the CAM devices attached to those
			phys, if any.

cam.h,
cam.c:			Add a status value for SMP errors
			(CAM_SMP_STATUS_ERROR).

			Add a missing description for CAM_SCSI_IT_NEXUS_LOST.

			Add support for SMP commands to cam_error_string().

cam_ccb.h:		Rename the CAM_DIR_RESV flag to CAM_DIR_BOTH.  SMP
			commands are by nature bi-directional, and we may
			need to support bi-directional SCSI commands later.

			Add the XPT_SMP_IO CCB.  Since SMP commands are
			bi-directional, there are pointers for both the
			request and response.

			Add a fill routine for SMP CCBs.

			Add the XPT_GDEV_ADVINFO CCB.  This is currently
			used to fetch cached page 0x83 data from the
			transport later, but is extensible to fetch many
			other types of data.

cam_periph.c:		Add support in cam_periph_mapmem() for XPT_SMP_IO
			and XPT_GDEV_ADVINFO CCBs.

cam_xpt.c:		Add support for executing XPT_SMP_IO CCBs.

cam_xpt_internal.h:	Add fields for VPD pages 0x00 and 0x83 in struct
			cam_ed.

scsi_all.c:		Add scsi_get_sas_addr(), a function that parses
			VPD page 0x83 data and pulls out a SAS address.

scsi_all.h:		Add VPD page 0x00 and 0x83 structures, and a
			prototype for scsi_get_sas_addr().

scsi_pass.c:		Add support for mapping buffers in XPT_SMP_IO and
			XPT_GDEV_ADVINFO CCBs.

scsi_xpt.c:		In the SCSI probe code, first ask the device for
			VPD page 0x00.  If any VPD pages are supported,
			that page is required to be implemented.  Based on
			the response, we may probe for the serial number
			(page 0x80) or device id (page 0x83).

			Add support for the XPT_GDEV_ADVINFO CCB.

sys/conf/files:		Add smp_all.c.

mps.c:			Add support for passing in a uio in mps_map_command(),
			so we can map a S/G list at once.

			Add support for SMP passthrough commands in
			mps_data_cb().  SMP is a special case, because the
			first buffer in the S/G list is outbound and the
			second buffer is inbound.

			Add support for warning the user if the busdma code
			comes back with more buffers than will work for the
			command.  This will, for example, help the user
			determine why an SMP command failed if busdma comes
			back with three buffers.

mps_pci.c:		Add sys/uio.h.

mps_sas.c:		Add the SAS address and the parent handle to the
			list of fields we pull from device page 0 and cache
			in struct mpssas_target.  These are needed for SMP
			passthrough.

			Add support for the XPT_SMP_IO CCB.  For now, this
			CCB is routed to the addressed device if it supports
			SMP, or to its parent if it does not and the parent
			does.  This is necessary because CAM does not
			currently support SMP-only nodes in the topology.

			Make SMP passthrough support conditional on
			__FreeBSD_version >= 900026.  This will make it
			easier to MFC this change to the driver without
			MFCing the CAM changes as well.

mps_user.c:		Un-staticize mpi_init_sge() so we can use it for
			the SMP passthrough code.

mpsvar.h:		Add a uio and iovecs into struct mps_command for
			SMP passthrough commands.

			Add a cm_max_segs field to struct mps_command so
			that we can warn the user if busdma comes back with
			too many segments.

			Clear the cm_reply when a command gets freed.  If
			it is not cleared, reply frames will eventually get
			freed into the pool multiple times and corrupt the
			pool.  (This fix is from scottl.)

			Add a prototype for mpi_init_sge().

sys/param.h:		Bump __FreeBSD_version to 900026 for the for the
			inclusion of the XPT_GDEV_ADVINFO and XPT_SMP_IO
			CAM CCBs.
This commit is contained in:
Kenneth D. Merry 2010-11-30 22:39:46 +00:00
parent 06127c9c2a
commit 06e794928b
22 changed files with 3627 additions and 78 deletions

View File

@ -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}

View File

@ -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 ,

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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 */
@ -197,6 +198,12 @@ typedef enum {
CAM_ESF_PRINT_SENSE = 0x20
} cam_error_scsi_flags;
typedef enum {
CAM_ESMF_PRINT_NONE = 0x00,
CAM_ESMF_PRINT_STATUS = 0x10,
CAM_ESMF_PRINT_FULL_CMD = 0x20,
} cam_error_smp_flags;
typedef enum {
CAM_EAF_PRINT_NONE = 0x00,
CAM_EAF_PRINT_STATUS = 0x10,

View File

@ -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
@ -1053,6 +1084,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
@ -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
@ -1115,6 +1168,13 @@ cam_fill_ataio(struct ccb_ataio *ataio, u_int32_t retries,
u_int8_t *data_ptr, u_int32_t dxfer_len,
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 *),
@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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.

View File

@ -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 *),

View File

@ -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 *),

View File

@ -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));

View File

@ -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 @@ probestart(struct cam_periph *periph, union ccb *start_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);
goto again;
}
scsi_report_luns(csio, 5, probedone, MSG_SIMPLE_Q_TAG,
@ -843,19 +861,20 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
}
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 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
probedone(periph, start_ccb);
return;
}
case PROBE_SERIAL_NUM_1:
case PROBE_DEVICE_ID:
{
struct scsi_vpd_device_id *devid;
struct cam_ed *device;
devid = NULL;
device = periph->path->device;
if (device_has_vpd(device, SVPD_DEVICE_ID))
devid = 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 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
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,35 +1382,82 @@ 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) {
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);
}
if (page_list)
free(page_list, M_CAMXPT);
/* No VPDs available, skip to device check. */
csio->data_ptr = NULL;
goto probe_device_check;
}
case PROBE_DEVICE_ID:
{
struct scsi_vpd_device_id *devid;
struct ccb_scsiio *csio;
uint32_t length = 0;
csio = &done_ccb->csio;
devid = (struct scsi_vpd_device_id *)csio->data_ptr;
/* Clean up from previous instance of this device */
if (path->device->device_id != NULL) {
path->device->device_id_len = 0;
free(path->device->device_id, M_CAMXPT);
path->device->device_id = NULL;
}
if (devid == NULL) {
/* Don't process the command as it was never sent */
} else if (CCB_COMPLETED_OK(csio->ccb_h)) {
length = scsi_2btoul(devid->length);
if (length != 0) {
/*
* NB: device_id_len is actual response
* size, not buffer size.
*/
path->device->device_id_len = length +
SVPD_DEVICE_ID_HDR_LEN;
path->device->device_id = (uint8_t *)devid;
}
} else if (cam_periph_error(done_ccb, 0,
SF_RETRY_UA|SF_NO_PRINT,
@ -1366,21 +1469,17 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
/*run_queue*/TRUE);
}
if (page_list != NULL)
free(page_list, M_CAMXPT);
if (serialnum_supported) {
xpt_release_ccb(done_ccb);
PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_1);
xpt_schedule(periph, priority);
return;
}
csio->data_ptr = NULL;
/* FALLTHROUGH */
/* 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
@ -2336,6 +2432,31 @@ scsi_devise_transport(struct cam_path *path)
xpt_action((union ccb *)&cts);
}
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;

620
sys/cam/scsi/smp_all.c Normal file
View File

@ -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;
}

520
sys/cam/scsi/smp_all.h Normal file
View File

@ -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*/

View File

@ -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

View File

@ -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);

View File

@ -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>

View File

@ -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)
{

View File

@ -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;

View File

@ -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 *);

View File

@ -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