/** * @brief This method processes an unsolicited frame while the task mgmt * request is waiting for a response frame. It will copy the * response data, release the unsolicited frame, and transition * the request to the SCI_BASE_REQUEST_STATE_COMPLETED state. * * @param[in] this_request This parameter specifies the request for which * the unsolicited frame was received. * @param[in] frame_index This parameter indicates the unsolicited frame * index that should contain the response. * * @return This method returns an indication of whether the TC response * frame was handled successfully or not. * @retval SCI_SUCCESS Currently this value is always returned and indicates * successful processing of the TC response. * * @todo Should probably update to check frame type and make sure it is * a response frame. */ static SCI_STATUS scic_sds_ssp_task_request_await_tc_response_frame_handler( SCIC_SDS_REQUEST_T * this_request, U32 frame_index ) { // Save off the controller, so that we do not touch the request after it // is completed. SCIC_SDS_CONTROLLER_T * owning_controller = this_request->owning_controller; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_TASK_MANAGEMENT, "scic_sds_ssp_task_request_await_tc_response_frame_handler(0x%x, 0x%x) enter\n", this_request, frame_index )); scic_sds_io_request_copy_response(this_request); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); scic_sds_controller_release_frame( owning_controller, frame_index ); return SCI_SUCCESS; }
/** * This timer routine is used to allow the SCI User to rediscover or change * device objects before a new series of link up notifications because a * link down has allowed a better port configuration. * * @param[in] controller This is the core controller object which is used * to obtain the port configuration agent. */ static void scic_sds_mpc_agent_timeout_handler( void * object ) { U8 index; SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object; SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent = &controller->port_agent; U16 configure_phy_mask; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_mpc_agent_timeout_handler(0x%08x) enter\n", controller )); port_agent->timer_pending = FALSE; // Find the mask of phys that are reported read but as yet unconfigured into a port configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask; for (index = 0; index < SCI_MAX_PHYS; index++) { if (configure_phy_mask & (1 << index)) { port_agent->link_up_handler( controller, port_agent, scic_sds_phy_get_port(&controller->phy_table[index]), &controller->phy_table[index] ); } } }
/** * @brief This method is responsible for processing a terminate/abort * request for this TC while the request is waiting for the task * management response unsolicited frame. * * @param[in] this_request This parameter specifies the request for which * the termination was requested. * * @return This method returns an indication as to whether the abort * request was successfully handled. * * @todo need to update to ensure the received UF doesn't cause damage * to subsequent requests (i.e. put the extended tag in a holding * pattern for this particular device). */ static SCI_STATUS scic_sds_ssp_task_request_await_tc_response_abort_handler( SCI_BASE_REQUEST_T * request ) { SCIC_SDS_REQUEST_T *this_request = (SCIC_SDS_REQUEST_T *)request; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_TASK_MANAGEMENT, "scic_sds_ssp_task_request_await_tc_response_abort_handler(0x%x) enter\n", this_request )); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_ABORTING ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); return SCI_SUCCESS; }
/** * This method handles the manual port configuration link up notifications. * Since all ports and phys are associate at initialization time we just turn * around and notifiy the port object that there is a link up. If this PHY is * not associated with a port there is no action taken. * * @param[in] controller This is the controller object that receives the * link up notification. * @param[in] port This is the port object associated with the phy. If the * is no associated port this is an SCI_INVALID_HANDLE. * @param[in] phy This is the phy object which has gone ready. * * @note Is it possible to get a link up notification from a phy that has * no assocoated port? */ static void scic_sds_mpc_agent_link_up( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PORT_T * port, SCIC_SDS_PHY_T * phy ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_mpc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, port, phy )); // If the port has an invalid handle then the phy was not assigned to // a port. This is because the phy was not given the same SAS Address // as the other PHYs in the port. if (port != SCI_INVALID_HANDLE) { port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy)); scic_sds_port_link_up(port, phy); if ((port->active_phy_mask & (1 << scic_sds_phy_get_index(phy))) != 0) { port_agent->phy_configured_mask |= (1 << scic_sds_phy_get_index(phy)); } } }
/** * This method handles the automatic port configuration link down notifications. * If this PHY is * not associated with a port there is no action taken. * * @param[in] controller This is the controller object that receives the * link down notification. * @param[in] port This is the port object associated with the phy. If the * is no associated port this is an SCI_INVALID_HANDLE. * @param[in] phy This is the phy object which has gone link down. * * @note Is it possible to get a link down notification from a phy that has * no assocoated port? */ static void scic_sds_apc_agent_link_down( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PORT_T * port, SCIC_SDS_PHY_T * phy ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_apc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, port, phy )); port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy)); if (port != SCI_INVALID_HANDLE) { if (port_agent->phy_configured_mask & (1 << phy->phy_index)) { SCI_STATUS status; status = scic_sds_port_remove_phy(port, phy); if (status == SCI_SUCCESS) { port_agent->phy_configured_mask &= ~(1 << phy->phy_index); } } } }
/** * @brief This method is called by the SCI user to build an SMP pass-through * IO request. * * @pre * - The user must have previously called scic_io_request_construct() * on the supplied IO request. * * @param[in] scic_smp_request This parameter specifies the handle to the * io request object to be built. * * @param[in] passthru_cb This parameter specifies the pointer to the callback * structure that contains the function pointers * * @return Indicate if the controller successfully built the IO request. */ SCI_STATUS scic_io_request_construct_smp_pass_through( SCI_IO_REQUEST_HANDLE_T scic_smp_request, SCIC_SMP_PASSTHRU_REQUEST_CALLBACKS_T *passthru_cb ) { SMP_REQUEST_T smp_request; U8 * request_buffer; U32 request_buffer_length_in_bytes; SCIC_SDS_REQUEST_T *this_request = (SCIC_SDS_REQUEST_T *) scic_smp_request; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_SMP_IO_REQUEST, "scic_io_request_construct_smp_pass_through(0x%x) enter\n", this_request )); this_request->protocol = SCIC_SMP_PROTOCOL; this_request->has_started_substate_machine = TRUE; // Call the callback function to retrieve the SMP passthrough request request_buffer_length_in_bytes = passthru_cb->scic_cb_smp_passthru_get_request ( (void *)this_request, &request_buffer ); //copy the request to smp request memcpy((char *)&smp_request.request.vendor_specific_request, request_buffer, request_buffer_length_in_bytes); //the header length in smp_request is in dwords - the sas spec has similar way, //but the csmi header contains the number of bytes, so we need to convert the //number of bytes to number of dwords smp_request.header.request_length = (U8) (request_buffer_length_in_bytes / sizeof (U32)); //Grab the other needed fields from the smp request using callbacks smp_request.header.smp_frame_type = passthru_cb->scic_cb_smp_passthru_get_frame_type ((void *)this_request); smp_request.header.function = passthru_cb->scic_cb_smp_passthru_get_function ((void *)this_request); smp_request.header.allocated_response_length = passthru_cb->scic_cb_smp_passthru_get_allocated_response_length((void *)this_request); // Construct the started sub-state machine. sci_base_state_machine_construct( &this_request->started_substate_machine, &this_request->parent.parent, scic_sds_smp_request_started_substate_table, SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_RESPONSE ); // Construct the SMP SCU Task Context scu_smp_request_construct_task_context (this_request, &smp_request); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_CONSTRUCTED ); return SCI_SUCCESS; }
/** * This method will construct the port configuration agent for this controller. * * @param[in] controller This is the controller object for which the port * agent is being initialized. * * @param[in] port_agent This is the port configuration agent that is being * initialized. The initialization path is handled differntly * for the automatic port configuration agent and the manual port * configuration agent. * * @return */ SCI_STATUS scic_sds_port_configuration_agent_initialize( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { SCI_STATUS status = SCI_SUCCESS; enum SCIC_PORT_CONFIGURATION_MODE mode; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_port_configuration_agent_initialize(0x%08x, 0x%08x) enter\n", controller, port_agent )); mode = controller->oem_parameters.sds1.controller.mode_type; if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) { status = scic_sds_mpc_agent_validate_phy_configuration(controller, port_agent); port_agent->link_up_handler = scic_sds_mpc_agent_link_up; port_agent->link_down_handler = scic_sds_mpc_agent_link_down; port_agent->timer = scic_cb_timer_create( controller, scic_sds_mpc_agent_timeout_handler, controller ); } else { status = scic_sds_apc_agent_validate_phy_configuration(controller, port_agent); port_agent->link_up_handler = scic_sds_apc_agent_link_up; port_agent->link_down_handler = scic_sds_apc_agent_link_down; port_agent->timer = scic_cb_timer_create( controller, scic_sds_apc_agent_timeout_handler, controller ); } // Make sure we have actually gotten a timer if (status == SCI_SUCCESS && port_agent->timer == NULL) { SCIC_LOG_ERROR(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER, "Controller 0x%x automatic port configuration agent could not get timer.\n", controller )); status = SCI_FAILURE; } return status; }
/** * This routine will verify that the phys are assigned a valid SAS address for * automatic port configuration mode. */ static SCI_STATUS scic_sds_apc_agent_validate_phy_configuration( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { U8 phy_index; U8 port_index; SCI_SAS_ADDRESS_T sas_address; SCI_SAS_ADDRESS_T phy_assigned_address; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_apc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n", controller, port_agent )); phy_index = 0; while (phy_index < SCI_MAX_PHYS) { port_index = phy_index; // Get the assigned SAS Address for the first PHY on the controller. scic_sds_phy_get_sas_address( &controller->phy_table[phy_index], &sas_address ); while (++phy_index < SCI_MAX_PHYS) { scic_sds_phy_get_sas_address( &controller->phy_table[phy_index], &phy_assigned_address ); // Verify each of the SAS address are all the same for every PHY if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) { port_agent->phy_valid_port_range[phy_index].min_index = port_index; port_agent->phy_valid_port_range[phy_index].max_index = phy_index; } else { port_agent->phy_valid_port_range[phy_index].min_index = phy_index; port_agent->phy_valid_port_range[phy_index].max_index = phy_index; break; } } } return scic_sds_port_configuration_agent_validate_ports(controller, port_agent); }
/** * This routine will try to configure the phys into ports when the timer fires. * * @param[in] object This is actually the controller that needs to have the * pending phys configured. */ static void scic_sds_apc_agent_timeout_handler( void * object ) { U32 index; SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent; SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object; U16 configure_phy_mask; port_agent = scic_sds_controller_get_port_configuration_agent(controller); SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_apc_agent_timeout_handler(0x%08x) enter\n", controller )); port_agent->timer_pending = FALSE; configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask; if (configure_phy_mask != 0x00) { for (index = 0; index < SCI_MAX_PHYS; index++) { if (configure_phy_mask & (1 << index)) { scic_sds_apc_agent_configure_ports( controller, port_agent, &controller->phy_table[index], FALSE ); } } //Notify the controller ports are configured. if ( (port_agent->phy_ready_mask == port_agent->phy_configured_mask) && (controller->next_phy_to_start == SCI_MAX_PHYS) && (controller->phy_startup_timer_pending == FALSE) ) { // The controller has successfully finished the start process. // Inform the SCI Core user and transition to the READY state. if (scic_sds_controller_is_start_complete(controller) == TRUE) { scic_sds_controller_port_agent_configured_ports(controller); } } } }
/** * This routine will find a matching port for the phy. This means that the * port and phy both have the same broadcast sas address and same received * sas address. * * @param[in] controller The controller object used for the port search. * @param[in] phy The phy object to match. * * @return The port address or the SCI_INVALID_HANDLE if there is no matching * port. * * @retvalue port address if the port can be found to match the phy. * @retvalue SCI_INVALID_HANDLE if there is no matching port for the phy. */ static SCIC_SDS_PORT_T * scic_sds_port_configuration_agent_find_port( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PHY_T * phy ) { U8 port_index; SCI_PORT_HANDLE_T port_handle; SCI_SAS_ADDRESS_T port_sas_address; SCI_SAS_ADDRESS_T port_attached_device_address; SCI_SAS_ADDRESS_T phy_sas_address; SCI_SAS_ADDRESS_T phy_attached_device_address; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_port_confgiruation_agent_find_port(0x%08x, 0x%08x) enter\n", controller, phy )); // Since this phy can be a member of a wide port check to see if one or // more phys match the sent and received SAS address as this phy in which // case it should participate in the same port. scic_sds_phy_get_sas_address(phy, &phy_sas_address); scic_sds_phy_get_attached_sas_address(phy, &phy_attached_device_address); for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) { if (scic_controller_get_port_handle(controller, port_index, &port_handle) == SCI_SUCCESS) { SCIC_SDS_PORT_T * port = (SCIC_SDS_PORT_T *)port_handle; scic_sds_port_get_sas_address(port, &port_sas_address); scic_sds_port_get_attached_sas_address(port, &port_attached_device_address); if ( (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0) && (sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0) ) { return port; } } } return SCI_INVALID_HANDLE; }
/** * This method handles the manual port configuration link down notifications. * Since all ports and phys are associated at initialization time we just turn * around and notifiy the port object of the link down event. If this PHY is * not associated with a port there is no action taken. * * @param[in] controller This is the controller object that receives the * link down notification. * @param[in] port This is the port object associated with the phy. If the * is no associated port this is an SCI_INVALID_HANDLE. The port * is an invalid handle only if the phy was never port of this * port. This happens when the phy is not broadcasting the same * SAS address as the other phys in the assigned port. * @param[in] phy This is the phy object which has gone link down. * * @note Is it possible to get a link down notification from a phy that has * no assocoated port? */ static void scic_sds_mpc_agent_link_down( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PORT_T * port, SCIC_SDS_PHY_T * phy ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_mpc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, port, phy )); if (port != SCI_INVALID_HANDLE) { // If we can form a new port from the remainder of the phys then we want // to start the timer to allow the SCI User to cleanup old devices and // rediscover the port before rebuilding the port with the phys that // remain in the ready state. port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy)); port_agent->phy_configured_mask &= ~(1 << scic_sds_phy_get_index(phy)); // Check to see if there are more phys waiting to be configured into a port. // If there are allow the SCI User to tear down this port, if necessary, and // then reconstruc the port after the timeout. if ( (port_agent->phy_configured_mask == 0x0000) && (port_agent->phy_ready_mask != 0x0000) && !port_agent->timer_pending ) { port_agent->timer_pending = TRUE; scic_cb_timer_start( controller, port_agent->timer, SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT ); } scic_sds_port_link_down(port, phy); } }
/** * This method handles the automatic port configuration for link up notifications. * * @param[in] controller This is the controller object that receives the * link up notification. * @param[in] port This is the port object associated with the phy. If the * is no associated port this is an SCI_INVALID_HANDLE. * @param[in] phy This is the phy object which has gone link up. * * @note Is it possible to get a link down notification from a phy that has * no assocoated port? */ static void scic_sds_apc_agent_link_up( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PORT_T * port, SCIC_SDS_PHY_T * phy ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_apc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, port, phy )); //the phy is not the part of this port, configure the port with this phy if (port == SCI_INVALID_HANDLE) { port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy)); scic_sds_apc_agent_start_timer( controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION ); } else { //the phy is already the part of the port //if the PORT'S state is resetting then the link up is from port hard reset //in this case, we need to tell the port that link up is recieved if ( SCI_BASE_PORT_STATE_RESETTING == port->parent.state_machine.current_state_id ) { //notify the port that port needs to be ready port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy)); scic_sds_port_link_up(port, phy); } else { ASSERT (0); } } }
/** * @brief This method processes the completions transport layer (TL) status * to determine if the SMP request was sent successfully. If the SMP * request was sent successfully, then the state for the SMP request * transits to waiting for a response frame. * * @param[in] this_request This parameter specifies the request for which * the TC completion was received. * @param[in] completion_code This parameter indicates the completion status * information for the TC. * * @return Indicate if the tc completion handler was successful. * @retval SCI_SUCCESS currently this method always returns success. */ static SCI_STATUS scic_sds_smp_request_await_tc_completion_tc_completion_handler( SCIC_SDS_REQUEST_T * this_request, U32 completion_code ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_SMP_IO_REQUEST, "scic_sds_smp_request_await_tc_completion_tc_completion_handler(0x%x, 0x%x) enter\n", this_request, completion_code )); switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): scic_sds_request_set_status( this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); break; default: // All other completion status cause the IO to be complete. If a NAK // was received, then it is up to the user to retry the request. scic_sds_request_set_status( this_request, SCU_NORMALIZE_COMPLETION_STATUS(completion_code), SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); break; } return SCI_SUCCESS; }
/** * @brief This method release resources in for a scic port configuration agent. * * @param[in] controller This parameter specifies the core controller, one of * its phy's resources are to be released. * @param[in] this_phy This parameter specifies the phy whose resource is to * be released. */ void scic_sds_port_configuration_agent_release_resource( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_PORT, "scic_sds_port_configuration_agent_release_resource(0x%x, 0x%x)\n", controller, port_agent )); //Currently, the only resource to be released is a timer. if (port_agent->timer != NULL) { scic_cb_timer_destroy(controller, port_agent->timer); port_agent->timer = NULL; } }
/** * This routine will restart the automatic port configuration timeout * timer for the next time period. This could be caused by either a * link down event or a link up event where we can not yet tell to which * port a phy belongs. * * @param[in] controller This is the controller that to which the port * agent is assigned. * @param[in] port_agent This is the port agent that is requesting the * timer start operation. * @param[in] phy This is the phy that has caused the timer operation to * be scheduled. * @param[in] timeout This is the timeout in ms. */ static void scic_sds_apc_agent_start_timer( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PHY_T * phy, U32 timeout ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_apc_agent_start_timer(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n", controller, port_agent, phy, timeout )); if (port_agent->timer_pending) { scic_cb_timer_stop(controller, port_agent->timer); } port_agent->timer_pending = TRUE; scic_cb_timer_start(controller, port_agent->timer, timeout); }
/** * @brief This method is called by the SCI user to build an SMP * IO request. * * @pre * - The user must have previously called scic_io_request_construct() * on the supplied IO request. * * @param[in] scic_io_request This parameter specifies the handle to the * io request object to be built. * * @return Indicate if the controller successfully built the IO request. * @retval SCI_SUCCESS This value is returned if the IO request was * successfully built. * @retval SCI_FAILURE_UNSUPPORTED_PROTOCOL This value is returned if the * remote_device does not support the SMP protocol. * @retval SCI_FAILURE_INVALID_ASSOCIATION This value is returned if the * user did not properly set the association between the SCIC IO * request and the user's IO request. Please refer to the * sci_object_set_association() routine for more * information. */ SCI_STATUS scic_io_request_construct_smp( SCI_IO_REQUEST_HANDLE_T scic_smp_request ) { SMP_REQUEST_T smp_request; SCIC_SDS_REQUEST_T *this_request = (SCIC_SDS_REQUEST_T *) scic_smp_request; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_SMP_IO_REQUEST, "scic_io_request_construct_smp(0x%x) enter\n", this_request )); this_request->protocol = SCIC_SMP_PROTOCOL; this_request->has_started_substate_machine = TRUE; // Construct the started sub-state machine. sci_base_state_machine_construct( &this_request->started_substate_machine, &this_request->parent.parent, scic_sds_smp_request_started_substate_table, SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_RESPONSE ); // Construct the SMP SCU Task Context memcpy((char *)&smp_request, this_request->command_buffer, sizeof(SMP_REQUEST_T)); // Look at the SMP requests' header fields; for certain SAS 1.x SMP // functions under SAS 2.0, a zero request length really indicates // a non-zero default length. if( smp_request.header.request_length == 0 ) { switch( smp_request.header.function ) { case SMP_FUNCTION_DISCOVER: case SMP_FUNCTION_REPORT_PHY_ERROR_LOG: case SMP_FUNCTION_REPORT_PHY_SATA: case SMP_FUNCTION_REPORT_ROUTE_INFORMATION: smp_request.header.request_length = 2; break; case SMP_FUNCTION_CONFIGURE_ROUTE_INFORMATION: case SMP_FUNCTION_PHY_CONTROL: case SMP_FUNCTION_PHY_TEST: smp_request.header.request_length = 9; break; // Default - zero is a valid default for 2.0. } } scu_smp_request_construct_task_context( this_request, &smp_request ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_CONSTRUCTED ); return SCI_SUCCESS; }
/** * @brief This method will fill in the SCU Task Context for a SMP request. The * following important settings are utilized: * * -# task_type == SCU_TASK_TYPE_SMP. This simply indicates * that a normal request type (i.e. non-raw frame) is being * utilized to perform task management. * -# control_frame == 1. This ensures that the proper endianess * is set so that the bytes are transmitted in the right order * for a smp request frame. * * @param[in] this_request This parameter specifies the smp request object * being constructed. * * @return none */ void scu_smp_request_construct_task_context( SCIC_SDS_REQUEST_T *this_request, SMP_REQUEST_T *smp_request ) { SCI_PHYSICAL_ADDRESS physical_address; SCIC_SDS_CONTROLLER_T *owning_controller; SCIC_SDS_REMOTE_DEVICE_T *target_device; SCIC_SDS_PORT_T *target_port; SCU_TASK_CONTEXT_T *task_context; //byte swap the smp request. scic_word_copy_with_swap( this_request->command_buffer, (U32*) smp_request, sizeof(SMP_REQUEST_T)/sizeof(U32) ); task_context = scic_sds_request_get_task_context(this_request); owning_controller = scic_sds_request_get_controller(this_request); target_device = scic_sds_request_get_device(this_request); target_port = scic_sds_request_get_port(this_request); SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_SMP_IO_REQUEST, "scu_smp_request_construct_task_context(0x%x) contents\n" " reqlen=%x; function=%x;\n", this_request, smp_request->header.request_length, smp_request->header.function )); // Fill in the TC with the its required data // 00h task_context->priority = 0; task_context->initiator_request = 1; task_context->connection_rate = scic_remote_device_get_connection_rate(target_device); task_context->protocol_engine_index = scic_sds_controller_get_protocol_engine_group(owning_controller); task_context->logical_port_index = scic_sds_port_get_index(target_port); task_context->protocol_type = SCU_TASK_CONTEXT_PROTOCOL_SMP; task_context->abort = 0; task_context->valid = SCU_TASK_CONTEXT_VALID; task_context->context_type = SCU_TASK_CONTEXT_TYPE; //04h task_context->remote_node_index = this_request->target_device->rnc->remote_node_index; task_context->command_code = 0; task_context->task_type = SCU_TASK_TYPE_SMP_REQUEST; //08h task_context->link_layer_control = 0; task_context->do_not_dma_ssp_good_response = 1; task_context->strict_ordering = 0; task_context->control_frame = 1; task_context->timeout_enable = 0; task_context->block_guard_enable = 0; //0ch task_context->address_modifier = 0; //10h task_context->ssp_command_iu_length = smp_request->header.request_length; //14h task_context->transfer_length_bytes = 0; //18h ~ 30h, protocol specific // since commandIU has been build by framework at this point, we just // copy the frist DWord from command IU to this location. memcpy((void *)(&task_context->type.smp), this_request->command_buffer, sizeof(U32) ); //40h // "For SMP you could program it to zero. We would prefer that way so that // done code will be consistent." - Venki task_context->task_phase = 0; if (this_request->was_tag_assigned_by_user) { // Build the task context now since we have already read the data this_request->post_context = ( SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC | ( scic_sds_controller_get_protocol_engine_group(owning_controller) << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT ) | ( scic_sds_port_get_index(target_port) << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT ) | scic_sds_io_tag_get_index(this_request->io_tag) ); } else { // Build the task context now since we have already read the data this_request->post_context = ( SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC | ( scic_sds_controller_get_protocol_engine_group(owning_controller) << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT ) | ( scic_sds_port_get_index(target_port) << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT ) // This is not assigned because we have to wait until we get a TCi ); } // Copy the physical address for the command buffer to the SCU Task Context // command buffer should not contain command header. scic_cb_io_request_get_physical_address( scic_sds_request_get_controller(this_request), this_request, ((char *)(this_request->command_buffer) + sizeof(U32)), &physical_address ); task_context->command_iu_upper = sci_cb_physical_address_upper(physical_address); task_context->command_iu_lower = sci_cb_physical_address_lower(physical_address); //SMP response comes as UF, so no need to set response IU address. task_context->response_iu_upper = 0; task_context->response_iu_lower = 0; }
/** * This method handles the automatic port configuration for link up notifications. * * @param[in] controller This is the controller object that receives the * link up notification. * @param[in] phy This is the phy object which has gone link up. * @param[in] start_timer This tells the routine if it should start the timer for * any phys that might be added to a port in the future. */ static void scic_sds_apc_agent_configure_ports( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent, SCIC_SDS_PHY_T * phy, BOOL start_timer ) { U8 port_index; SCI_STATUS status; SCIC_SDS_PORT_T * port; SCI_PORT_HANDLE_T port_handle; enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY, "scic_sds_apc_agent_configure_ports(0x%08x, 0x%08x, 0x%08x, %d) enter\n", controller, port_agent, phy, start_timer )); port = scic_sds_port_configuration_agent_find_port(controller, phy); if (port != SCI_INVALID_HANDLE) { if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index)) apc_activity = SCIC_SDS_APC_ADD_PHY; else apc_activity = SCIC_SDS_APC_SKIP_PHY; } else { // There is no matching Port for this PHY so lets search through the // Ports and see if we can add the PHY to its own port or maybe start // the timer and wait to see if a wider port can be made. // // Note the break when we reach the condition of the port id == phy id for ( port_index = port_agent->phy_valid_port_range[phy->phy_index].min_index; port_index <= port_agent->phy_valid_port_range[phy->phy_index].max_index; port_index++ ) { scic_controller_get_port_handle(controller, port_index, &port_handle); port = (SCIC_SDS_PORT_T *)port_handle; // First we must make sure that this PHY can be added to this Port. if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index)) { // Port contains a PHY with a greater PHY ID than the current // PHY that has gone link up. This phy can not be part of any // port so skip it and move on. if (port->active_phy_mask > (1 << phy->phy_index)) { apc_activity = SCIC_SDS_APC_SKIP_PHY; break; } // We have reached the end of our Port list and have not found // any reason why we should not either add the PHY to the port // or wait for more phys to become active. if (port->physical_port_index == phy->phy_index) { // The Port either has no active PHYs. // Consider that if the port had any active PHYs we would have // or active PHYs with // a lower PHY Id than this PHY. if (apc_activity != SCIC_SDS_APC_START_TIMER) { apc_activity = SCIC_SDS_APC_ADD_PHY; } break; } // The current Port has no active PHYs and this PHY could be part // of this Port. Since we dont know as yet setup to start the // timer and see if there is a better configuration. if (port->active_phy_mask == 0) { apc_activity = SCIC_SDS_APC_START_TIMER; } } else if (port->active_phy_mask != 0) { // The Port has an active phy and the current Phy can not // participate in this port so skip the PHY and see if // there is a better configuration. apc_activity = SCIC_SDS_APC_SKIP_PHY; } } } // Check to see if the start timer operations should instead map to an // add phy operation. This is caused because we have been waiting to // add a phy to a port but could not becuase the automatic port // configuration engine had a choice of possible ports for the phy. // Since we have gone through a timeout we are going to restrict the // choice to the smallest possible port. if ( (start_timer == FALSE) && (apc_activity == SCIC_SDS_APC_START_TIMER) ) { apc_activity = SCIC_SDS_APC_ADD_PHY; } switch (apc_activity) { case SCIC_SDS_APC_ADD_PHY: status = scic_sds_port_add_phy(port, phy); if (status == SCI_SUCCESS) { port_agent->phy_configured_mask |= (1 << phy->phy_index); } break; case SCIC_SDS_APC_START_TIMER: scic_sds_apc_agent_start_timer( controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION ); break; case SCIC_SDS_APC_SKIP_PHY: default: // do nothing the PHY can not be made part of a port at this time. break; } }
/** * @brief This method processes an unsolicited frame while the SMP request is * waiting for a response frame. It will copy the response data, release * the unsolicited frame, and transition the request to the * SCI_BASE_REQUEST_STATE_COMPLETED state. * * @param[in] this_request This parameter specifies the request for which * the unsolicited frame was received. * @param[in] frame_index This parameter indicates the unsolicited frame * index that should contain the response. * * @return This method returns an indication of whether the response * frame was handled successfully or not. * @retval SCI_SUCCESS Currently this value is always returned and indicates * successful processing of the TC response. */ static SCI_STATUS scic_sds_smp_request_await_response_frame_handler( SCIC_SDS_REQUEST_T * this_request, U32 frame_index ) { SCI_STATUS status; void * frame_header; SMP_RESPONSE_HEADER_T * this_frame_header; U8 * user_smp_buffer = this_request->response_buffer; // Save off the controller, so that we do not touch the request after it // is completed. SCIC_SDS_CONTROLLER_T * controller = scic_sds_request_get_controller(this_request); SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_SMP_IO_REQUEST, "scic_sds_smp_request_await_response_frame_handler(0x%x, 0x%x) enter\n", this_request, frame_index )); status = scic_sds_unsolicited_frame_control_get_header( &(controller->uf_control), frame_index, &frame_header ); //byte swap the header. scic_word_copy_with_swap( (U32*) user_smp_buffer, frame_header, sizeof(SMP_RESPONSE_HEADER_T)/sizeof(U32) ); this_frame_header = (SMP_RESPONSE_HEADER_T*) user_smp_buffer; if (this_frame_header->smp_frame_type == SMP_FRAME_TYPE_RESPONSE) { void * smp_response_buffer; status = scic_sds_unsolicited_frame_control_get_buffer( &(controller->uf_control), frame_index, &smp_response_buffer ); scic_word_copy_with_swap( (U32*) (user_smp_buffer + sizeof(SMP_RESPONSE_HEADER_T)), smp_response_buffer, sizeof(SMP_RESPONSE_BODY_T)/sizeof(U32) ); if (this_frame_header->function == SMP_FUNCTION_DISCOVER) { SMP_RESPONSE_T * this_smp_response; this_smp_response = (SMP_RESPONSE_T *)user_smp_buffer; // Some expanders only report an attached SATA device, and // not an STP target. Since the core depends on the STP // target attribute to correctly build I/O, set the bit now // if necessary. if (this_smp_response->response.discover.protocols.u.bits.attached_sata_device && !this_smp_response->response.discover.protocols.u.bits.attached_stp_target) { this_smp_response->response.discover.protocols.u.bits.attached_stp_target = 1; SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_SMP_IO_REQUEST, "scic_sds_smp_request_await_response_frame_handler(0x%x) Found SATA dev, setting STP bit.\n", this_request )); } } //Don't need to copy to user space. User instead will refer to //core request's response buffer. //copy the smp response to framework smp request's response buffer. //scic_sds_smp_request_copy_response(this_request); scic_sds_request_set_status( this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS ); sci_base_state_machine_change_state( &this_request->started_substate_machine, SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_TC_COMPLETION ); } else { // This was not a response frame why did it get forwarded? SCIC_LOG_ERROR(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_SMP_IO_REQUEST, "SCIC SMP Request 0x%08x received unexpected frame %d type 0x%02x\n", this_request, frame_index, this_frame_header->smp_frame_type )); scic_sds_request_set_status( this_request, SCU_TASK_DONE_SMP_FRM_TYPE_ERR, SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); } scic_sds_controller_release_frame( controller, frame_index ); return SCI_SUCCESS; }
/** * @brief This method processes an abnormal TC completion while the SMP * request is waiting for a response frame. It decides what * happened to the IO based on TC completion status. * * @param[in] this_request This parameter specifies the request for which * the TC completion was received. * @param[in] completion_code This parameter indicates the completion status * information for the TC. * * @return Indicate if the tc completion handler was successful. * @retval SCI_SUCCESS currently this method always returns success. */ static SCI_STATUS scic_sds_smp_request_await_response_tc_completion_handler( SCIC_SDS_REQUEST_T * this_request, U32 completion_code ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_SMP_IO_REQUEST, "scic_sds_smp_request_await_response_tc_completion_handler(0x%x, 0x%x) enter\n", this_request, completion_code )); switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): //In the AWAIT RESPONSE state, any TC completion is unexpected. //but if the TC has success status, we complete the IO anyway. scic_sds_request_set_status( this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); break; case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_RESP_TO_ERR): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_UFI_ERR): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_FRM_TYPE_ERR): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_LL_RX_ERR): //These status has been seen in a specific LSI expander, which sometimes //is not able to send smp response within 2 ms. This causes our hardware //break the connection and set TC completion with one of these SMP_XXX_XX_ERR //status. For these type of error, we ask scic user to retry the request. scic_sds_request_set_status( this_request, SCU_TASK_DONE_SMP_RESP_TO_ERR, SCI_FAILURE_RETRY_REQUIRED ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); break; default: // All other completion status cause the IO to be complete. If a NAK // was received, then it is up to the user to retry the request. scic_sds_request_set_status( this_request, SCU_NORMALIZE_COMPLETION_STATUS(completion_code), SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); break; } return SCI_SUCCESS; }
/** * This routine will verify that all of the phys in the same port are using * the same SAS address. * * @param[in] controller This is the controller that contains the PHYs to * be verified. */ static SCI_STATUS scic_sds_mpc_agent_validate_phy_configuration( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { U32 phy_mask; U32 assigned_phy_mask; SCI_SAS_ADDRESS_T sas_address; SCI_SAS_ADDRESS_T phy_assigned_address; U8 port_index; U8 phy_index; assigned_phy_mask = 0; sas_address.high = 0; sas_address.low = 0; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_mpc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n", controller, port_agent )); for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) { phy_mask = controller->oem_parameters.sds1.ports[port_index].phy_mask; if (phy_mask != 0) { // Make sure that one or more of the phys were not already assinged to // a different port. if ((phy_mask & ~assigned_phy_mask) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } // Find the starting phy index for this round through the loop for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) { if ((1 << phy_index) & phy_mask) { scic_sds_phy_get_sas_address( &controller->phy_table[phy_index], &sas_address ); // The phy_index can be used as the starting point for the // port range since the hardware starts all logical ports // the same as the PE index. port_agent->phy_valid_port_range[phy_index].min_index = port_index; port_agent->phy_valid_port_range[phy_index].max_index = phy_index; if (phy_index != port_index) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } break; } } // See how many additional phys are being added to this logical port. // Note: We have not moved the current phy_index so we will actually // compare the startting phy with itself. // This is expected and required to add the phy to the port. while (phy_index < SCI_MAX_PHYS) { if ((1 << phy_index) & phy_mask) { scic_sds_phy_get_sas_address( &controller->phy_table[phy_index], &phy_assigned_address ); if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) { // The phy mask specified that this phy is part of the same port // as the starting phy and it is not so fail this configuration return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } port_agent->phy_valid_port_range[phy_index].min_index = port_index; port_agent->phy_valid_port_range[phy_index].max_index = phy_index; scic_sds_port_add_phy( &controller->port_table[port_index], &controller->phy_table[phy_index] ); assigned_phy_mask |= (1 << phy_index); } phy_index++; } } } return scic_sds_port_configuration_agent_validate_ports(controller, port_agent); }
/** * This routine will validate the port configuration is correct for the SCU * hardware. The SCU hardware allows for port configurations as follows. * LP0 -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3) * LP1 -> (PE1) * LP2 -> (PE2), (PE2, PE3) * LP3 -> (PE3) * * @param[in] controller This is the controller object that contains the * port agent * @param[in] port_agent This is the port configruation agent for * the controller. * * @return SCI_STATUS * @retval SCI_SUCCESS the port configuration is valid for this * port configuration agent. * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION the port configuration * is not valid for this port configuration agent. */ static SCI_STATUS scic_sds_port_configuration_agent_validate_ports( SCIC_SDS_CONTROLLER_T * controller, SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent ) { #if !defined(ARLINGTON_BUILD) SCI_SAS_ADDRESS_T first_address; SCI_SAS_ADDRESS_T second_address; SCIC_LOG_TRACE(( sci_base_object_get_logger(controller), SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT, "scic_sds_port_configuration_agent_validate_ports(0x%08x, 0x%08x) enter\n", controller, port_agent )); // Sanity check the max ranges for all the phys the max index // is always equal to the port range index if ( (port_agent->phy_valid_port_range[0].max_index != 0) || (port_agent->phy_valid_port_range[1].max_index != 1) || (port_agent->phy_valid_port_range[2].max_index != 2) || (port_agent->phy_valid_port_range[3].max_index != 3) ) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } // This is a request to configure a single x4 port or at least attempt // to make all the phys into a single port if ( (port_agent->phy_valid_port_range[0].min_index == 0) && (port_agent->phy_valid_port_range[1].min_index == 0) && (port_agent->phy_valid_port_range[2].min_index == 0) && (port_agent->phy_valid_port_range[3].min_index == 0) ) { return SCI_SUCCESS; } // This is a degenerate case where phy 1 and phy 2 are assigned // to the same port this is explicitly disallowed by the hardware // unless they are part of the same x4 port and this condition was // already checked above. if (port_agent->phy_valid_port_range[2].min_index == 1) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } // PE0 and PE3 can never have the same SAS Address unless they // are part of the same x4 wide port and we have already checked // for this condition. scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address); scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address); if (sci_sas_address_compare(first_address, second_address) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } // PE0 and PE1 are configured into a 2x1 ports make sure that the // SAS Address for PE0 and PE2 are different since they can not be // part of the same port. if ( (port_agent->phy_valid_port_range[0].min_index == 0) && (port_agent->phy_valid_port_range[1].min_index == 1) ) { scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address); scic_sds_phy_get_sas_address(&controller->phy_table[2], &second_address); if (sci_sas_address_compare(first_address, second_address) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } } // PE2 and PE3 are configured into a 2x1 ports make sure that the // SAS Address for PE1 and PE3 are different since they can not be // part of the same port. if ( (port_agent->phy_valid_port_range[2].min_index == 2) && (port_agent->phy_valid_port_range[3].min_index == 3) ) { scic_sds_phy_get_sas_address(&controller->phy_table[1], &first_address); scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address); if (sci_sas_address_compare(first_address, second_address) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; } } #endif // !defined(ARLINGTON_BUILD) return SCI_SUCCESS; }
/** * @brief This method processes the completions transport layer (TL) status * to determine if the RAW task management frame was sent successfully. * If the raw frame was sent successfully, then the state for the * task request transitions to waiting for a response frame. * * @param[in] this_request This parameter specifies the request for which * the TC completion was received. * @param[in] completion_code This parameter indicates the completion status * information for the TC. * * @return Indicate if the tc completion handler was successful. * @retval SCI_SUCCESS currently this method always returns success. */ static SCI_STATUS scic_sds_ssp_task_request_await_tc_completion_tc_completion_handler( SCIC_SDS_REQUEST_T * this_request, U32 completion_code ) { SCIC_LOG_TRACE(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_TASK_MANAGEMENT, "scic_sds_ssp_task_request_await_tc_completion_tc_completion_handler(0x%x, 0x%x) enter\n", this_request, completion_code )); switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): scic_sds_request_set_status( this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS ); sci_base_state_machine_change_state( &this_request->started_substate_machine, SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_RESPONSE ); break; case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_ACK_NAK_TO): // Currently, the decision is to simply allow the task request to // timeout if the task IU wasn't received successfully. // There is a potential for receiving multiple task responses if we // decide to send the task IU again. SCIC_LOG_WARNING(( sci_base_object_get_logger(this_request), SCIC_LOG_OBJECT_TASK_MANAGEMENT, "TaskRequest:0x%x CompletionCode:%x - ACK/NAK timeout\n", this_request, completion_code )); sci_base_state_machine_change_state( &this_request->started_substate_machine, SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_RESPONSE ); break; default: // All other completion status cause the IO to be complete. If a NAK // was received, then it is up to the user to retry the request. scic_sds_request_set_status( this_request, SCU_NORMALIZE_COMPLETION_STATUS(completion_code), SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR ); sci_base_state_machine_change_state( &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED ); break; } return SCI_SUCCESS; }