text: /****************************************************************************
   * nuttx/graphics/nxterm/nxterm_kbdin.c
   *
   *   Copyright (C) 2012, 2016 Gregory Nutt. All rights reserved.
   *   Author: Gregory Nutt <gnutt@nuttx.org>
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   * 3. Neither the name NuttX nor the names of its contributors may be
   *    used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <nuttx/config.h>
  
  #include <inttypes.h>
  #include <fcntl.h>
  #include <sched.h>
  #include <assert.h>
  #include <poll.h>
  #include <errno.h>
  #include <debug.h>
  
  #include <nuttx/irq.h>
  
  #include "nxterm.h"
  
  #ifdef CONFIG_NXTERM_NXKBDIN
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: nxterm_pollnotify
   ****************************************************************************/
  
  static void nxterm_pollnotify(FAR struct nxterm_state_s *priv,
                                pollevent_t eventset)
  {
    FAR struct pollfd *fds;
    irqstate_t flags;
    int i;
  
    /* This function may be called from an interrupt handler */
  
    for (i = 0; i < CONFIG_NXTERM_NPOLLWAITERS; i++)
      {
        flags = enter_critical_section();
        fds   = priv->fds[i];
        if (fds)
          {
            fds->revents |= (fds->events & eventset);
            if (fds->revents != 0)
              {
                nxsem_post(fds->sem);
              }
          }
  
        leave_critical_section(flags);
      }
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: nxterm_read
   *
   * Description:
   *   The optional NxTerm read method
   *
   ****************************************************************************/
  
  ssize_t nxterm_read(FAR struct file *filep, FAR char *buffer, size_t len)
  {
    FAR struct nxterm_state_s *priv;
    ssize_t nread;
    char ch;
    int ret;
  
    /* Recover our private state structure */
  
    DEBUGASSERT(filep && filep->f_priv);
    priv = (FAR struct nxterm_state_s *)filep->f_priv;
  
    /* Get exclusive access to the driver structure */
  
    ret = nxterm_semwait(priv);
    if (ret < 0)
      {
        gerr("ERROR: nxterm_semwait failed\n");
        return ret;
      }
  
    /* Loop until something is read */
  
    for (nread = 0; nread < len; )
      {
        /* Get the next byte from the buffer */
  
        if (priv->head == priv->tail)
          {
            /* The circular buffer is empty. Did we read anything? */
  
            if (nread > 0)
              {
                /* Yes.. break out to return what we have.  */
  
                break;
              }
  
            /* If the driver was opened with O_NONBLOCK option, then
             * don't wait. Just return EGAIN.
             */
  
            if (filep->f_oflags & O_NONBLOCK)
              {
                nread = -EAGAIN;
                break;
              }
  
            /* Otherwise, wait for something to be written to the circular
             * buffer. Increment the number of waiters so that the
             * nxterm_write() will not that it needs to post the semaphore
             * to wake us up.
             */
  
            sched_lock();
            priv->nwaiters++;
            nxterm_sempost(priv);
  
            /* We may now be pre-empted!  But that should be okay because we
             * have already incremented nwaiters.  Pre-emption is disabled
             * but will be re-enabled while we are waiting.
             */
  
            ret = nxsem_wait(&priv->waitsem);
  
            /* Pre-emption will be disabled when we return.  So the
             * decrementing nwaiters here is safe.
             */
  
            priv->nwaiters--;
            sched_unlock();
  
            /* Did we successfully get the waitsem? */
  
            if (ret >= 0)
              {
                /* Yes... then retake the mutual exclusion semaphore */
  
                ret = nxterm_semwait(priv);
              }
  
            /* Was the semaphore wait successful? Did we successful re-take the
             * mutual exclusion semaphore?
             */
  
            if (ret < 0)
              {
                /* No.. One of the two nxterm_semwait's failed. */
  
                gerr("ERROR: nxterm_semwait failed\n");
  
                /* Were we awakened by a signal?  Did we read anything before
                 * we received the signal?
                 */
  
                if (ret != -EINTR || nread >= 0)
                  {
                    /* Yes.. return the error. */
  
                    nread = ret;
                  }
  
                /* Break out to return what we have.  Note, we can't exactly
                 * "break" out because whichever error occurred, we do not hold
                 * the exclusion semaphore.
                 */
  
                goto errout_without_sem;
              }
          }
        else
          {
            /* The circular buffer is not empty, get the next byte from the
             * tail index.
             */
  
            ch = priv->rxbuffer[priv->tail];
  
            /* Increment the tail index and re-enable interrupts */
  
            if (++priv->tail >= CONFIG_NXTERM_KBDBUFSIZE)
              {
                priv->tail = 0;
              }
  
            /* Add the character to the user buffer */
  
            buffer[nread] = ch;
            nread++;
          }
      }
  
    /* Relinquish the mutual exclusion semaphore */
  
    nxterm_sempost(priv);
  
    /* Notify all poll/select waiters that they can write to the FIFO */
  
  errout_without_sem:
  
    if (nread > 0)
      {
        nxterm_pollnotify(priv, POLLOUT);
      }
  
    /* Return the number of characters actually read */
  
    return nread;
  }
  
  /****************************************************************************
   * Name: nxterm_poll
   ****************************************************************************/
  
  int nxterm_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
  {
    FAR struct inode *inode = filep->f_inode;
    FAR struct nxterm_state_s *priv;
    pollevent_t eventset;
    int ret;
    int i;
  
    /* Some sanity checking */
  
    DEBUGASSERT(inode && inode->i_private);
    priv = inode->i_private;
  
    /* Get exclusive access to the driver structure */
  
    ret = nxterm_semwait(priv);
    if (ret < 0)
      {
        gerr("ERROR: nxterm_semwait failed\n");
        return ret;
      }
  
    /* Are we setting up the poll?  Or tearing it down? */
  
    if (setup)
      {
        /* This is a request to set up the poll.  Find an available
         * slot for the poll structure reference
         */
  
        for (i = 0; i < CONFIG_NXTERM_NPOLLWAITERS; i++)
          {
            /* Find an available slot */
  
            if (!priv->fds[i])
              {
                /* Bind the poll structure and this slot */
  
                priv->fds[i] = fds;
                fds->priv       = &priv->fds[i];
                break;
              }
          }
  
        if (i >= CONFIG_NXTERM_NPOLLWAITERS)
          {
            gerr("ERROR: Too many poll waiters\n");
  
            fds->priv    = NULL;
            ret          = -EBUSY;
            goto errout;
          }
  
        /* Should immediately notify on any of the requested events?
         * This driver is always available for transmission.
         */
  
        eventset = POLLOUT;
  
        /* Check if the receive buffer is empty */
  
        if (priv->head != priv->tail)
          {
            eventset |= POLLIN;
          }
  
        if (eventset)
          {
            nxterm_pollnotify(priv, eventset);
          }
      }
    else if (fds->priv)
      {
        /* This is a request to tear down the poll. */
  
        struct pollfd **slot = (struct pollfd **)fds->priv;
  
  #ifdef CONFIG_DEBUG_GRAPHICS
        if (!slot)
          {
            gerr("ERROR: No slot\n");
  
            ret = -EIO;
            goto errout;
          }
  #endif
  
        /* Remove all memory of the poll setup */
  
        *slot      = NULL;
        fds->priv  = NULL;
      }
  
  errout:
    nxterm_sempost(priv);
    return ret;
  }
  
  /****************************************************************************
   * Name: nxterm_kbdin
   *
   * Description:
   *  This function should be driven by the window kbdin callback function
   *  (see nx.h).  When the NxTerm is the top window and keyboard input is
   *  received on the top window, that window callback should be directed to
   *  this function.  This function will buffer the keyboard data and makE
   *  it available to the NxTerm as stdin.
   *
   *  If CONFIG_NXTERM_NXKBDIN is not selected, then the NxTerm will
   *  receive its input from stdin (/dev/console).  This works great but
   *  cannot be shared between different windows.  Chaos will ensue if you
   *  try to support multiple NxTerm windows without CONFIG_NXTERM_NXKBDIN
   *
   * Input Parameters:
   *   handle - A handle previously returned by nx_register, nxtk_register, or
   *     nxtool_register.
   *   buffer   - The array of characters
   *   buflen  - The number of characters that are available in buffer[]
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  void nxterm_kbdin(NXTERM handle, FAR const uint8_t *buffer, uint8_t buflen)
  {
    FAR struct nxterm_state_s *priv;
    ssize_t nwritten;
    int nexthead;
    char ch;
    int ret;
  
    ginfo("buflen=%" PRId8 "\n", buflen);
    DEBUGASSERT(handle);
  
    /* Get the reference to the driver structure from the handle */
  
    priv = (FAR struct nxterm_state_s *)handle;
  
    /* Get exclusive access to the driver structure */
  
    ret = nxterm_semwait(priv);
    if (ret < 0)
      {
        gerr("ERROR: nxterm_semwait failed\n");
        return;
      }
  
    /* Loop until all of the bytes have been written.  This function may be
     * called from an interrupt handler!  Semaphores cannot be used!
     *
     * The write logic only needs to modify the head index.  Therefore,
     * there is a difference in the way that head and tail are protected:
     * tail is protected with a semaphore; tail is protected by disabling
     * interrupts.
     */
  
    for (nwritten = 0; nwritten < buflen; nwritten++)
      {
        /* Add the next character */
  
        ch = buffer[nwritten];
  
        /* Calculate the write index AFTER the next byte is add to the ring
         * buffer
         */
  
        nexthead = priv->head + 1;
        if (nexthead >= CONFIG_NXTERM_KBDBUFSIZE)
          {
            nexthead = 0;
          }
  
        /* Would the next write overflow the circular buffer? */
  
        if (nexthead == priv->tail)
          {
            /* Yes... Return an indication that nothing was saved in
             * the buffer.
             */
  
            gerr("ERROR: Keyboard data overrun\n");
            break;
          }
  
        /* No... copy the byte */
  
        priv->rxbuffer[priv->head] = ch;
        priv->head = nexthead;
      }
  
    /* Was anything written? */
  
    if (nwritten > 0)
      {
        int i;
  
        /* Are there threads waiting for read data? */
  
        sched_lock();
  
        /* Notify all poll/select waiters that they can read from the FIFO */
  
        nxterm_pollnotify(priv, POLLIN);
  
        for (i = 0; i < priv->nwaiters; i++)
          {
            /* Yes.. Notify all of the waiting readers that more data is
             * available
             */
  
            nxsem_post(&priv->waitsem);
          }
  
        sched_unlock();
      }
  
    nxterm_sempost(priv);
  }
  
  #endif /* CONFIG_NXTERM_NXKBDIN */
  