freebsd-dev/sys/dev/isci/scil/scif_sas_smp_remote_device.c
Pedro F. Giffuni 718cf2ccb9 sys/dev: further adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 2-Clause license, however the tool I
was using misidentified many licenses so this was mostly a manual - error
prone - task.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.
2017-11-27 14:52:40 +00:00

2627 lines
93 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
* BSD LICENSE
*
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 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 MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* 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 DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/**
* @file
*
* @brief This file contains the methods for the SCIF_SAS_SMP_REMOTE_DEVICE object.
*/
#include <dev/isci/scil/sci_controller.h>
#include <dev/isci/scil/scif_sas_controller.h>
#include <dev/isci/scil/scif_sas_remote_device.h>
#include <dev/isci/scil/scif_sas_logger.h>
#include <dev/isci/scil/scif_sas_smp_remote_device.h>
#include <dev/isci/scil/scif_sas_smp_io_request.h>
#include <dev/isci/scil/intel_sas.h>
#include <dev/isci/scil/scic_io_request.h>
#include <dev/isci/scil/scic_remote_device.h>
#include <dev/isci/scil/scif_sas_smp_phy.h>
/**
* @brief This method resets all fields for a smp remote device. This is a
* private method.
*
* @param[in] fw_device the framework SMP device that is being
* constructed.
*
* @return none
*/
void scif_sas_smp_remote_device_clear(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
//reset all fields in smp_device, indicate that the smp device is not
//in discovery process.
fw_device->protocol_device.smp_device.current_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
fw_device->protocol_device.smp_device.current_smp_request =
NOT_IN_SMP_ACTIVITY;
fw_device->protocol_device.smp_device.current_activity_phy_index = 0;
fw_device->protocol_device.smp_device.curr_config_route_index = 0;
fw_device->protocol_device.smp_device.config_route_smp_phy_anchor = NULL;
fw_device->protocol_device.smp_device.is_route_table_cleaned = FALSE;
fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy = NULL;
fw_device->protocol_device.smp_device.scheduled_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
fw_device->protocol_device.smp_device.io_retry_count = 0;
fw_device->protocol_device.smp_device.curr_clear_affiliation_phy = NULL;
if (fw_device->protocol_device.smp_device.smp_activity_timer != NULL)
{
//stop the timer
scif_cb_timer_stop(
fw_device->domain->controller,
fw_device->protocol_device.smp_device.smp_activity_timer
);
//destroy the timer
scif_cb_timer_destroy(
fw_device->domain->controller,
fw_device->protocol_device.smp_device.smp_activity_timer
);
fw_device->protocol_device.smp_device.smp_activity_timer = NULL;
}
}
/**
* @brief This method intializes a smp remote device.
*
* @param[in] fw_device the framework SMP device that is being
* constructed.
*
* @return none
*/
void scif_sas_smp_remote_device_construct(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE,
"scif_sas_smp_remote_device_construct(0x%x) enter\n",
fw_device
));
fw_device->protocol_device.smp_device.number_of_phys = 0;
fw_device->protocol_device.smp_device.expander_route_indexes = 0;
fw_device->protocol_device.smp_device.is_table_to_table_supported = FALSE;
fw_device->protocol_device.smp_device.is_externally_configurable = FALSE;
fw_device->protocol_device.smp_device.is_able_to_config_others = FALSE;
sci_fast_list_init(&fw_device->protocol_device.smp_device.smp_phy_list);
scif_sas_smp_remote_device_clear(fw_device);
}
/**
* @brief This method decodes a smp response to this smp device and then
* continue the smp discover process.
*
* @param[in] fw_device The framework device that a SMP response targets to.
* @param[in] fw_request The pointer to an smp request whose response
* is to be decoded.
* @param[in] response_data The response data passed in.
*
* @return none
*/
SCI_STATUS scif_sas_smp_remote_device_decode_smp_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SCIF_SAS_REQUEST_T * fw_request,
void * response_data,
SCI_IO_STATUS completion_status
)
{
SMP_RESPONSE_T * smp_response = (SMP_RESPONSE_T *)response_data;
SCI_STATUS status = SCI_FAILURE_UNSUPPORTED_INFORMATION_TYPE;
if (fw_device->protocol_device.smp_device.smp_activity_timer != NULL)
{
//if there is a timer being used, recycle it now. Since we may
//use the timer for other purpose next.
scif_cb_timer_destroy(
fw_device->domain->controller,
fw_device->protocol_device.smp_device.smp_activity_timer
);
fw_device->protocol_device.smp_device.smp_activity_timer = NULL;
}
//if Core set the status of this io to be RETRY_REQUIRED, we should
//retry the IO without even decode the response.
if (completion_status == SCI_FAILURE_RETRY_REQUIRED)
{
scif_sas_smp_remote_device_continue_current_activity(
fw_device, fw_request, SCI_FAILURE_RETRY_REQUIRED
);
return SCI_FAILURE_RETRY_REQUIRED;
}
//check the current smp request, decide what's next smp request to issue.
switch (fw_device->protocol_device.smp_device.current_smp_request)
{
case SMP_FUNCTION_REPORT_GENERAL:
{
//interpret REPORT GENERAL response.
status = scif_sas_smp_remote_device_decode_report_general_response(
fw_device, smp_response
);
break;
}
case SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION:
{
// No need to perform any parsing. Just want to see
// the information in a trace if necessary.
status = SCI_SUCCESS;
break;
}
case SMP_FUNCTION_DISCOVER:
{
if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER)
{
//decode discover response
status = scif_sas_smp_remote_device_decode_initial_discover_response(
fw_device, smp_response
);
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET)
{
//decode discover response as a polling result for a remote device
//target reset.
status =
scif_sas_smp_remote_device_decode_target_reset_discover_response(
fw_device, smp_response
);
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_SATA_SPINUP_HOLD_RELEASE)
{
//decode discover response
status =
scif_sas_smp_remote_device_decode_spinup_hold_release_discover_response(
fw_device, smp_response
);
}
else
ASSERT(0);
break;
}
case SMP_FUNCTION_REPORT_PHY_SATA:
{
//decode the report phy sata response.
status = scif_sas_smp_remote_device_decode_report_phy_sata_response(
fw_device, smp_response
);
break;
}
case SMP_FUNCTION_PHY_CONTROL:
{
if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER)
{
//decode the phy control response.
status = scif_sas_smp_remote_device_decode_discover_phy_control_response(
fw_device, smp_response
);
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET)
{
//decode discover response as a polling result for a remote device
//target reset.
status = scif_sas_smp_remote_device_decode_target_reset_phy_control_response(
fw_device, smp_response
);
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAR_AFFILIATION)
{
//currently don't care about the status.
status = SCI_SUCCESS;
}
else
ASSERT(0);
break;
}
case SMP_FUNCTION_CONFIGURE_ROUTE_INFORMATION:
{
//Note, currently we don't expect any abnormal status from config route info response,
//but there is a possibility that we exceed the maximum route index. We will take care
//of errors later.
status = scif_sas_smp_remote_device_decode_config_route_info_response(
fw_device, smp_response
);
break;
}
default:
//unsupported case, TBD
status = SCI_FAILURE_UNSUPPORTED_INFORMATION_TYPE;
break;
} //end of switch
//Continue current activity based on response's decoding status.
scif_sas_smp_remote_device_continue_current_activity(
fw_device, fw_request, status
);
return status;
}
/**
* @brief This method decodes a smp Report Genernal response to this smp device
* and then continue the smp discover process.
*
* @param[in] fw_device The framework device that the REPORT GENERAL command
* targets to.
* @param[in] report_general_response The pointer to a report general response
*
* @return none
*/
SCI_STATUS scif_sas_smp_remote_device_decode_report_general_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_T * smp_response
)
{
SMP_RESPONSE_REPORT_GENERAL_T * report_general_response =
&smp_response->response.report_general;
SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_decode_report_general_response(0x%x, 0x%x) enter\n",
fw_device, smp_response
));
if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
{
/// @todo: more decoding work needed when the function_result is not
/// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
/// function result.
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"Report General function result(0x%x)\n",
response_header->function_result
));
return SCI_FAILURE;
}
//get info from report general response.
fw_device->protocol_device.smp_device.number_of_phys =
(U8)report_general_response->number_of_phys;
//currently there is byte swap issue in U16 data.
fw_device->protocol_device.smp_device.expander_route_indexes =
((report_general_response->expander_route_indexes & 0xff) << 8) |
((report_general_response->expander_route_indexes & 0xff00) >> 8);
fw_device->protocol_device.smp_device.is_table_to_table_supported =
(BOOL)report_general_response->table_to_table_supported;
fw_device->protocol_device.smp_device.is_externally_configurable =
(BOOL)report_general_response->configurable_route_table;
fw_device->protocol_device.smp_device.is_able_to_config_others =
(BOOL)report_general_response->configures_others;
//If the top level expander of a domain is able to configure others,
//no config route table is needed in the domain. Or else,
//we'll let all the externally configurable expanders in the damain
//configure route table.
if (fw_device->containing_device == NULL
&& ! fw_device->protocol_device.smp_device.is_able_to_config_others)
fw_device->domain->is_config_route_table_needed = TRUE;
//knowing number of phys this expander has, we can allocate all the smp phys for
//this expander now if it is not done already.
if (fw_device->protocol_device.smp_device.smp_phy_list.element_count == 0)
scif_sas_smp_remote_device_populate_smp_phy_list(fw_device);
if (report_general_response->configuring)
return SCI_FAILURE_RETRY_REQUIRED;
return SCI_SUCCESS;
}
/**
* @brief This method decodes a smp Discover response to this smp device
* and then continue the smp discover process. This is only ever
* called for the very first discover stage during a given domain
* discovery process.
*
* @param[in] fw_device The framework device that the DISCOVER command
* targets to.
* @param[in] discover_response The pointer to a DISCOVER response
*
* @return none
*/
SCI_STATUS scif_sas_smp_remote_device_decode_initial_discover_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_T * smp_response
)
{
SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
SCI_SAS_ADDRESS_T attached_device_address;
SCIF_SAS_REMOTE_DEVICE_T * attached_remote_device;
SMP_RESPONSE_DISCOVER_T * discover_response =
&smp_response->response.discover;
SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_decode_initial_discover_response(0x%x, 0x%x) enter\n",
fw_device, smp_response
));
if (response_header->function_result == SMP_RESULT_PHY_VACANT)
{
return SCI_SUCCESS;
}
else if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
{
/// @todo: more decoding work needed when the function_result is not
/// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
/// function result.
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"Discover function result(0x%x)\n",
response_header->function_result
));
return SCI_FAILURE;
}
//only if there is target device attached. We don't add device that is
//initiator only.
if ( ( discover_response->u2.sas1_1.attached_device_type
!= SMP_NO_DEVICE_ATTACHED )
&& ( discover_response->protocols.u.bits.attached_ssp_target
|| discover_response->protocols.u.bits.attached_stp_target
|| discover_response->protocols.u.bits.attached_smp_target
|| discover_response->protocols.u.bits.attached_sata_device ) )
{
attached_device_address = discover_response->attached_sas_address;
attached_remote_device = (SCIF_SAS_REMOTE_DEVICE_T *)
scif_domain_get_device_by_sas_address(
fw_domain, &attached_device_address
);
//need to check if the device already existed in the domian.
if (attached_remote_device != SCI_INVALID_HANDLE)
{
#if !defined(DISABLE_WIDE_PORTED_TARGETS)
if ( attached_remote_device->is_currently_discovered == TRUE
&& attached_remote_device != fw_device->containing_device )
{
//a downstream wide port target is found.
attached_remote_device->device_port_width++;
}
else
#endif //#if !defined(DISABLE_WIDE_PORTED_TARGETS)
{
//The device already existed. Mark the device as discovered.
attached_remote_device->is_currently_discovered = TRUE;
}
#if !defined(DISABLE_WIDE_PORTED_TARGETS)
if (attached_remote_device->device_port_width !=
scic_remote_device_get_port_width(attached_remote_device->core_object)
&& discover_response->protocols.u.bits.attached_ssp_target
)
{
scif_sas_remote_device_update_port_width(
attached_remote_device, attached_remote_device->device_port_width);
}
#endif //#if !defined(DISABLE_WIDE_PORTED_TARGETS)
if ( discover_response->protocols.u.bits.attached_smp_target
&& attached_remote_device != fw_device->containing_device)
{
//another expander device is discovered. Its own smp discover will starts after
//this discover finishes.
attached_remote_device->protocol_device.smp_device.scheduled_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER;
}
}
else
{
//report the discovery of a disk for all types of end device.
scif_cb_domain_ea_device_added(
fw_domain->controller, fw_domain, fw_device, discover_response
);
//get info from discover response to see what we found. And do
//extra work according to end device's protocol type.
if ( discover_response->protocols.u.bits.attached_ssp_target
|| discover_response->protocols.u.bits.attached_smp_target)
{
//for SSP or SMP target, no extra work.
;
}
else if ( (discover_response->protocols.u.bits.attached_stp_target)
|| (discover_response->protocols.u.bits.attached_sata_device) )
{
// We treat a SATA Device bit the same as an attached STP
// target.
discover_response->protocols.u.bits.attached_stp_target = 1;
//kick off REPORT PHY SATA to the same phy.
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_REPORT_PHY_SATA;
}
}
}
else if( (discover_response->u2.sas1_1.negotiated_physical_link_rate == SCI_SATA_SPINUP_HOLD
|| discover_response->u4.sas2.negotiated_physical_link_rate == SCI_SATA_SPINUP_HOLD)
&&(discover_response->protocols.u.bits.attached_stp_target
|| discover_response->protocols.u.bits.attached_sata_device)
)
{
attached_remote_device = scif_sas_domain_get_device_by_containing_device(
fw_domain,
fw_device,
discover_response->phy_identifier
);
if (attached_remote_device != SCI_INVALID_HANDLE)
{
//Here, the only reason a device already existed in domain but
//the initial discover rersponse shows it in SPINUP_HOLD, is that
//a device has been removed and coming back in SPINUP_HOLD before
//we detected. The possibility of this situation is very very rare.
//we need to remove the device then add it back using the new
//discover response.
scif_cb_domain_device_removed(
fw_domain->controller, fw_domain, attached_remote_device
);
}
discover_response->protocols.u.bits.attached_stp_target = 1;
//still report ea_device_added(). But this device will not be
//started during scif_remote_device_ea_construct().
scif_cb_domain_ea_device_added(
fw_domain->controller, fw_domain, fw_device, discover_response
);
//need to send Phy Control (RESET) to release the phy from spinup hold
//condition.
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_PHY_CONTROL;
}
//update the smp phy info based on this DISCOVER response.
return scif_sas_smp_remote_device_save_smp_phy_info(
fw_device, discover_response);
}
/**
* @brief This method decodes a smp Report Phy Sata response to this
* smp device and then continue the smp discover process.
*
* @param[in] fw_device The framework device that the REPORT PHY SATA
* command targets to.
* @param[in] report_phy_sata_response The pointer to a REPORT PHY
* SATA response
*
* @return none
*/
SCI_STATUS scif_sas_smp_remote_device_decode_report_phy_sata_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_T * smp_response
)
{
SMP_RESPONSE_REPORT_PHY_SATA_T * report_phy_sata_response =
&smp_response->response.report_phy_sata;
SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_decode_report_phy_sata_response(0x%x, 0x%x) enter\n",
fw_device, smp_response
));
if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
{
/// @todo: more decoding work needed when the function_result is not
/// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
/// function result.
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"Report Phy Sata function result(0x%x)\n",
response_header->function_result
));
return SCI_FAILURE;
}
scif_sas_remote_device_save_report_phy_sata_information(
report_phy_sata_response
);
// continue the discover process.
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_DISCOVER;
return SCI_SUCCESS;
}
/**
* @brief This method decodes a smp Phy Control response to this smp device and
* then continue the smp TARGET RESET process.
*
* @param[in] fw_device The framework device that the Phy Control command
* targets to.
* @param[in] smp_response The pointer to a Phy Control response
* @param[in] fw_io The scif IO request that associates to this smp response.
*
* @return none
*/
SCI_STATUS scif_sas_smp_remote_device_decode_target_reset_phy_control_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_T * smp_response
)
{
SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
SCI_STATUS status = SCI_SUCCESS;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_decode_target_reset_phy_control_response(0x%x, 0x%x) enter\n",
fw_device, smp_response
));
if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
{
/// @todo: more decoding work needed when the function_result is not
/// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
/// function result.
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"Phy Control function unaccepted result(0x%x)\n",
response_header->function_result
));
status = SCI_FAILURE_RETRY_REQUIRED;
}
// phy Control succeeded.
return status;
}
/**
* @brief This method decodes a smp Phy Control response to this smp device and
* then continue the smp DISCOVER process.
*
* @param[in] fw_device The framework device that the Phy Control command
* targets to.
* @param[in] smp_response The pointer to a Phy Control response
*
* @return Almost always SCI_SUCCESS
*/
SCI_STATUS scif_sas_smp_remote_device_decode_discover_phy_control_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_T * smp_response
)
{
SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
SCI_STATUS status = SCI_SUCCESS;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_decode_discover_phy_control_response(0x%x, 0x%x) enter\n",
fw_device, smp_response
));
if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
{
/// @todo: more decoding work needed when the function_result is not
/// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
/// function result.
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"Phy Control function unaccepted result(0x%x)\n",
response_header->function_result
));
return SCI_FAILURE_RETRY_REQUIRED;
}
// continue the discover process.
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_DISCOVER;
// phy Control succeeded.
return status;
}
/**
* @brief This method decodes a smp Discover response to this smp device
* and then continue the smp discover process.
*
* @param[in] fw_device The framework device that the DISCOVER command
* targets to.
* @param[in] discover_response The pointer to a DISCOVER response
*
* @return none
*/
SCI_STATUS scif_sas_smp_remote_device_decode_target_reset_discover_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_T * smp_response
)
{
SCIF_SAS_DOMAIN_T * fw_domain;
SCI_SAS_ADDRESS_T attached_device_address;
SMP_RESPONSE_DISCOVER_T * discover_response =
&smp_response->response.discover;
SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_decode_target_reset_discover_response(0x%x, 0x%x) enter\n",
fw_device, smp_response
));
if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
{
/// @todo: more decoding work needed when the function_result is not
/// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
/// function result.
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"Discover function result(0x%x)\n",
response_header->function_result
));
return SCI_FAILURE_RETRY_REQUIRED;
}
//only if there is device attached.
if ( discover_response->u2.sas1_1.attached_device_type != SMP_NO_DEVICE_ATTACHED )
{
fw_domain = fw_device->domain;
attached_device_address = discover_response->attached_sas_address;
// the device should have already existed in the domian.
ASSERT(scif_domain_get_device_by_sas_address(
fw_domain,
&attached_device_address
) != SCI_INVALID_HANDLE);
return SCI_SUCCESS;
}
else
return SCI_FAILURE_RETRY_REQUIRED;
}
/**
* @brief This method decodes a smp Discover response to this smp device
* for SPINUP_HOLD_RELEASE activity. If a DISCOVER response says
* SATA DEVICE ATTACHED and has a valid NPL value, we call fw_device's
* start_handler(). But if a DISCOVER response still shows SPINUP
* in NPL state, we need to return retry_required status
*
* @param[in] fw_device The framework device that the DISCOVER command
* targets to.
* @param[in] discover_response The pointer to a DISCOVER response
*
* @return SCI_SUCCESS
* SCI_FAILURE_RETRY_REQUIRED
*/
SCI_STATUS scif_sas_smp_remote_device_decode_spinup_hold_release_discover_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_T * smp_response
)
{
SMP_RESPONSE_DISCOVER_T * discover_response = &smp_response->response.discover;
SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_decode_spinup_hold_release_discover_response(0x%x, 0x%x) enter\n",
fw_device, smp_response
));
if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
{
/// @todo: more decoding work needed when the function_result is not
/// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
/// function result.
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"Discover function result(0x%x)\n",
response_header->function_result
));
return SCI_FAILURE;
}
if ( discover_response->u2.sas1_1.attached_device_type != SMP_NO_DEVICE_ATTACHED )
{
if (discover_response->u2.sas1_1.negotiated_physical_link_rate != SCI_SATA_SPINUP_HOLD
&& discover_response->u4.sas2.negotiated_physical_link_rate != SCI_SATA_SPINUP_HOLD
&& ( discover_response->protocols.u.bits.attached_stp_target
||discover_response->protocols.u.bits.attached_sata_device )
)
{
SCIF_SAS_REMOTE_DEVICE_T * target_device =
scif_sas_domain_get_device_by_containing_device(
fw_device->domain,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index
);
//Need to update the device's connection rate. Its connection rate was SPINIP_HOLD.
scic_remote_device_set_max_connection_rate(
target_device->core_object,
discover_response->u2.sas1_1.negotiated_physical_link_rate
);
//Need to update the smp phy info too.
scif_sas_smp_remote_device_save_smp_phy_info(
fw_device, discover_response);
//This device has already constructed, only need to call start_handler
//of this device here.
return target_device->state_handlers->parent.start_handler(
&target_device->parent );
}
else
return SCI_FAILURE_RETRY_REQUIRED;
}
else
return SCI_FAILURE_RETRY_REQUIRED;
}
/**
* @brief This method decodes a smp CONFIG ROUTE INFO response to this smp
* device and then continue to config route table.
*
* @param[in] fw_device The framework device that the CONFIG ROUTE INFO command
* targets to.
* @param[in] smp_response The pointer to a CONFIG ROUTE INFO response
*
* @return none
*/
SCI_STATUS scif_sas_smp_remote_device_decode_config_route_info_response(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_T * smp_response
)
{
SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_decode_config_route_info_response(0x%x, 0x%x) enter\n",
fw_device, smp_response
));
if (response_header->function_result == SMP_RESULT_INDEX_DOES_NOT_EXIST)
{
//case of exceeding max route index. We need to remove the devices that are not
//able to be edit to route table. The destination config route smp phy
//is used to remove devices.
scif_sas_smp_remote_device_cancel_config_route_table_activity(fw_device);
return SCI_FAILURE_EXCEED_MAX_ROUTE_INDEX;
}
else if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
{
/// @todo: more decoding work needed when the function_result is not
/// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
/// function result.
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"Discover function result(0x%x)\n",
response_header->function_result
));
return SCI_FAILURE;
}
return SCI_SUCCESS;
}
/**
* @brief This method starts the smp Discover process for an expander by
* sending Report General request.
*
* @param[in] fw_device The framework smp device that a command
* targets to.
*
* @return none
*/
void scif_sas_smp_remote_device_start_discover(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_start_discover(0x%x) enter\n",
fw_device
));
//For safety, clear the device again, there may be some config route table
//related info are not cleared yet.
scif_sas_smp_remote_device_clear(fw_device);
//set current activity
fw_device->protocol_device.smp_device.current_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER;
//Set current_smp_request to REPORT GENERAL.
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_REPORT_GENERAL;
//reset discover_to_start flag.
fw_device->protocol_device.smp_device.scheduled_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
//build the first smp request Report Genernal.
scif_sas_smp_request_construct_report_general(fw_controller, fw_device);
//issue DPC to start this request.
scif_cb_start_internal_io_task_schedule(
fw_controller,
scif_sas_controller_start_high_priority_io,
fw_controller
);
}
/**
* @brief This method continues the smp Discover process.
*
* @param[in] fw_device The framework smp device that a DISCOVER command
* targets to.
* @param[in] fw_request The pointer to an smp request whose response
* has been decoded.
* @param[in] status The decoding status of the smp request's response
*
* @return none
*/
void scif_sas_smp_remote_device_continue_current_activity(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SCIF_SAS_REQUEST_T * fw_request,
SCI_STATUS status
)
{
SCIF_SAS_IO_REQUEST_T * fw_io = (SCIF_SAS_IO_REQUEST_T *)fw_request;
// save the retry count.
U8 io_retry_count = fw_io->retry_count;
if (fw_request->is_internal)
{
// Complete this internal io request now. We want to free this io before
// we create another SMP request, which is going to happen soon.
scif_sas_internal_io_request_complete(
fw_device->domain->controller,
(SCIF_SAS_INTERNAL_IO_REQUEST_T *)fw_request,
SCI_SUCCESS
);
}
if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER)
{
if (status == SCI_SUCCESS)
{ //continue the discover process.
scif_sas_smp_remote_device_continue_discover(fw_device);
}
else if (status == SCI_FAILURE_RETRY_REQUIRED)
{
//Retry the smp request. Since we are in the middle of Discover
//process, all the smp requests are internal. A new smp request
//will be created for retry.
U32 retry_wait_duration = (SCIF_DOMAIN_DISCOVER_TIMEOUT / 2) / SCIF_SAS_IO_RETRY_LIMIT;
if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
scif_sas_smp_remote_device_retry_internal_io (
fw_device, io_retry_count, retry_wait_duration);
else
scif_sas_smp_remote_device_fail_discover(fw_device);
}
else if (status == SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION)
{
//remove this expander device and its child devices. No need to
//continue the discover on this device.
scif_sas_domain_remove_expander_device(fw_device->domain, fw_device);
//continue the domain's smp discover.
scif_sas_domain_continue_discover(fw_device->domain);
}
else
{ //terminate the discover process.
scif_sas_smp_remote_device_fail_discover(fw_device);
}
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET)
{
if (status == SCI_SUCCESS)
{ //continue the target reset process.
scif_sas_smp_remote_device_continue_target_reset(
fw_device, fw_request);
}
else if (status == SCI_FAILURE_RETRY_REQUIRED)
{
//Retry the same smp request. Since we are in the middle of Target
//reset process, all the smp requests are using external resource.
//We will use the exactly same memory to retry.
if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
{
if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
{
//create the timer to wait before retry.
fw_device->protocol_device.smp_device.smp_activity_timer =
scif_cb_timer_create(
(SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
(SCI_TIMER_CALLBACK_T)scif_sas_smp_external_request_retry,
(void*)fw_request
);
}
else
{
ASSERT(0);
}
//start the timer to wait
scif_cb_timer_start(
(SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
fw_device->protocol_device.smp_device.smp_activity_timer,
SMP_REQUEST_RETRY_WAIT_DURATION //20 miliseconds
);
}
else
scif_sas_smp_remote_device_fail_target_reset(fw_device, fw_request);
}
else
//terminate the discover process.
scif_sas_smp_remote_device_fail_target_reset(fw_device, fw_request);
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_SATA_SPINUP_HOLD_RELEASE)
{
SCIF_SAS_REMOTE_DEVICE_T * target_device =
scif_sas_domain_get_device_by_containing_device(
fw_device->domain,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index
);
if (status == SCI_SUCCESS)
{
//move on to next round of SPINUP_HOLD_REALSE activity.
scif_sas_smp_remote_device_sata_spinup_hold_release(fw_device);
}
else if (status == SCI_FAILURE_RETRY_REQUIRED)
{
U32 delay =
(scic_remote_device_get_suggested_reset_timeout(target_device->core_object) /
SCIF_SAS_IO_RETRY_LIMIT);
//Retry the smp request. Since we are in the middle of Discover
//process, all the smp requests are internal. A new smp request
//will be created for retry.
if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
{
scif_sas_smp_remote_device_retry_internal_io(
fw_device, io_retry_count, delay);
}
else //give up on this target device.
{
scif_sas_smp_remote_device_fail_target_spinup_hold_release(
fw_device , target_device);
}
}
else //give up on this target device.
scif_sas_smp_remote_device_fail_target_spinup_hold_release(
fw_device, target_device);
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE)
{
SCI_FAST_LIST_ELEMENT_T * next_phy_element = sci_fast_list_get_next(
&(fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element) );
SCI_FAST_LIST_T * destination_smp_phy_list =
fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element.owning_list;
SCIF_SAS_SMP_PHY_T * next_phy_in_wide_port = NULL;
if (next_phy_element != NULL
&& status != SCI_FAILURE_EXCEED_MAX_ROUTE_INDEX)
{
fw_device->protocol_device.smp_device.curr_config_route_index++;
fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy =
(SCIF_SAS_SMP_PHY_T *)sci_fast_list_get_object(next_phy_element);
// Update the anchor for config route index.
fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor =
fw_device->protocol_device.smp_device.curr_config_route_index;
scif_sas_smp_remote_device_configure_route_table(fw_device);
}
else if ( scif_sas_smp_remote_device_get_config_route_table_method(fw_device)
== SCIF_SAS_CONFIG_ROUTE_TABLE_ALL_PHYS
&& (next_phy_in_wide_port = scif_sas_smp_phy_find_next_phy_in_wide_port(
fw_device->protocol_device.smp_device.config_route_smp_phy_anchor)
)!= NULL
)
{
//config the other phy in the same wide port
fw_device->protocol_device.smp_device.config_route_smp_phy_anchor =
next_phy_in_wide_port;
fw_device->protocol_device.smp_device.current_activity_phy_index =
fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier;
fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy =
sci_fast_list_get_head(destination_smp_phy_list);
if (fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor != 0)
fw_device->protocol_device.smp_device.curr_config_route_index =
fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor + 1;
else
fw_device->protocol_device.smp_device.curr_config_route_index = 0;
scif_sas_smp_remote_device_configure_route_table(fw_device);
}
else if ( fw_device->protocol_device.smp_device.is_route_table_cleaned == FALSE)
{
fw_device->protocol_device.smp_device.current_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAN_ROUTE_TABLE;
scif_sas_smp_remote_device_clean_route_table(fw_device);
}
else
{
//set this device's activity to NON.
fw_device->protocol_device.smp_device.current_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
//we need to notify domain that this device finished config route table, domain
//may pick up other activities (i.e. Discover) for other expanders.
scif_sas_domain_continue_discover(fw_device->domain);
}
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAN_ROUTE_TABLE)
{
scif_sas_smp_remote_device_clean_route_table(fw_device);
}
else if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAR_AFFILIATION)
{
scif_sas_smp_remote_device_continue_clear_affiliation(fw_device);
}
}
/**
* @brief This method continues the smp Discover process.
*
* @param[in] fw_device The framework smp device that a DISCOVER command
* targets to.
*
* @return none
*/
void scif_sas_smp_remote_device_continue_discover(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_continue_discover(0x%x) enter\n",
fw_device
));
switch (fw_device->protocol_device.smp_device.current_smp_request)
{
case SMP_FUNCTION_REPORT_GENERAL:
// send the REPORT MANUFACTURER_INFO request
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION;
scif_sas_smp_request_construct_report_manufacturer_info(
fw_domain->controller, fw_device
);
break;
case SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION:
//send the first SMP DISCOVER request.
fw_device->protocol_device.smp_device.current_activity_phy_index = 0;
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_DISCOVER;
scif_sas_smp_request_construct_discover(
fw_domain->controller,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index,
NULL, NULL
);
break;
case SMP_FUNCTION_DISCOVER:
fw_device->protocol_device.smp_device.current_activity_phy_index++;
if ( (fw_device->protocol_device.smp_device.current_activity_phy_index <
fw_device->protocol_device.smp_device.number_of_phys) )
{
scif_sas_smp_request_construct_discover(
fw_domain->controller,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index,
NULL, NULL
);
}
else
scif_sas_smp_remote_device_finish_initial_discover(fw_device);
break;
case SMP_FUNCTION_REPORT_PHY_SATA:
scif_sas_smp_request_construct_report_phy_sata(
fw_device->domain->controller,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index
);
break;
case SMP_FUNCTION_PHY_CONTROL:
scif_sas_smp_request_construct_phy_control(
fw_device->domain->controller,
fw_device,
PHY_OPERATION_HARD_RESET,
fw_device->protocol_device.smp_device.current_activity_phy_index,
NULL,
NULL
);
break;
default:
break;
}
}
/**
* @brief This method finishes the initial smp DISCOVER process. There
* may be a spinup_hold release phase following of initial discover,
* depending on whether there are SATA device in the domain
* in SATA_SPINUP_HOLD condition.
*
* @param[in] fw_device The framework smp device that finishes all the
* DISCOVER requests.
*
* @return none
*/
void scif_sas_smp_remote_device_finish_initial_discover(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_SAS_REMOTE_DEVICE_T * device_in_sata_spinup_hold =
scif_sas_domain_find_device_in_spinup_hold(fw_device->domain);
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_finish_initial_discover(0x%x) enter\n",
fw_device
));
if ( device_in_sata_spinup_hold != NULL )
{
//call the common private routine to reset all fields of this smp device.
scif_sas_smp_remote_device_clear(fw_device);
//Move on to next activity SPINUP_HOLD_RELEASE
fw_device->protocol_device.smp_device.current_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_SATA_SPINUP_HOLD_RELEASE;
//create the timer to delay a little bit before going to
//sata spinup hold release activity.
if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
{
fw_device->protocol_device.smp_device.smp_activity_timer =
scif_cb_timer_create(
(SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
(SCI_TIMER_CALLBACK_T)scif_sas_smp_remote_device_sata_spinup_hold_release,
(void*)fw_device
);
}
else
{
ASSERT (0);
}
scif_cb_timer_start(
(SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
fw_device->protocol_device.smp_device.smp_activity_timer,
SMP_SPINUP_HOLD_RELEASE_WAIT_DURATION
);
}
else
scif_sas_smp_remote_device_finish_discover(fw_device);
}
/**
* @brief This method finishes the smp DISCOVER process.
*
* @param[in] fw_device The framework smp device that finishes all the
* DISCOVER requests.
*
* @return none
*/
void scif_sas_smp_remote_device_finish_discover(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_finish_discover(0x%x) enter\n",
fw_device
));
if ( fw_domain->is_config_route_table_needed
&& fw_device->protocol_device.smp_device.smp_phy_list.list_head != NULL)
scif_sas_smp_remote_device_configure_upstream_expander_route_info(fw_device);
//call the common private routine to reset all fields of this smp device.
scif_sas_smp_remote_device_clear(fw_device);
#ifdef SCI_SMP_PHY_LIST_DEBUG_PRINT
scif_sas_smp_remote_device_print_smp_phy_list(fw_device);
#endif
//notify domain this smp device's discover finishes, it's up to domain
//to continue the discover process in a bigger scope.
scif_sas_domain_continue_discover(fw_domain);
}
/**
* @brief This method continues the smp Target Reset (Phy Control) process.
*
* @param[in] fw_device The framework smp device that a smp reset targets to.
*
* @return none
*/
void scif_sas_smp_remote_device_continue_target_reset(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SCIF_SAS_REQUEST_T * fw_request
)
{
SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
SCIF_SAS_REMOTE_DEVICE_T * target_device =
scif_sas_domain_get_device_by_containing_device(
fw_device->domain,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index
);
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_continue_target_reset(0x%x, 0x%x) enter\n",
fw_device, fw_request
));
if (fw_device->protocol_device.smp_device.current_smp_request ==
SMP_FUNCTION_PHY_CONTROL)
{
//query the core remote device to get suggested reset timeout value
//then scale down by factor of 8 to get the duration of the pause
//before sending out Discover command to poll.
U32 delay =
(scic_remote_device_get_suggested_reset_timeout(target_device->core_object)/8);
//create the timer to send Discover command polling target device's
//coming back.
if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
{
fw_device->protocol_device.smp_device.smp_activity_timer =
scif_cb_timer_create(
(SCI_CONTROLLER_HANDLE_T *)fw_controller,
(SCI_TIMER_CALLBACK_T)scif_sas_smp_remote_device_target_reset_poll,
(void*)fw_request
);
}
else
{
ASSERT(0);
}
//start the timer
scif_cb_timer_start(
(SCI_CONTROLLER_HANDLE_T)fw_controller,
fw_device->protocol_device.smp_device.smp_activity_timer,
delay
);
}
else if (fw_device->protocol_device.smp_device.current_smp_request ==
SMP_FUNCTION_DISCOVER)
{
//tell target reset successful
scif_sas_remote_device_target_reset_complete(
target_device, fw_request, SCI_SUCCESS);
}
}
/**
* @brief This routine is invoked by timer or when 2 BCN are received
* after Phy Control command. This routine will construct a
* Discover command to the same expander phy to poll the target
* device's coming back. This new request is then put into
* high priority queue and will be started by a DPC soon.
*
* @param[in] fw_request The scif request for smp activities.
*/
void scif_sas_smp_remote_device_target_reset_poll(
SCIF_SAS_REQUEST_T * fw_request
)
{
SCIF_SAS_REMOTE_DEVICE_T * fw_device = fw_request->device;
SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
void * new_command_handle;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_target_reset_poll(0x%x) enter\n",
fw_request
));
// Before we construct new io using the same memory, we need to
// remove the IO from the list of outstanding requests on the domain
// so that we don't damage the domain's fast list of request.
sci_fast_list_remove_element(&fw_request->list_element);
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_DISCOVER;
//sent smp discover request to poll on remote device's coming back.
//construct Discover command using the same memory as fw_request.
new_command_handle = scif_sas_smp_request_construct_discover(
fw_device->domain->controller,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index,
(void *)sci_object_get_association(fw_request),
(void *)fw_request
);
//put into the high priority queue.
sci_pool_put(fw_controller->hprq.pool, (POINTER_UINT) new_command_handle);
//schedule the DPC to start new Discover command.
scif_cb_start_internal_io_task_schedule(
fw_controller, scif_sas_controller_start_high_priority_io, fw_controller
);
}
/**
* @brief This method fails discover process.
*
* @param[in] fw_device The framework smp device that failed at current
* activity.
*
* @return none
*/
void scif_sas_smp_remote_device_fail_discover(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_fail_discover(0x%x) enter\n",
fw_device
));
switch (fw_device->protocol_device.smp_device.current_smp_request)
{
case SMP_FUNCTION_REPORT_GENERAL:
case SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION:
scif_sas_smp_remote_device_finish_discover(fw_device);
break;
case SMP_FUNCTION_DISCOVER:
case SMP_FUNCTION_REPORT_PHY_SATA:
//Retry limit reached, we will continue to send DISCOVER to next phy.
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_DISCOVER;
scif_sas_smp_remote_device_continue_discover(fw_device);
break;
default:
break;
}
}
/**
* @brief This method fails Target Reset.
*
* @param[in] fw_device The framework smp device that failed at current
* activity.
* @param[in] fw_request The smp request created for target reset
* using external resource.
*
* @return none
*/
void scif_sas_smp_remote_device_fail_target_reset(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SCIF_SAS_REQUEST_T * fw_request
)
{
SCIF_SAS_REMOTE_DEVICE_T * target_device =
scif_sas_domain_get_device_by_containing_device(
fw_device->domain,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index
);
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_fail_target_reset(0x%x, 0x%x, 0x%x) enter\n",
fw_device, target_device, fw_request
));
//tell target reset failed
scif_sas_remote_device_target_reset_complete(
target_device, fw_request, SCI_FAILURE);
}
/**
* @brief This method init or continue the SATA SPINUP_HOLD RELEASE activity.
* This function searches domain's device list, find a device in STOPPED STATE
* and its connection_rate is SPINIP, then send DISCOVER command to its expander
* phy id to poll. But if searching the domain's device list for SATA devices on
* SPINUP_HOLD finds no device, the activity SPINUP_HOLD_RELEASE is finished.
* We then call fw_domain->device_start_complete_handler() for this smp-device.
*
* @param[in] fw_device The framework smp device that is on SATA SPINUP_HOLD_RELEASE
* activity.
*
* @return none
*/
void scif_sas_smp_remote_device_sata_spinup_hold_release(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
SCIF_SAS_CONTROLLER_T * fw_controller = fw_domain->controller;
SCIF_SAS_REMOTE_DEVICE_T * device_to_poll = NULL;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_sata_spinup_hold_release(0x%x) enter\n",
fw_device
));
//search throught domain's device list to find a sata device on spinup_hold
//state to poll.
device_to_poll = scif_sas_domain_find_device_in_spinup_hold(fw_domain);
if (device_to_poll != NULL)
{
//send DISCOVER command to this device's expaner phy.
fw_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_DISCOVER;
fw_device->protocol_device.smp_device.current_activity_phy_index =
device_to_poll->expander_phy_identifier;
scif_sas_smp_request_construct_discover(
fw_domain->controller,
fw_device,
fw_device->protocol_device.smp_device.current_activity_phy_index,
NULL, NULL
);
//schedule the DPC to start new Discover command.
scif_cb_start_internal_io_task_schedule(
fw_controller, scif_sas_controller_start_high_priority_io, fw_controller
);
}
else //SATA SPINUP HOLD RELEASE activity is done.
scif_sas_smp_remote_device_finish_discover (fw_device);
}
/**
* @brief This method fail an action of SATA SPINUP_HOLD RELEASE on a single EA
* SATA device. It will remove a remote_device object for a sata device
* that fails to come out of spinup_hold.
*
* @param[in] fw_device The framework smp device that is on SATA SPINUP_HOLD_RELEASE
* activity.
* @param[in] target_device The expander attached device failed being brought out
* of SPINUP_HOLD state.
*
* @return none
*/
void scif_sas_smp_remote_device_fail_target_spinup_hold_release(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SCIF_SAS_REMOTE_DEVICE_T * target_device
)
{
SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_fail_target_spinup_hold_release(0x%x, 0x%x) enter\n",
fw_device, target_device
));
//need to remove the device, since we have to give up on spinup_hold_release
//activity on this device.
scif_cb_domain_device_removed(
fw_domain->controller, fw_domain, target_device
);
//move on to next round of SPINUP_HOLD_REALSE activity.
scif_sas_smp_remote_device_sata_spinup_hold_release(fw_device);
}
/**
* @brief This method retry only internal IO for the smp device.
*
* @param[in] fw_device The framework smp device that has an smp request to retry.
* @param[in] io_retry_count current count for times the IO being retried.
* @param[in] delay The time delay before the io gets retried.
*
* @return none
*/
void scif_sas_smp_remote_device_retry_internal_io(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
U8 io_retry_count,
U32 delay
)
{
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_retry_internal_io(0x%x, 0x%x, 0x%x) enter\n",
fw_device, io_retry_count, delay
));
fw_device->protocol_device.smp_device.io_retry_count =
io_retry_count;
//create the timer for poll target device's coming back.
if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
{
fw_device->protocol_device.smp_device.smp_activity_timer =
scif_cb_timer_create(
(SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
(SCI_TIMER_CALLBACK_T)scif_sas_smp_internal_request_retry,
(void*)fw_device
);
}
else
{
ASSERT(0);
}
//start the timer for a purpose of waiting.
scif_cb_timer_start(
(SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
fw_device->protocol_device.smp_device.smp_activity_timer,
delay
);
}
/**
* @brief This method indicates whether an expander device is in Discover
* process.
*
* @param[in] fw_device The framework smp device.
*
* @return Whether an expander device is in the middle of discovery process.
*/
BOOL scif_sas_smp_remote_device_is_in_activity(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
return(fw_device->protocol_device.smp_device.current_activity
!= SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE);
}
/**
* @brief This method search through the smp phy list of an expander to
* find a smp phy by its phy id of the expander.
*
* @param[in] phy_identifier The search criteria.
* @param[in] smp_remote_device The expander that owns the smp phy list.
*
* @return The found smp phy or a NULL pointer to indicate no smp phy is found.
*/
SCIF_SAS_SMP_PHY_T * scif_sas_smp_remote_device_find_smp_phy_by_id(
U8 phy_identifier,
SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device
)
{
SCI_FAST_LIST_ELEMENT_T * element = smp_remote_device->smp_phy_list.list_head;
SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
ASSERT(phy_identifier < smp_remote_device->smp_phy_list.number_of_phys);
while (element != NULL)
{
curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
element = sci_fast_list_get_next(element);
if (curr_smp_phy->phy_identifier == phy_identifier)
return curr_smp_phy;
}
return NULL;
}
/**
* @brief This method takes care of removing smp phy list of a smp devcie, which is
* about to be removed.
*
* @param[in] fw_device The expander device that is about to be removed.
*
* @return none.
*/
void scif_sas_smp_remote_device_removed(
SCIF_SAS_REMOTE_DEVICE_T * this_device
)
{
SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
&this_device->protocol_device.smp_device;
SCI_FAST_LIST_ELEMENT_T * element = smp_remote_device->smp_phy_list.list_head;
SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
SCIF_LOG_TRACE((
sci_base_object_get_logger(this_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_removed(0x%x) enter\n",
this_device
));
//remove all the smp phys in this device's smp_phy_list, and the conterpart smp phys
//in phy connections.
while (element != NULL)
{
curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
element = sci_fast_list_get_next(element);
scif_sas_smp_phy_destruct(curr_smp_phy);
}
this_device->protocol_device.smp_device.number_of_phys = 0;
this_device->protocol_device.smp_device.expander_route_indexes = 0;
this_device->protocol_device.smp_device.is_table_to_table_supported = FALSE;
this_device->protocol_device.smp_device.is_externally_configurable = FALSE;
this_device->protocol_device.smp_device.is_able_to_config_others = FALSE;
scif_sas_smp_remote_device_clear(this_device);
}
/**
* @brief This method takes care of terminated smp request to a smp device. The
* terminated smp request is most likely timeout and being aborted. A timeout
* maybe due to OPEN REJECT (NO DESTINATION).
*
* @param[in] fw_device The expander device that a timed out smp request towards to.
* @param[in] fw_request A failed smp request that is terminated by scic.
*
* @return none.
*/
void scif_sas_smp_remote_device_terminated_request_handler(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SCIF_SAS_REQUEST_T * fw_request
)
{
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_terminated_request_handler(0x%x, 0x%x) enter\n",
fw_device, fw_request
));
scif_sas_smp_remote_device_decode_smp_response(
fw_device, fw_request, NULL, SCI_IO_FAILURE_RETRY_REQUIRED
);
}
/**
* @brief This method allocates and populates the smp phy list of a expander device.
*
* @param[in] fw_device The expander device, whose smp phy list is to be populated after
* getting REPORT GENERAL response.
*
* @return none.
*/
void scif_sas_smp_remote_device_populate_smp_phy_list(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_SAS_SMP_PHY_T * this_smp_phy = NULL;
U8 expander_phy_id = 0;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_populate_smp_phy_list(0x%x) enter\n",
fw_device
));
for ( expander_phy_id = 0;
expander_phy_id < fw_device->protocol_device.smp_device.number_of_phys;
expander_phy_id++ )
{
this_smp_phy =
scif_sas_controller_allocate_smp_phy(fw_device->domain->controller);
ASSERT( this_smp_phy != NULL );
if ( this_smp_phy != NULL )
scif_sas_smp_phy_construct(this_smp_phy, fw_device, expander_phy_id);
}
}
/**
* @brief This method updates a smp phy of a expander device based on DISCOVER response.
*
* @param[in] fw_device The expander device, one of whose smp phys is to be updated.
* @param[in] discover_response The smp DISCOVER response.
*
* @return SCI_STATUS If a smp phy pair between expanders has invalid routing attribute,
* return SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION, otherwise,
* return SCI_SUCCESS
*/
SCI_STATUS scif_sas_smp_remote_device_save_smp_phy_info(
SCIF_SAS_REMOTE_DEVICE_T * fw_device,
SMP_RESPONSE_DISCOVER_T * discover_response
)
{
SCI_STATUS status = SCI_SUCCESS;
SCIF_SAS_SMP_PHY_T * smp_phy = NULL;
SCIF_SAS_REMOTE_DEVICE_T * attached_device = NULL;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_save_smp_phy_info(0x%x, 0x%x) enter\n",
fw_device, discover_response
));
smp_phy = scif_sas_smp_remote_device_find_smp_phy_by_id(
discover_response->phy_identifier,
&fw_device->protocol_device.smp_device
);
ASSERT( smp_phy != NULL );
//Note, attached_device could be NULL, not all the smp phy have to connected to a device.
attached_device = (SCIF_SAS_REMOTE_DEVICE_T *)
scif_domain_get_device_by_sas_address(
fw_device->domain, &discover_response->attached_sas_address);
scif_sas_smp_phy_save_information(
smp_phy, attached_device, discover_response);
//handle the special case of smp phys between expanders.
if ( discover_response->protocols.u.bits.attached_smp_target )
{
//this fw_device is a child expander, just found its parent expander.
//And there is no smp_phy constructed yet, record this phy connection.
if ( attached_device != NULL
&& attached_device == fw_device->containing_device )
{
//record the smp phy info, for this phy connects to a upstream smp device.
//the connection of a pair of smp phys are completed.
status = scif_sas_smp_phy_set_attached_phy(
smp_phy,
discover_response->attached_phy_identifier,
attached_device
);
if (status == SCI_SUCCESS)
{
//check the routing attribute for this phy and its containing device's
//expander_phy_routing_attribute.
if ( scif_sas_smp_phy_verify_routing_attribute(
smp_phy, smp_phy->u.attached_phy) != SCI_SUCCESS )
return SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION;
}
}
}
return status;
}
#ifdef SCI_SMP_PHY_LIST_DEBUG_PRINT
void scif_sas_smp_remote_device_print_smp_phy_list(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device = &fw_device->protocol_device.smp_device;
SCI_FAST_LIST_ELEMENT_T * element = smp_remote_device->smp_phy_list.list_head;
SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE,
"==========EXPANDER DEVICE (0x%x) smp phy list========== \n",
fw_device
));
while (element != NULL)
{
curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
element = sci_fast_list_get_next(element);
//print every thing about a smp phy
SCIF_LOG_ERROR((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE,
"SMP_PHY_%d (0x%x), attached device(0x%x), attached_sas_address(%x%x) attached_device_type(%d), routing_attribute(%d)\n",
curr_smp_phy->phy_identifier, curr_smp_phy,
curr_smp_phy->u.end_device,
curr_smp_phy->attached_sas_address.high, curr_smp_phy->attached_sas_address.low,
curr_smp_phy->attached_device_type,
curr_smp_phy->routing_attribute
));
}
}
#endif
/**
* @brief This method configure upstream expander(s)' (if there is any) route info.
*
* @param[in] this_device The expander device that is currently in discover process.
*
* @return none.
*/
void scif_sas_smp_remote_device_configure_upstream_expander_route_info(
SCIF_SAS_REMOTE_DEVICE_T * this_device
)
{
SCIF_SAS_REMOTE_DEVICE_T * curr_child_expander = this_device;
SCIF_SAS_REMOTE_DEVICE_T * curr_parent_expander =
scif_sas_remote_device_find_upstream_expander(this_device);
SCIF_SAS_REMOTE_DEVICE_T * curr_config_route_info_expander = NULL;
SCIF_LOG_TRACE((
sci_base_object_get_logger(this_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_configure_upstream_expander_route_info(0x%x) enter\n",
this_device
));
//traverse back to find root device.
while(curr_parent_expander != NULL )
{
//must set destination_smp_phy outside of find_upstream_expander() using the device
//that is just about to finish the discovery.
curr_parent_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy =
(SCIF_SAS_SMP_PHY_T*)sci_fast_list_get_object(
this_device->protocol_device.smp_device.smp_phy_list.list_head);
curr_child_expander = curr_parent_expander;
curr_parent_expander = scif_sas_remote_device_find_upstream_expander(curr_child_expander);
}
//found the root device: curr_child_expander. configure it and its downstream expander(s) till
//this_device or a self-configuring expander that configures others;
curr_config_route_info_expander = curr_child_expander;
while ( curr_config_route_info_expander != NULL
&& curr_config_route_info_expander != this_device
&& curr_config_route_info_expander->protocol_device.smp_device.scheduled_activity
== SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE
)
{
if (curr_config_route_info_expander->protocol_device.smp_device.is_externally_configurable)
{
SCIF_SAS_SMP_PHY_T * phy_being_config =
curr_config_route_info_expander->protocol_device.smp_device.config_route_smp_phy_anchor;
curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index =
phy_being_config->config_route_table_index_anchor;
if (curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index != 0)
curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index++;
curr_config_route_info_expander->protocol_device.smp_device.scheduled_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE;
//Find a downstream expander that has curr_config_route_destination_smp_phy.owning device
//same as curr_config_route_info_expander.
curr_config_route_info_expander = scif_sas_remote_device_find_downstream_expander(
curr_config_route_info_expander);
}
else if (curr_config_route_info_expander->protocol_device.smp_device.is_able_to_config_others)
{
//no need to config route table to this expander and its children.
//find its downstream expander and clear the planned config route table activity.
SCIF_SAS_REMOTE_DEVICE_T * curr_downstream_expander =
scif_sas_remote_device_find_downstream_expander(
curr_config_route_info_expander);
scif_sas_smp_remote_device_clear(curr_config_route_info_expander);
while ( curr_downstream_expander != NULL
&& curr_downstream_expander != this_device )
{
scif_sas_smp_remote_device_clear(curr_downstream_expander);
curr_downstream_expander =
scif_sas_remote_device_find_downstream_expander(
curr_config_route_info_expander);
}
break;
}
else
{
// current expander is a self-configuring expander, which is not externally
// configurable, and doesn't config others. we need to simply skip this expander.
curr_config_route_info_expander = scif_sas_remote_device_find_downstream_expander(
curr_config_route_info_expander);
}
}
}
/**
* @brief This method finds the immediate upstream expander of a given expander device.
*
* @param[in] this_device The given expander device, whose upstream expander is to be found.
*
* @return The immediate upstream expander. Or a NULL pointer if this_device is root already.
*/
SCIF_SAS_REMOTE_DEVICE_T * scif_sas_remote_device_find_upstream_expander(
SCIF_SAS_REMOTE_DEVICE_T * this_device
)
{
SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
&this_device->protocol_device.smp_device;
SCIF_SAS_REMOTE_DEVICE_T * upstream_expander = NULL;
SCI_FAST_LIST_ELEMENT_T * element = smp_remote_device->smp_phy_list.list_head;
SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
SCIF_LOG_TRACE((
sci_base_object_get_logger(this_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_configure_upstream_expander_route_info(0x%x) enter\n",
this_device
));
while (element != NULL)
{
curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
element = sci_fast_list_get_next(element);
if ( curr_smp_phy->routing_attribute == SUBTRACTIVE_ROUTING_ATTRIBUTE
&& ( curr_smp_phy->attached_device_type == SMP_EDGE_EXPANDER_DEVICE
|| curr_smp_phy->attached_device_type == SMP_FANOUT_EXPANDER_DEVICE)
&& curr_smp_phy->u.attached_phy != NULL
&& curr_smp_phy->u.attached_phy->routing_attribute == TABLE_ROUTING_ATTRIBUTE )
{
//set the current_activity and current_config_route_index for that
//upstream expander.
upstream_expander = curr_smp_phy->u.attached_phy->owning_device;
upstream_expander->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_CONFIGURE_ROUTE_INFORMATION;
//if the upstream_expander's config route table method is config phy0 only or
//config all phys, the current activity phy is found.
upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
scif_sas_smp_remote_device_find_smp_phy_by_id(
curr_smp_phy->u.attached_phy->phy_identifier,
&(curr_smp_phy->u.attached_phy->owning_device->protocol_device.smp_device)
);
//if the upstream_expander's config route table method is config middle phy only
//config highest phy only, the current activity phy needs a update.
if ( scif_sas_smp_remote_device_get_config_route_table_method(upstream_expander)
== SCIF_SAS_CONFIG_ROUTE_TABLE_MIDDLE_PHY_ONLY )
{
upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
scif_sas_smp_phy_find_middle_phy_in_wide_port (
upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor
);
}
else if ( scif_sas_smp_remote_device_get_config_route_table_method(upstream_expander)
== SCIF_SAS_CONFIG_ROUTE_TABLE_HIGHEST_PHY_ONLY )
{
upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
scif_sas_smp_phy_find_highest_phy_in_wide_port (
upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor
);
}
upstream_expander->protocol_device.smp_device.current_activity_phy_index =
upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier;
return upstream_expander;
}
}
return NULL;
}
/**
* @brief This method finds the immediate downstream expander of a given expander device.
*
* @param[in] this_device The given expander device, whose downstream expander is to be found.
*
* @return The immediate downstream expander. Or a NULL pointer if there is none.
*/
SCIF_SAS_REMOTE_DEVICE_T * scif_sas_remote_device_find_downstream_expander(
SCIF_SAS_REMOTE_DEVICE_T * this_device
)
{
SCIF_SAS_SMP_REMOTE_DEVICE_T * this_smp_remote_device =
&this_device->protocol_device.smp_device;
SCIF_SAS_REMOTE_DEVICE_T * downstream_expander = NULL;
SCI_FAST_LIST_ELEMENT_T * element = this_smp_remote_device->smp_phy_list.list_head;
SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
SCIF_LOG_TRACE((
sci_base_object_get_logger(this_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_remote_device_find_downstream_expander(0x%x) enter\n",
this_device
));
while (element != NULL)
{
curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
element = sci_fast_list_get_next(element);
if ( curr_smp_phy->routing_attribute == TABLE_ROUTING_ATTRIBUTE
&& curr_smp_phy->attached_device_type == SMP_EDGE_EXPANDER_DEVICE
&& curr_smp_phy->u.attached_phy != NULL)
{
//set the current_activity and current_config_route_index for that
//upstream expander.
downstream_expander = curr_smp_phy->u.attached_phy->owning_device;
if ( downstream_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy != NULL
&& downstream_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy->owning_device ==
this_smp_remote_device->curr_config_route_destination_smp_phy->owning_device )
return downstream_expander;
}
}
return NULL;
}
/**
* @brief This method follows route table optimization rule to check if a destination_device
* should be recorded in the device_being_config's route table
*
* @param[in] device_being_config The upstream expander device, whose route table is being configured.
* @param[in] destination_smp_phy A smp phy whose attached device is potentially to be
* recorded in route table.
*
* @return BOOL This method returns TRUE if a destination_device should be recorded in route table.
* This method returns FALSE if a destination_device need not to be recorded
* in route table.
*/
BOOL scif_sas_smp_remote_device_do_config_route_info(
SCIF_SAS_REMOTE_DEVICE_T * device_being_config,
SCIF_SAS_SMP_PHY_T * destination_smp_phy
)
{
SCI_SAS_ADDRESS_T device_being_config_sas_address;
SCIF_LOG_TRACE((
sci_base_object_get_logger(device_being_config),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_do_config_route_info(0x%x, 0x%x) enter\n",
device_being_config, destination_smp_phy
));
scic_remote_device_get_sas_address(
device_being_config->core_object, &device_being_config_sas_address
);
//refer to SAS-2 spec 4.8.3, rule (b)
if ((destination_smp_phy->attached_sas_address.low == 0
&& destination_smp_phy->attached_sas_address.high == 0)
&& (destination_smp_phy->attached_device_type == SMP_NO_DEVICE_ATTACHED))
{
return FALSE;
}
//refer to SAS-2 spec 4.8.3, rule (c), self-referencing.
if (destination_smp_phy->attached_sas_address.high ==
device_being_config_sas_address.high
&& destination_smp_phy->attached_sas_address.low ==
device_being_config_sas_address.low)
{
return FALSE;
}
//There will be no cases that falling into rule (a), (d), (e) to be excluded,
//based on our current mechanism of cofig route table.
return TRUE;
}
/**
* @brief This method configures device_being_config's route table for all the enclosed devices in
* a downstream smp device, destination_device.
*
* @param[in] device_being_config The upstream expander device, whose route table is being configured.
*
* @return None
*/
void scif_sas_smp_remote_device_configure_route_table(
SCIF_SAS_REMOTE_DEVICE_T * device_being_config
)
{
//go through the smp phy list of this_device.
SCI_FAST_LIST_ELEMENT_T * element =
&(device_being_config->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element);
SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
SCIF_LOG_TRACE((
sci_base_object_get_logger(device_being_config),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_configure_route_table(0x%x) enter\n",
device_being_config
));
device_being_config->protocol_device.smp_device.current_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE;
while (element != NULL)
{
curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
element = sci_fast_list_get_next(element);
//check if this phy needs to be added to the expander's route table.
if (scif_sas_smp_remote_device_do_config_route_info(
device_being_config, curr_smp_phy) == TRUE )
{
SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
&device_being_config->protocol_device.smp_device;
smp_remote_device->curr_config_route_destination_smp_phy =
curr_smp_phy;
//Then config this_device's route table entry at the phy and next route_index.
//send config_route_info using curr_smp_phy.phy_identifier and sas_address.
scif_sas_smp_request_construct_config_route_info(
device_being_config->domain->controller,
device_being_config,
smp_remote_device->current_activity_phy_index,
smp_remote_device->curr_config_route_index,
curr_smp_phy->attached_sas_address,
FALSE
);
//schedule the DPC.
scif_cb_start_internal_io_task_schedule(
device_being_config->domain->controller,
scif_sas_controller_start_high_priority_io,
device_being_config->domain->controller
);
//stop here, we need to wait for config route info's response then send
//the next one.
break;
}
}
}
/**
* @brief This method walks through an expander's route table to clean table
* attribute phys' route entries. This routine finds one table entry
* to clean and will be called repeatly till it finishes cleanning the
* whole table.
*
* @param[in] fw_device The expander device, whose route table entry is to be cleaned.
*
* @return None.
*/
void scif_sas_smp_remote_device_clean_route_table(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_SAS_SMP_PHY_T * smp_phy_being_config;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_clean_route_table(0x%x) enter\n",
fw_device
));
//from anchors, start to clean all the other route table entries.
fw_device->protocol_device.smp_device.curr_config_route_index++;
if ( fw_device->protocol_device.smp_device.curr_config_route_index >=
fw_device->protocol_device.smp_device.expander_route_indexes )
{
fw_device->protocol_device.smp_device.curr_config_route_index = 0;
do //find next table attribute PHY.
{
fw_device->protocol_device.smp_device.current_activity_phy_index++;
if (fw_device->protocol_device.smp_device.current_activity_phy_index ==
fw_device->protocol_device.smp_device.number_of_phys)
fw_device->protocol_device.smp_device.current_activity_phy_index=0;
//phy_index changed, so update the smp_phy_being_config.
smp_phy_being_config =
scif_sas_smp_remote_device_find_smp_phy_by_id(
fw_device->protocol_device.smp_device.current_activity_phy_index,
&(fw_device->protocol_device.smp_device)
);
} while( smp_phy_being_config->routing_attribute != TABLE_ROUTING_ATTRIBUTE );
if ( smp_phy_being_config->phy_identifier !=
fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier)
{
if (smp_phy_being_config->config_route_table_index_anchor != 0)
fw_device->protocol_device.smp_device.curr_config_route_index =
smp_phy_being_config->config_route_table_index_anchor + 1;
else
fw_device->protocol_device.smp_device.curr_config_route_index = 0;
}
}
if ( !(fw_device->protocol_device.smp_device.current_activity_phy_index ==
fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier
&& fw_device->protocol_device.smp_device.curr_config_route_index == 0)
)
{
//clean this route entry.
scif_sas_smp_remote_device_clean_route_table_entry(fw_device);
}
else
{
fw_device->protocol_device.smp_device.is_route_table_cleaned = TRUE;
//set this device's activity to NON.
fw_device->protocol_device.smp_device.current_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
//we need to notify domain that this device finished config route table, domain
//may pick up other activities (i.e. Discover) for other expanders.
scif_sas_domain_continue_discover(fw_device->domain);
}
}
/**
* @brief This method cleans a device's route table antry.
*
* @param[in] fw_device The expander device, whose route table entry is to be cleaned.
*
* @return None.
*/
void scif_sas_smp_remote_device_clean_route_table_entry(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCI_SAS_ADDRESS_T empty_sas_address;
SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
&(fw_device->protocol_device.smp_device);
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_clean_route_table(0x%x) enter\n",
fw_device
));
empty_sas_address.high = 0;
empty_sas_address.low = 0;
scif_sas_smp_request_construct_config_route_info(
fw_device->domain->controller,
fw_device,
smp_remote_device->current_activity_phy_index,
smp_remote_device->curr_config_route_index,
empty_sas_address,
TRUE
);
//schedule the DPC.
scif_cb_start_internal_io_task_schedule(
fw_device->domain->controller,
scif_sas_controller_start_high_priority_io,
fw_device->domain->controller
);
}
/**
* @brief This method handles the case of exceeding route index when config route table
* for a device, by removing the attached device of current config route
* destination smp phy and the rest of smp phys in the same smp phy list.
*
* @param[in] fw_device The expander device, whose route table to be edited but failed
* with a SMP function result of INDEX DOES NOT EXIST.
*
* @return None.
*/
void scif_sas_smp_remote_device_cancel_config_route_table_activity(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
//go through the rest of the smp phy list of destination device.
SCI_FAST_LIST_ELEMENT_T * element =
&(fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element);
SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
SCIF_SAS_REMOTE_DEVICE_T * curr_attached_device = NULL;
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_cancel_config_route_table_activity(0x%x) enter\n",
fw_device
));
while (element != NULL)
{
curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
element = sci_fast_list_get_next(element);
//check if this phy needs to be added to the expander's route table but can't due to
//exceeding max route index.
if (scif_sas_smp_remote_device_do_config_route_info(
fw_device, curr_smp_phy) == TRUE )
{
//set the is_currently_discovered to FALSE for attached device. Then when
//domain finish discover, domain will remove this device.
curr_attached_device = (SCIF_SAS_REMOTE_DEVICE_T *)
scif_domain_get_device_by_sas_address(
fw_device->domain, &(curr_smp_phy->attached_sas_address));
if (curr_attached_device != NULL)
curr_attached_device->is_currently_discovered = FALSE;
}
}
}
/**
* @brief This method cancel current activity and terminate the outstanding internal IO
* if there is one.
*
* @param[in] fw_device The expander device, whose smp activity is to be canceled.
*
* @return None.
*/
void scif_sas_smp_remote_device_cancel_smp_activity(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
SCIF_LOG_TRACE((
sci_base_object_get_logger(fw_device),
SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
"scif_sas_smp_remote_device_cancel_smp_activity(0x%x) enter\n",
fw_device
));
//Terminate all of the requests in the silicon for this device.
scif_sas_domain_terminate_requests(
fw_device->domain, fw_device, NULL, NULL
);
if (fw_device->protocol_device.smp_device.current_activity ==
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE)
scif_sas_smp_remote_device_cancel_config_route_table_activity(fw_device);
//Clear the device to stop the smp sctivity.
scif_sas_smp_remote_device_clear(fw_device);
}
/**
* @brief This method tells the way to configure route table for a expander. The
* possible ways are: configure phy 0's route table, configure middle
* phy's route table, configure highest order phy's route table,
* configure all phys.
*
* @param[in] fw_device The expander device, whose config route table method is
* to be chosen.
*
* @return one in 4 possible options.
*/
U8 scif_sas_smp_remote_device_get_config_route_table_method(
SCIF_SAS_REMOTE_DEVICE_T * fw_device
)
{
U8 config_route_table_method;
//config_route_table_method = SCIF_SAS_CONFIG_ROUTE_TABLE_MIDDLE_PHY_ONLY;
config_route_table_method = SCIF_SAS_CONFIG_ROUTE_TABLE_ALL_PHYS;
return config_route_table_method;
}
/**
* @brief This method starts the EA target reset process by constructing
* and starting a PHY CONTROL (hard reset) smp request.
*
* @param[in] expander_device The expander device, to which a PHY Control smp command is
* sent.
* @param[in] target_device The expander attahced target device, to which the target reset
* request is sent.
* @param[in] fw_request The target reset task request.
*
* @return none
*/
void scif_sas_smp_remote_device_start_target_reset(
SCIF_SAS_REMOTE_DEVICE_T * expander_device,
SCIF_SAS_REMOTE_DEVICE_T * target_device,
SCIF_SAS_REQUEST_T * fw_request
)
{
SCIF_SAS_CONTROLLER_T * fw_controller = expander_device->domain->controller;
//set current_activity and current_smp_request to expander device.
expander_device->protocol_device.smp_device.current_activity =
SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET;
expander_device->protocol_device.smp_device.current_smp_request =
SMP_FUNCTION_PHY_CONTROL;
expander_device->protocol_device.smp_device.current_activity_phy_index =
target_device->expander_phy_identifier;
//A Phy Control smp request has been constructed towards parent device.
//Walk the high priority io path.
fw_controller->state_handlers->start_high_priority_io_handler(
(SCI_BASE_CONTROLLER_T*) fw_controller,
(SCI_BASE_REMOTE_DEVICE_T*) expander_device,
(SCI_BASE_REQUEST_T*) fw_request,
SCI_CONTROLLER_INVALID_IO_TAG
);
}