diff options
Diffstat (limited to 'sys/dev/isci/scil/scif_sas_smp_remote_device.c')
-rw-r--r-- | sys/dev/isci/scil/scif_sas_smp_remote_device.c | 2632 |
1 files changed, 2632 insertions, 0 deletions
diff --git a/sys/dev/isci/scil/scif_sas_smp_remote_device.c b/sys/dev/isci/scil/scif_sas_smp_remote_device.c new file mode 100644 index 0000000..6df955a --- /dev/null +++ b/sys/dev/isci/scil/scif_sas_smp_remote_device.c @@ -0,0 +1,2632 @@ +/*- + * 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; + 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_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; + + attached_remote_device = (SCIF_SAS_REMOTE_DEVICE_T *) + scif_domain_get_device_by_sas_address( + fw_domain, &attached_device_address + ); + + // the device should have already existed in the domian. + ASSERT (attached_remote_device != 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; + + 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_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); + + if (smp_phy != NULL) + { + 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_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) + ); + + 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 + ); +} + + |