/**
 * @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);
         }
      }
   }
}
Beispiel #6
0
/**
 * @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);
      }
   }
}
Beispiel #13
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);
}
Beispiel #16
0
/**
 * @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;
}
Beispiel #17
0
/**
 * @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;
   }
}
Beispiel #19
0
/**
 * @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;
}
Beispiel #20
0
/**
 * @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;
}