4a740671创建于 2025年7月22日历史提交
/****************************************************************************
 * drivers/rpmsg/rpmsg_test.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <string.h>
#include <syslog.h>
#include <time.h>

#include <nuttx/clock.h>
#include <nuttx/kmalloc.h>
#include <nuttx/list.h>
#include <nuttx/wqueue.h>

#include "rpmsg_test.h"

/****************************************************************************
 * Pre-processor definitions
 ****************************************************************************/

#define RPMSG_TEST_EPT_NAME           "rpmsg-test"

#define RPMSG_TEST_CMD(type, value)   (((type) << 16) | (value))
#define RPMSG_TEST_GET_TYPE(cmd)      ((cmd) >> 16)
#define RPMSG_TEST_GET_VALUE(cmd)     ((cmd) & 0xffff)

/* Rpmsg test type,value and cmd definition  */

#define RPMSG_TEST_TYPE_HOLD_RX       1
#define RPMSG_TEST_VAL_HOLD_RX        0
#define RPMSG_TEST_VAL_HOLD_RX_END    1

#define RPMSG_TEST_CMD_HOLD_RX        \
  RPMSG_TEST_CMD(RPMSG_TEST_TYPE_HOLD_RX, RPMSG_TEST_VAL_HOLD_RX)
#define RPMSG_TEST_CMD_HOLD_RX_END    \
  RPMSG_TEST_CMD(RPMSG_TEST_TYPE_HOLD_RX, RPMSG_TEST_VAL_HOLD_RX_END)

/* Magic number used in test case */

#define RPMSG_TEST_MAGIC1             0x55
#define RPMSG_TEST_MAGIC2             0xaa

/****************************************************************************
 * Private Types
 ****************************************************************************/

struct rpmsg_test_priv_s
{
  FAR struct rpmsg_endpoint *ept;
  struct list_node           rxhead;
  struct work_s              work;
};

begin_packed_struct struct rpmsg_test_msg_s
{
  uint32_t node[4]; /* Used for the list node */
  uint32_t cmd;
  uint32_t len;
  uint8_t  data[1];
} end_packed_struct;

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: rpmsg_test_memcmp
 ****************************************************************************/

static bool rpmsg_test_memcmp(FAR const uint8_t *buf, uint8_t ch,
                              size_t size)
{
  size_t i;

  for (i = 0; i < size; i++)
    {
      if (buf[i] != ch)
        {
          return false;
        }
    }

  return true;
}

/****************************************************************************
 * Name: rpmsg_test_hold_rx_work
 ****************************************************************************/

static void rpmsg_test_hold_rx_work(FAR void *arg)
{
  FAR struct rpmsg_test_priv_s *priv = arg;
  FAR struct rpmsg_test_msg_s *msg;
  bool ret;

  /* Check all the rx buffer value (should still be MAGIC2) and release
   * all the held rx buffers.
   */

  syslog(LOG_EMERG, "Rpmsg Test: release rx buffers start\n");

  while (!list_is_empty(&priv->rxhead))
    {
      msg = (FAR struct rpmsg_test_msg_s *)list_remove_head(&priv->rxhead);
      ret = rpmsg_test_memcmp(msg->data, RPMSG_TEST_MAGIC2, msg->len);
      DEBUGASSERT(ret);
      rpmsg_release_rx_buffer(priv->ept, msg);
    }

  syslog(LOG_EMERG, "Rpmsg Test: release rx buffers end\n");
}

/****************************************************************************
 * Name: rpmsg_test_hold_rx_cb
 ****************************************************************************/

