/*- * 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 __FBSDID("$FreeBSD$"); /** * @file * * @brief This file contains the methods for the SCIF_SAS_SMP_REMOTE_DEVICE object. */ #include #include #include #include #include #include #include #include #include #include /** * @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 ); }