text: /*
   * Arm SCP/MCP Software
   * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
   *
   * SPDX-License-Identifier: BSD-3-Clause
   */
  
  #include <debug.h>
  
  #include <mod_debug.h>
  
  #include <fwk_assert.h>
  #include <fwk_id.h>
  #include <fwk_macros.h>
  #include <fwk_mm.h>
  #include <fwk_module.h>
  #include <fwk_module_idx.h>
  #include <fwk_multi_thread.h>
  #include <fwk_status.h>
  
  #include <stdbool.h>
  
  static struct debug_dev_ctx *ctx_table;
  
  static inline void mark_user(fwk_id_t id,
                               bool enable,
                               enum scp_debug_user user_id)
  {
      uint32_t user_mask = (1 << user_id);
      struct debug_dev_ctx *ctx = &ctx_table[fwk_id_get_element_idx(id)];
  
      if (enable) {
          /* Add user to the list of users that is enabling this element */
          ctx->debug_users_mask |= user_mask;
      } else {
          /* Remove current user */
          ctx->debug_users_mask &= ~user_mask;
      }
  }
  
  /*
   * APIs
   */
  
  /*
   * This function can be called by the USER_AP as part of processing a request
   * coming (usually) from scmi_power_domain.
   * It can also be called from this module when processing an event that was
   * generated by the driver (USER_DAP). In this case, there is no need to submit
   * an event as per 'deferred_response_architecture', because the
   * driver does not require any response.
   */
  static int set_enabled(fwk_id_t id, bool enable, enum scp_debug_user user_id)
  {
      uint32_t user_mask;
      struct debug_dev_ctx *ctx;
      struct fwk_event req;
      int status;
      struct mod_debug_request_params *req_params;
  
      if (!fwk_expect(user_id < SCP_DEBUG_USER_COUNT))
          return FWK_E_PARAM;
  
      ctx = &ctx_table[fwk_id_get_element_idx(id)];
  
      if (ctx->state != DEBUG_IDLE)
          return FWK_E_BUSY;
  
      user_mask = (1 << user_id);
  
      /*
       * Ensure that the user attempting to enable the element had not already
       * enabled it.
       */
      if ((enable && (ctx->debug_users_mask & user_mask)) ||
          (!enable && (!(ctx->debug_users_mask & user_mask))))
          return FWK_E_ACCESS;
  
      status = ctx->driver_api->set_enabled(ctx->config->driver_id,
                                            enable, user_id);
      if (status == FWK_PENDING) {
          if (user_id == SCP_DEBUG_USER_DAP) {
              /*
               * In case the request comes from the driver, we won't respond so
               * we don't even need to put an event
               */
              ctx->requester = user_id;
              ctx->state = DEBUG_SET;
              return FWK_PENDING;
          }
  
          req = (struct fwk_event) {
              .target_id = id,
              .id = mod_debug_event_id_req_enable_set,
              .response_requested = true,
          };
  
          req_params = (struct mod_debug_request_params *)&req.params;
          req_params->user_id = user_id;
          req_params->enable = enable;
  
          status = fwk_thread_put_event(&req);
          if (status == FWK_SUCCESS) {
              ctx->state = DEBUG_SET;
              return FWK_PENDING;
          }
  
          return status;
      } else if (status != FWK_SUCCESS)
            return status;
  
      mark_user(id, enable, user_id);
  
      return FWK_SUCCESS;
  }
  
  static int get_enabled(fwk_id_t id, bool *enable, enum scp_debug_user user_id)
  {
      struct debug_dev_ctx *ctx;
      struct fwk_event req;
      int status;
      struct mod_debug_request_params *req_params;
  
      if ((enable == NULL) || !fwk_expect(user_id < SCP_DEBUG_USER_COUNT))
          return FWK_E_PARAM;
  
      ctx = &ctx_table[fwk_id_get_element_idx(id)];
  
      if (ctx->state != DEBUG_IDLE)
          return FWK_E_BUSY;
  
      status = ctx->driver_api->get_enabled(ctx->config->driver_id,
                                            enable, user_id);
      if (status == FWK_PENDING) {
          req = (struct fwk_event) {
              .target_id = id,
              .id = mod_debug_event_id_req_enable_get,
              .response_requested = true,
          };
  
          req_params = (struct mod_debug_request_params *)&req.params;
          req_params->user_id = user_id;
  
          status = fwk_thread_put_event(&req);
          if (status == FWK_SUCCESS) {
              ctx->state = DEBUG_GET;
              return FWK_PENDING;
          }
  
          return status;
      }
  
      return status;
  }
  
  static int debug_reset(fwk_id_t id)
  {
      return FWK_E_SUPPORT;
  }
  
  static int debug_end(fwk_id_t id)
  {
      return FWK_E_SUPPORT;
  }
  
  static const struct mod_debug_api debug_api = {
      .set_enabled = set_enabled,
      .get_enabled = get_enabled,
      .reset = debug_reset,
      .end = debug_end,
  };
  
  /*
   * Driver Input API
   */
  
  static int enable_request(fwk_id_t id,
                            bool enable,
                            enum scp_debug_user user_id)
  {
      struct fwk_event req;
      struct mod_debug_request_params *req_params;
      struct debug_dev_ctx *ctx;
  
      ctx = &ctx_table[fwk_id_get_element_idx(id)];
  
      fwk_assert(user_id == SCP_DEBUG_USER_DAP);
  
      req = (struct fwk_event) {
          .target_id = id,
          .source_id = ctx->config->driver_id,
          .id = mod_debug_event_id_req_drv,
      };
  
      req_params = (struct mod_debug_request_params *)&req.params;
      req_params->user_id = user_id;
      req_params->enable = enable;
  
      return fwk_thread_put_event(&req);
  }
  
  /*
   * Driver Response API
   */
  
  static void request_complete(fwk_id_t id,
                               struct mod_debug_response_params *response)
  {
      int status;
      struct fwk_event event;
      struct debug_dev_ctx *ctx;
      struct mod_debug_response_params *resp_params =
          (struct mod_debug_response_params *)event.params;
  
      fwk_assert(response != NULL);
  
      ctx = &ctx_table[fwk_id_get_element_idx(id)];
  
      if (ctx->requester == SCP_DEBUG_USER_DAP) {
          mark_user(id, response->enabled, ctx->requester);
          ctx->state = DEBUG_IDLE;
          return;
      }
  
      event = (struct fwk_event) {
          .source_id = ctx->config->driver_id,
          .target_id = id,
      };
  
      if (ctx->state == DEBUG_GET)
          event.id = mod_debug_event_id_get_complete;
      else
          event.id = mod_debug_event_id_set_complete;
  
      resp_params->status = response->status;
      resp_params->enabled = response->enabled;
  
      status = fwk_thread_put_event(&event);
      fwk_assert(status == FWK_SUCCESS);
  }
  
  static const struct mod_debug_driver_input_api driver_input_api = {
      .enable = enable_request,
      .request_complete = request_complete,
  };
  
  /*
   * Framework handlers
   */
  static int mod_debug_init(
      fwk_id_t module_id,
      unsigned int element_count,
      const void *unused)
  {
      fwk_assert(element_count > 0);
  
      ctx_table = fwk_mm_calloc(element_count, sizeof(struct debug_dev_ctx));
      if (ctx_table == NULL)
          return FWK_E_NOMEM;
  
      return FWK_SUCCESS;
  }
  
  static int mod_debug_dev_init(
      fwk_id_t element_id,
      unsigned int sub_element_count,
      const void *data)
  {
      struct debug_dev_ctx *ctx;
      struct mod_debug_dev_config *config;
  
      ctx = &ctx_table[fwk_id_get_element_idx(element_id)];
      config = (struct mod_debug_dev_config*)data;
  
      fwk_assert(config != NULL);
      ctx->config = config;
  
      return FWK_SUCCESS;
  }
  
  static int mod_debug_bind(fwk_id_t id, unsigned int round)
  {
      int status;
      struct mod_debug_driver_api *driver_api = NULL;
      struct debug_dev_ctx *ctx;
      struct mod_debug_dev_config *dev_cfg;
  
      if ((round > 0) || fwk_id_is_type(id, FWK_ID_TYPE_MODULE))
          return FWK_SUCCESS;
  
      ctx = &ctx_table[fwk_id_get_element_idx(id)];
      dev_cfg = ctx->config;
  
      /* Bind to debug driver */
      status = fwk_module_bind(
          dev_cfg->driver_id,
          dev_cfg->driver_api_id,
          &driver_api);
      if (status != FWK_SUCCESS)
          return status;
  
      /* Validate driver API */
      if ((driver_api->set_enabled == NULL) || (driver_api->get_enabled == NULL))
          return FWK_E_DATA;
  
      ctx->driver_api = driver_api;
  
      return FWK_SUCCESS;
  }
  
  static int mod_debug_process_bind_request(
      fwk_id_t source_id,
      fwk_id_t target_id,
      fwk_id_t api_id,
      const void **api)
  {
      struct debug_dev_ctx *ctx;
      unsigned int api_idx;
  
      api_idx = fwk_id_get_api_idx(api_id);
  
      /* Only binding to elements is allowed */
      if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT))
          return FWK_E_ACCESS;
  
      ctx = &ctx_table[fwk_id_get_element_idx(target_id)];
  
      switch (api_idx) {
      case MOD_DEBUG_API_IDX_HAL:
          *api = &debug_api;
  
          return FWK_SUCCESS;
  
      case MOD_DEBUG_API_IDX_DRIVER_INPUT:
          if (fwk_id_is_equal(source_id, ctx->config->driver_id))
              *api = &driver_input_api;
  
          return FWK_SUCCESS;
  
      default:
          return FWK_E_PARAM;
      }
  }
  
  static int respond(const struct fwk_event *event)
  {
      int status;
      struct fwk_event response;
      struct mod_debug_response_params *resp_params =
          (struct mod_debug_response_params *)response.params;
      struct mod_debug_response_params *req_result =
          (struct mod_debug_response_params *)event->params;
      struct debug_dev_ctx *ctx =
          &ctx_table[fwk_id_get_element_idx(event->target_id)];
  
      ctx->state = DEBUG_IDLE;
  
      status = fwk_thread_get_delayed_response(event->target_id, ctx->cookie,
                                               &response);
      if (status != FWK_SUCCESS)
          return status;
  
      resp_params->status = req_result->status;
      resp_params->enabled = req_result->enabled;
      return fwk_thread_put_event(&response);
  }
  
  static int mod_debug_process_event(const struct fwk_event *event,
                                     struct fwk_event *resp_event)
  {
      int status;
      struct debug_dev_ctx *ctx;
      struct mod_debug_request_params *req_params;
      struct mod_debug_response_params *req_result;
  
      ctx = &ctx_table[fwk_id_get_element_idx(event->target_id)];
  
      switch (fwk_id_get_event_idx(event->id)) {
      case MOD_DEBUG_PUBLIC_EVENT_IDX_REQ_ENABLE_GET:
      case MOD_DEBUG_PUBLIC_EVENT_IDX_REQ_ENABLE_SET:
      case MOD_DEBUG_EVENT_IDX_REQ_DRV:
          req_params = (struct mod_debug_request_params *)event->params;
          ctx->requester = req_params->user_id;
  
          /* Request from USER_AP we will need to respond */
          if (resp_event->response_requested) {
              ctx->cookie = event->cookie;
              resp_event->is_delayed_response = true;
              return FWK_SUCCESS;
          }
  
          /*
           * When there is no response requested, we are processing an event
           * generated by the driver (MOD_DEBUG_EVENT_IDX_REQ_DRV)
           */
          status = set_enabled(ctx->config->driver_id, req_params->enable,
                              req_params->user_id);
          if (status == FWK_PENDING)
              break;
          if (status != FWK_SUCCESS)
              return FWK_E_DEVICE;
  
          break;
  
      case MOD_DEBUG_EVENT_IDX_SET_COMPLETE:
          req_result = (struct mod_debug_response_params *)event->params;
  
          if (req_result->status == FWK_SUCCESS)
              mark_user(event->target_id, req_result->enabled, ctx->requester);
  
          return respond(event);
  
      case MOD_DEBUG_EVENT_IDX_GET_COMPLETE:
          return respond(event);
  
      default:
          return FWK_E_PARAM;
  
      }
  
      return FWK_SUCCESS;
  }
  
  const struct fwk_module module_debug = {
      .name = "DEBUG",
      .api_count = MOD_DEBUG_API_IDX_COUNT,
      .event_count = MOD_DEBUG_EVENT_COUNT,
      .type = FWK_MODULE_TYPE_HAL,
      .init = mod_debug_init,
      .element_init = mod_debug_dev_init,
      .bind = mod_debug_bind,
      .process_bind_request = mod_debug_process_bind_request,
      .process_event = mod_debug_process_event,
  };
  