static void rpmsg_test_hold_rx_cb(FAR struct rpmsg_endpoint *ept,
                                  FAR void *data, size_t len, uint32_t src,
                                  FAR void *_priv)
{
  FAR struct rpmsg_test_priv_s *priv = _priv;
  FAR struct rpmsg_test_msg_s *msg = data;
  uint32_t value = RPMSG_TEST_GET_VALUE(msg->cmd);
  bool ret;

  /* Hold the rx buffer and fill the rx buffer data with MAGIC2 */

  ret = rpmsg_test_memcmp(msg->data, RPMSG_TEST_MAGIC1, msg->len);
  DEBUGASSERT(ret);
  memset(msg->data, RPMSG_TEST_MAGIC2, msg->len);
  rpmsg_hold_rx_buffer(ept, data);

  list_add_tail(&priv->rxhead, (FAR struct list_node *)data);

  if (value == RPMSG_TEST_VAL_HOLD_RX_END)
    {
      syslog(LOG_EMERG, "Rpmsg Test: hold rx buffer finish\n");

      /* Delay 10ms to release all the rx buffers, to make sure
       * the hold rx buffer api is valid.
       */

      work_queue(LPWORK, &priv->work, rpmsg_test_hold_rx_work, priv,
                 MSEC2TICK(10));
    }
}

/****************************************************************************
 * Name: rpmsg_test_ept_cb
 ****************************************************************************/

static int rpmsg_test_ept_cb(FAR struct rpmsg_endpoint *ept,
                             FAR void *data, size_t len, uint32_t src,
                             FAR void *priv)
{
  FAR struct rpmsg_test_msg_s *msg = data;

  switch (RPMSG_TEST_GET_TYPE(msg->cmd))
    {
      case RPMSG_TEST_TYPE_HOLD_RX:
        rpmsg_test_hold_rx_cb(ept, data, len, src, priv);
        break;
      default:
        break;
    }

  return 0;
}

/****************************************************************************
 * Name: rpmsg_test_hold_rx
 ****************************************************************************/

static int rpmsg_test_hold_rx(FAR struct rpmsg_test_priv_s *priv)
{
  FAR struct rpmsg_endpoint *ept = priv->ept;
  FAR struct rpmsg_test_msg_s *msg;
  FAR struct rpmsg_test_msg_s *nmsg;
  uint32_t space;
  uint32_t nspace;
  int num = 0;
  int ret;

  syslog(LOG_EMERG, "Rpmsg Test: start send\n");

  /* Send all the tx buffer to remote core, remote core will hold this
   * buffer.
   */

  msg = rpmsg_get_tx_payload_buffer(ept, &space, false);
  if (msg == NULL)
    {
      return -ENOMEM;
    }

  for (; ; )
    {
      nmsg = rpmsg_get_tx_payload_buffer(ept, &nspace, false);
      num++;
      msg->cmd = nmsg ? RPMSG_TEST_CMD_HOLD_RX :
                        RPMSG_TEST_CMD_HOLD_RX_END;
      msg->len = space - sizeof(*msg) + 1;
      memset(msg->data, RPMSG_TEST_MAGIC1, msg->len);
      ret = rpmsg_send_nocopy(ept, msg, space);
      DEBUGASSERT(ret >= 0);
      if (nmsg == NULL)
        {
          break;
        }

      msg = nmsg;
      space = nspace;
    }

  syslog(LOG_EMERG, "Rpmsg Test: tx buffer num=%d space=%" PRIu32 "\n",
         num, space);

  syslog(LOG_EMERG, "Rpmsg Test: send finish\n");
  return 0;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

int rpmsg_test(FAR struct rpmsg_endpoint *ept, unsigned long arg)
{
  FAR struct rpmsg_test_priv_s *priv = ept->priv;

  /* Only support rx hold buffer test case for now */

  return rpmsg_test_hold_rx(priv);
}

int rpmsg_test_init(FAR struct rpmsg_device *rdev,
                    FAR struct rpmsg_endpoint *ept)
{
  FAR struct rpmsg_test_priv_s *priv;

  priv = kmm_zalloc(sizeof(*priv));
  if (priv == NULL)
    {
      return -ENOMEM;
    }

  list_initialize(&priv->rxhead);
  priv->ept = ept;
  ept->priv = priv;
  return rpmsg_create_ept(ept, rdev, RPMSG_TEST_EPT_NAME,
                          RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
                          rpmsg_test_ept_cb, NULL);
}

void rpmsg_test_deinit(FAR struct rpmsg_endpoint *ept)
{
  FAR struct rpmsg_test_priv_s *priv = ept->priv;

  rpmsg_destroy_ept(ept);
  kmm_free(priv);
}