freebsd-dev/sys/cam/scsi/smp_all.c
Kenneth D. Merry 06e794928b 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.
2010-11-30 22:39:46 +00:00

621 lines
21 KiB
C

/*-
* 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;
}