text: /****************************************************************************
   * drivers/serial/uart_rpmsg.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 <errno.h>
  #include <stdio.h>
  #include <string.h>
  
  #include <nuttx/fs/ioctl.h>
  #include <nuttx/kmalloc.h>
  #include <nuttx/rptun/openamp.h>
  #include <nuttx/serial/serial.h>
  #include <nuttx/serial/uart_rpmsg.h>
  
  /****************************************************************************
   * Pre-processor definitions
   ****************************************************************************/
  
  #define UART_RPMSG_DEV_CONSOLE          "/dev/console"
  #define UART_RPMSG_DEV_PREFIX           "/dev/tty"
  #define UART_RPMSG_EPT_PREFIX           "rpmsg-tty"
  
  #define UART_RPMSG_TTY_WRITE            0
  #define UART_RPMSG_TTY_WAKEUP           1
  
  /****************************************************************************
   * Private Types
   ****************************************************************************/
  
  begin_packed_struct struct uart_rpmsg_header_s
  {
    uint32_t command : 31;
    uint32_t response : 1;
    int32_t  result;
    uint64_t cookie;
  } end_packed_struct;
  
  begin_packed_struct struct uart_rpmsg_write_s
  {
    struct uart_rpmsg_header_s header;
    uint32_t                   count;
    uint32_t                   resolved;
    char                       data[0];
  } end_packed_struct;
  
  begin_packed_struct struct uart_rpmsg_wakeup_s
  {
    struct uart_rpmsg_header_s header;
  } end_packed_struct;
  
  struct uart_rpmsg_priv_s
  {
    struct rpmsg_endpoint ept;
    FAR const char        *devname;
    FAR const char        *cpuname;
    FAR void              *recv_data;
    bool                  last_upper;
  #ifdef CONFIG_SERIAL_TERMIOS
    struct termios        termios;
  #endif
  };
  
  /****************************************************************************
   * Private Function Prototypes
   ****************************************************************************/
  
  static int  uart_rpmsg_setup(FAR struct uart_dev_s *dev);
  static void uart_rpmsg_shutdown(FAR struct uart_dev_s *dev);
  static int  uart_rpmsg_attach(FAR struct uart_dev_s *dev);
  static void uart_rpmsg_detach(FAR struct uart_dev_s *dev);
  static int  uart_rpmsg_ioctl(FAR struct file *filep,
                               int cmd, unsigned long arg);
  static void uart_rpmsg_rxint(FAR struct uart_dev_s *dev, bool enable);
  static bool uart_rpmsg_rxflowcontrol(FAR struct uart_dev_s *dev,
                                       unsigned int nbuffered, bool upper);
  static void uart_rpmsg_dmasend(FAR struct uart_dev_s *dev);
  static void uart_rpmsg_dmareceive(FAR struct uart_dev_s *dev);
  static void uart_rpmsg_dmarxfree(FAR struct uart_dev_s *dev);
  static void uart_rpmsg_dmatxavail(FAR struct uart_dev_s *dev);
  static void uart_rpmsg_send(FAR struct uart_dev_s *dev, int ch);
  static void uart_rpmsg_txint(FAR struct uart_dev_s *dev, bool enable);
  static bool uart_rpmsg_txready(FAR struct uart_dev_s *dev);
  static bool uart_rpmsg_txempty(FAR struct uart_dev_s *dev);
  static void uart_rpmsg_device_created(FAR struct rpmsg_device *rdev,
                                        FAR void *priv_);
  static void uart_rpmsg_device_destroy(FAR struct rpmsg_device *rdev,
                                        FAR void *priv_);
  static int  uart_rpmsg_ept_cb(struct rpmsg_endpoint *ept, FAR void *data,
                                size_t len, uint32_t src, FAR void *priv_);
  
  /****************************************************************************
   * Private Data
   ****************************************************************************/
  
  static const struct uart_ops_s g_uart_rpmsg_ops =
  {
    .setup         = uart_rpmsg_setup,
    .shutdown      = uart_rpmsg_shutdown,
    .attach        = uart_rpmsg_attach,
    .detach        = uart_rpmsg_detach,
    .ioctl         = uart_rpmsg_ioctl,
    .rxint         = uart_rpmsg_rxint,
    .rxflowcontrol = uart_rpmsg_rxflowcontrol,
    .dmasend       = uart_rpmsg_dmasend,
    .dmareceive    = uart_rpmsg_dmareceive,
    .dmarxfree     = uart_rpmsg_dmarxfree,
    .dmatxavail    = uart_rpmsg_dmatxavail,
    .send          = uart_rpmsg_send,
    .txint         = uart_rpmsg_txint,
    .txready       = uart_rpmsg_txready,
    .txempty       = uart_rpmsg_txempty,
  };
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  static int uart_rpmsg_setup(FAR struct uart_dev_s *dev)
  {
    return OK;
  }
  
  static void uart_rpmsg_shutdown(FAR struct uart_dev_s *dev)
  {
  }
  
  static int uart_rpmsg_attach(FAR struct uart_dev_s *dev)
  {
    return OK;
  }
  
  static void uart_rpmsg_detach(FAR struct uart_dev_s *dev)
  {
  }
  
  static int uart_rpmsg_ioctl(FAR struct file *filep, int cmd,
                              unsigned long arg)
  {
    int ret = -ENOTTY;
  
  #ifdef CONFIG_SERIAL_TERMIOS
    struct uart_dev_s *dev = filep->f_inode->i_private;
    struct uart_rpmsg_priv_s *priv = dev->priv;
  
    switch (cmd)
      {
      case TCGETS:
        {
          FAR struct termios *termiosp = (struct termios *)arg;
  
          if (termiosp)
            {
              *termiosp = priv->termios;
              ret = OK;
            }
          else
            {
              ret = -EINVAL;
            }
        }
        break;
  
      case TCSETS:
        {
          FAR struct termios *termiosp = (struct termios *)arg;
  
          if (termiosp)
            {
              priv->termios = *termiosp;
              ret = OK;
            }
          else
            {
              ret = -EINVAL;
            }
        }
        break;
      }
  #endif
  
    return ret;
  }
  
  static void uart_rpmsg_rxint(FAR struct uart_dev_s *dev, bool enable)
  {
  }
  
  static bool uart_rpmsg_rxflowcontrol(FAR struct uart_dev_s *dev,
                                       unsigned int nbuffered, bool upper)
  {
    FAR struct uart_rpmsg_priv_s *priv = dev->priv;
    FAR struct uart_rpmsg_wakeup_s msg;
  
    if (!upper && upper != priv->last_upper)
      {
        memset(&msg, 0, sizeof(msg));
  
        msg.header.command = UART_RPMSG_TTY_WAKEUP;
        if (is_rpmsg_ept_ready(&priv->ept))
          {
            rpmsg_send(&priv->ept, &msg, sizeof(msg));
          }
      }
  
    priv->last_upper = upper;
    return false;
  }
  
  static void uart_rpmsg_dmasend(FAR struct uart_dev_s *dev)
  {
    FAR struct uart_rpmsg_priv_s *priv = dev->priv;
    FAR struct uart_dmaxfer_s *xfer = &dev->dmatx;
    FAR struct uart_rpmsg_write_s *msg;
    size_t len = xfer->length + xfer->nlength;
    uint32_t space;
  
    msg = rpmsg_get_tx_payload_buffer(&priv->ept, &space, true);
    if (!msg)
      {
        dev->dmatx.length = 0;
        return;
      }
  
    memset(msg, 0, sizeof(*msg));
  
    space = C2B(space - sizeof(*msg));
  
    if (len > space)
      {
        len = space;
      }
  
    if (len > xfer->length)
      {
        cmem2bmem(msg->data, 0, xfer->buffer, xfer->length);
        cmem2bmem(msg->data + B2C_OFF(xfer->length), B2C_REM(xfer->length),
                xfer->nbuffer, len - xfer->length);
      }
    else
      {
        cmem2bmem(msg->data, 0, xfer->buffer, len);
      }
  
    msg->count          = len;
    msg->header.command = UART_RPMSG_TTY_WRITE;
    msg->header.result  = -ENXIO;
    msg->header.cookie  = (uintptr_t)dev;
  
    rpmsg_send_nocopy(&priv->ept, msg, sizeof(*msg) + B2C(len));
  }
  
  static void uart_rpmsg_dmareceive(FAR struct uart_dev_s *dev)
  {
    FAR struct uart_rpmsg_priv_s *priv = dev->priv;
    FAR struct uart_dmaxfer_s *xfer = &dev->dmarx;
    FAR struct uart_rpmsg_write_s *msg = priv->recv_data;
    uint32_t len = msg->count;
    size_t space = xfer->length + xfer->nlength;
  
    if (len > space)
      {
        len = space;
      }
  
    if (len > xfer->length)
      {
        bmem2cmem(xfer->buffer, msg->data, 0, xfer->length);
        bmem2cmem(xfer->nbuffer, msg->data + B2C_OFF(xfer->length),
                B2C_REM(xfer->length), len - xfer->length);
      }
    else
      {
        bmem2cmem(xfer->buffer, msg->data, 0, len);
      }
  
    xfer->nbytes = len;
    uart_recvchars_done(dev);
  
    msg->header.result = len;
  
    if (len != msg->count)
      {
        uart_rpmsg_rxflowcontrol(dev, 0, true);
      }
  }
  
  static void uart_rpmsg_dmarxfree(FAR struct uart_dev_s *dev)
  {
  }
  
  static void uart_rpmsg_dmatxavail(FAR struct uart_dev_s *dev)
  {
    FAR struct uart_rpmsg_priv_s *priv = dev->priv;
  
    if (is_rpmsg_ept_ready(&priv->ept) && dev->dmatx.length == 0)
      {
        uart_xmitchars_dma(dev);
      }
  }
  
  static void uart_rpmsg_send(FAR struct uart_dev_s *dev, int ch)
  {
    int nexthead;
  
    nexthead = dev->xmit.head + 1;
    if (nexthead >= dev->xmit.size)
      {
        nexthead = 0;
      }
  
    if (nexthead != dev->xmit.tail)
      {
        /* No.. not full.  Add the character to the TX buffer and return. */
  
        dev->xmit.buffer[dev->xmit.head] = ch;
        dev->xmit.head = nexthead;
      }
  
    uart_rpmsg_dmatxavail(dev);
  }
  
  static void uart_rpmsg_txint(FAR struct uart_dev_s *dev, bool enable)
  {
  }
  
  static bool uart_rpmsg_txready(FAR struct uart_dev_s *dev)
  {
    int nexthead;
  
    nexthead = dev->xmit.head + 1;
    if (nexthead >= dev->xmit.size)
      {
        nexthead = 0;
      }
  
    return nexthead != dev->xmit.tail;
  }
  
  static bool uart_rpmsg_txempty(FAR struct uart_dev_s *dev)
  {
    return true;
  }
  
  static void uart_rpmsg_device_created(FAR struct rpmsg_device *rdev,
                                        FAR void *priv_)
  {
    FAR struct uart_dev_s *dev = priv_;
    FAR struct uart_rpmsg_priv_s *priv = dev->priv;
    char eptname[RPMSG_NAME_SIZE];
  
    if (strcmp(priv->cpuname, rpmsg_get_cpuname(rdev)) == 0)
      {
        priv->ept.priv = dev;
        sprintf(eptname, "%s%s", UART_RPMSG_EPT_PREFIX, priv->devname);
        rpmsg_create_ept(&priv->ept, rdev, eptname,
                         RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
                         uart_rpmsg_ept_cb, NULL);
      }
  }
  
  static void uart_rpmsg_device_destroy(FAR struct rpmsg_device *rdev,
                                        FAR void *priv_)
  {
    FAR struct uart_dev_s *dev = priv_;
    FAR struct uart_rpmsg_priv_s *priv = dev->priv;
  
    if (strcmp(priv->cpuname, rpmsg_get_cpuname(rdev)) == 0)
      {
        rpmsg_destroy_ept(&priv->ept);
      }
  }
  
  static int uart_rpmsg_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data,
                               size_t len, uint32_t src, FAR void *priv_)
  {
    FAR struct uart_dev_s *dev = priv_;
    FAR struct uart_rpmsg_header_s *header = data;
    FAR struct uart_rpmsg_write_s *msg = data;
  
    if (header->response)
      {
        /* Get write-cmd response, this tell how many data have sent */
  
        dev->dmatx.nbytes = header->result;
        if (header->result < 0)
          {
            dev->dmatx.nbytes = 0;
          }
  
        uart_xmitchars_done(dev);
  
        /* If have sent some data succeed, then continue send */
  
        if (msg->count == header->result)
          {
            uart_rpmsg_dmatxavail(dev);
          }
      }
    else if (header->command == UART_RPMSG_TTY_WRITE)
      {
        FAR struct uart_rpmsg_priv_s *priv = dev->priv;
  
        /* Get write-cmd, there are some data, we need receive them */
  
        priv->recv_data = data;
        uart_recvchars_dma(dev);
        priv->recv_data = NULL;
  
        header->response = 1;
        rpmsg_send(ept, msg, sizeof(*msg));
      }
    else if (header->command == UART_RPMSG_TTY_WAKEUP)
      {
        /* Remote core have space, then wakeup current core, continue send */
  
        uart_rpmsg_dmatxavail(dev);
      }
  
    return 0;
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  int uart_rpmsg_init(FAR const char *cpuname, FAR const char *devname,
                      int buf_size, bool isconsole)
  {
    FAR struct uart_rpmsg_priv_s *priv;
    FAR struct uart_dev_s *dev;
    char dev_name[32];
    int ret = -ENOMEM;
  
    dev = kmm_zalloc(sizeof(struct uart_dev_s));
    if (!dev)
      {
        return ret;
      }
  
    dev->ops       = &g_uart_rpmsg_ops;
    dev->isconsole = isconsole;
    dev->recv.size = buf_size;
    dev->xmit.size = buf_size;
  
    dev->recv.buffer = kmm_malloc(dev->recv.size);
    if (!dev->recv.buffer)
      {
        goto fail;
      }
  
    dev->xmit.buffer = kmm_malloc(dev->xmit.size);
    if (!dev->xmit.buffer)
      {
        goto fail;
      }
  
    priv = kmm_zalloc(sizeof(struct uart_rpmsg_priv_s));
    if (!priv)
      {
        goto fail;
      }
  
    priv->cpuname = cpuname;
    priv->devname = devname;
  
    dev->priv = priv;
  
    ret = rpmsg_register_callback(dev,
                                  uart_rpmsg_device_created,
                                  uart_rpmsg_device_destroy,
                                  NULL);
    if (ret < 0)
      {
        goto fail;
      }
  
    sprintf(dev_name, "%s%s", UART_RPMSG_DEV_PREFIX, devname);
    uart_register(dev_name, dev);
  
    if (dev->isconsole)
      {
        uart_register(UART_RPMSG_DEV_CONSOLE, dev);
      }
  
    return OK;
  
  fail:
    kmm_free(dev->recv.buffer);
    kmm_free(dev->xmit.buffer);
    kmm_free(dev->priv);
    kmm_free(dev);
  
    return ret;
  }
  
  #ifdef CONFIG_RPMSG_SERIALINIT
  /* Dummy function to make linker happy */
  
  void up_earlyserialinit(void)
  {
  }
  
  void up_serialinit(void)
  {
  }
  #endif /* CONFIG_RPMSG_SERIALINIT */
  