text: /****************************************************************************
   * net/usrsock/usrsock_listen.c
   *
   *   Copyright (C) 2018 Pinecone Inc. All rights reserved.
   *   Author: Xiang Xiao <xiaoxiang@pinecone.net>
   *
   * 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>
  #if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK)
  
  #include <assert.h>
  #include <errno.h>
  #include <debug.h>
  
  #include <nuttx/semaphore.h>
  #include <nuttx/net/net.h>
  #include <nuttx/net/usrsock.h>
  
  #include "usrsock/usrsock.h"
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  static uint16_t listen_event(FAR struct net_driver_s *dev, FAR void *pvconn,
                               FAR void *pvpriv, uint16_t flags)
  {
    FAR struct usrsock_reqstate_s *pstate = pvpriv;
    FAR struct usrsock_conn_s *conn = pvconn;
  
    if (flags & USRSOCK_EVENT_ABORT)
      {
        ninfo("socket aborted.\n");
  
        pstate->result = -ECONNABORTED;
  
        /* Stop further callbacks */
  
        pstate->cb->flags   = 0;
        pstate->cb->priv    = NULL;
        pstate->cb->event   = NULL;
  
        /* Wake up the waiting thread */
  
        nxsem_post(&pstate->recvsem);
      }
    else if (flags & USRSOCK_EVENT_REQ_COMPLETE)
      {
        ninfo("request completed.\n");
  
        pstate->result = conn->resp.result;
  
        /* Stop further callbacks */
  
        pstate->cb->flags   = 0;
        pstate->cb->priv    = NULL;
        pstate->cb->event   = NULL;
  
        /* Wake up the waiting thread */
  
        nxsem_post(&pstate->recvsem);
      }
  
    return flags;
  }
  
  /****************************************************************************
   * Name: do_listen_request
   ****************************************************************************/
  
  static int do_listen_request(FAR struct usrsock_conn_s *conn, int backlog)
  {
    struct usrsock_request_listen_s req =
    {
    };
  
    struct iovec bufs[1];
  
    if (backlog > UINT16_MAX)
      {
        backlog = UINT16_MAX;
      }
  
    /* Prepare request for daemon to read. */
  
    req.head.reqid = USRSOCK_REQUEST_LISTEN;
    req.usockid = conn->usockid;
    req.backlog = backlog;
  
    bufs[0].iov_base = &req;
    bufs[0].iov_len = sizeof(req);
  
    return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs));
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: usrsock_listen
   *
   * Description:
   *   To accept connections, a socket is first created with psock_socket(), a
   *   willingness to accept incoming connections and a queue limit for
   *   incoming connections are specified with psock_listen(), and then the
   *   connections are accepted with psock_accept().  For the case of AFINET
   *   and AFINET6 sockets, psock_listen() calls this function.  The
   *   psock_listen() call applies only to sockets of type SOCK_STREAM or
   *   SOCK_SEQPACKET.
   *
   * Parameters:
   *   psock    Reference to an internal, bound socket structure.
   *   backlog  The maximum length the queue of pending connections may grow.
   *            If a connection request arrives with the queue full, the client
   *            may receive an error with an indication of ECONNREFUSED or,
   *            if the underlying protocol supports retransmission, the request
   *            may be ignored so that retries succeed.
   *
   * Returned Value:
   *   On success, zero is returned. On error, a negated errno value is
   *   returned.  See list() for the set of appropriate error values.
   *
   ****************************************************************************/
  
  int usrsock_listen(FAR struct socket *psock, int backlog)
  {
    FAR struct usrsock_conn_s *conn = psock->s_conn;
    struct usrsock_reqstate_s state =
    {
    };
  
    int ret;
  
    DEBUGASSERT(conn);
  
    net_lock();
  
    if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED ||
        conn->state == USRSOCK_CONN_STATE_ABORTED)
      {
        /* Invalid state or closed by daemon. */
  
        ninfo("usockid=%d; listen() with uninitialized usrsock.\n",
              conn->usockid);
  
        ret = (conn->state == USRSOCK_CONN_STATE_ABORTED) ? -EPIPE :
              -ECONNREFUSED;
        goto errout_unlock;
      }
  
    if (conn->connected)
      {
        /* Already connected. */
  
        ret = -EISCONN;
        goto errout_unlock;
      }
  
    if (conn->state == USRSOCK_CONN_STATE_CONNECTING)
      {
        /* Already connecting. */
  
        ninfo("usockid=%d; socket already connecting.\n",
              conn->usockid);
  
        ret = -EALREADY;
        goto errout_unlock;
      }
  
    /* Set up event callback for usrsock. */
  
    ret = usrsock_setup_request_callback(conn, &state, listen_event,
                                         USRSOCK_EVENT_ABORT |
                                         USRSOCK_EVENT_REQ_COMPLETE);
    if (ret < 0)
      {
        nwarn("usrsock_setup_request_callback failed: %d\n", ret);
  
        goto errout_unlock;
      }
  
    /* Mark conn as listening(reuse connecting here) one. */
  
    conn->state = USRSOCK_CONN_STATE_CONNECTING;
  
    /* Send request. */
  
    ret = do_listen_request(conn, backlog);
    if (ret < 0)
      {
        goto errout_teardown;
      }
  
    /* Wait for completion of request (or signal). */
  
    ret = net_lockedwait(&state.recvsem);
    if (ret < 0)
      {
        /* Wait interrupted, exit early. */
  
        goto errout_teardown;
      }
  
    ret = state.result;
  
  errout_teardown:
    usrsock_teardown_request_callback(&state);
  errout_unlock:
    net_unlock();
    return ret;
  }
  
  #endif /* CONFIG_NET && CONFIG_NET_USRSOCK */
  