/****************************************************************************
 * drivers/rptun/rptun_dump.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/rptun/openamp.h>
#include <nuttx/rptun/rptun.h>
#include <metal/utilities.h>

#include <rpmsg/rpmsg_internal.h>

#include "rptun.h"

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

static void rptun_dump_addr(FAR struct rpmsg_device *rdev,
                            FAR void *addr, bool rx)
{
  FAR struct rpmsg_hdr *hdr = addr;
  FAR struct rpmsg_endpoint *ept;

  ept = rpmsg_get_ept_from_addr(rdev, rx ? hdr->dst : hdr->src);
  if (ept)
    {
      metal_log(METAL_LOG_EMERGENCY,
                "      %s buffer %p hold by %s\n",
                rx ? "RX" : "TX", hdr, ept->name);
    }
}

static void rptun_dump_buffer(FAR struct rpmsg_virtio_device *rvdev,
                              bool rx)
{
  FAR struct virtqueue *vq = rx ? rvdev->rvq : rvdev->svq;
  FAR void *addr;
  int desc_idx;
  int num;
  int i;

  num = rptun_buffer_nused(rvdev, rx);
  metal_log(METAL_LOG_EMERGENCY,
            "    %s buffer, total %d, pending %d\n",
            rx ? "RX" : "TX", vq->vq_nentries, num);

  for (i = 0; i < num; i++)
    {
      if ((rpmsg_virtio_get_role(rvdev) == RPMSG_HOST) ^ rx)
        {
          desc_idx = (vq->vq_ring.used->idx + i) & (vq->vq_nentries - 1);
          desc_idx = vq->vq_ring.avail->ring[desc_idx];
        }
      else
        {
          desc_idx = (vq->vq_ring.avail->idx + i) & (vq->vq_nentries - 1);
          desc_idx = vq->vq_ring.used->ring[desc_idx].id;
        }

      addr = metal_io_phys_to_virt(vq->shm_io,
                                   vq->vq_ring.desc[desc_idx].addr);
      if (addr)
        {
          rptun_dump_addr(&rvdev->rdev, addr, rx);
        }
    }
}

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

void rptun_dump(FAR struct rpmsg_virtio_device *rvdev)
{
  FAR struct rpmsg_device *rdev = &rvdev->rdev;
  FAR struct rpmsg_endpoint *ept;
  FAR struct metal_list *node;
  bool needlock = true;

  if (!rvdev->vdev)
    {
      return;
    }

  if (up_interrupt_context() || sched_idletask() ||
      nxmutex_is_hold(&rdev->lock))
    {
      needlock = false;
    }

  if (needlock)
    {
      metal_mutex_acquire(&rdev->lock);
    }

  metal_log(METAL_LOG_EMERGENCY,
            "Dump rpmsg info between cpu (master: %s)%s <==> %s:\n",
            rpmsg_virtio_get_role(rvdev) == RPMSG_HOST ? "yes" : "no",
            CONFIG_RPTUN_LOCAL_CPUNAME, rpmsg_get_cpuname(rdev));

  metal_log(METAL_LOG_EMERGENCY, "rpmsg vq RX:\n");
  virtqueue_dump(rvdev->rvq);
  metal_log(METAL_LOG_EMERGENCY, "rpmsg vq TX:\n");
  virtqueue_dump(rvdev->svq);

  metal_log(METAL_LOG_EMERGENCY, "  rpmsg ept list:\n");

  metal_list_for_each(&rdev->endpoints, node)
    {
      ept = metal_container_of(node, struct rpmsg_endpoint, node);
      metal_log(METAL_LOG_EMERGENCY, "    ept %s\n", ept->name);
    }

  metal_log(METAL_LOG_EMERGENCY, "  rpmsg buffer list:\n");

  rptun_dump_buffer(rvdev, true);
  rptun_dump_buffer(rvdev, false);

  if (needlock)
    {
      metal_mutex_release(&rdev->lock);
    }
}