ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf,
                       size_t len)
{
    FAR struct tcp_conn_s *conn;
    net_lock_t save;
    ssize_t    result = 0;
    int        err;
    int        ret = OK;

    if (!psock || psock->s_crefs <= 0)
    {
        ndbg("ERROR: Invalid socket\n");
        err = EBADF;
        goto errout;
    }

    if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(psock->s_flags))
    {
        ndbg("ERROR: Not connected\n");
        err = ENOTCONN;
        goto errout;
    }

    /* Make sure that the IP address mapping is in the ARP table */

    conn = (FAR struct tcp_conn_s *)psock->s_conn;
#ifdef CONFIG_NET_ARP_SEND
    ret = arp_send(conn->ripaddr);
    if (ret < 0)
    {
        ndbg("ERROR: Not reachable\n");
        err = ENETUNREACH;
        goto errout;
    }
#endif

    /* Dump the incoming buffer */

    BUF_DUMP("psock_tcp_send", buf, len);

    /* Set the socket state to sending */

    psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND);

    save = net_lock();

    if (len > 0)
    {
        /* Allocate resources to receive a callback */

        if (!psock->s_sndcb)
        {
            psock->s_sndcb = tcp_callback_alloc(conn);
        }

        /* Test if the callback has been allocated */

        if (!psock->s_sndcb)
        {
            /* A buffer allocation error occurred */

            ndbg("ERROR: Failed to allocate callback\n");
            result = -ENOMEM;
        }
        else
        {
            FAR struct tcp_wrbuffer_s *wrb;

            /* Set up the callback in the connection */

            psock->s_sndcb->flags = (TCP_ACKDATA | TCP_REXMIT | TCP_POLL |
                                     TCP_CLOSE | TCP_ABORT | TCP_TIMEDOUT);
            psock->s_sndcb->priv  = (void*)psock;
            psock->s_sndcb->event = psock_send_interrupt;

            /* Allocate an write buffer */

            wrb = tcp_wrbuffer_alloc();
            if (wrb)
            {
                /* Initialize the write buffer */

                WRB_SEQNO(wrb) = (unsigned)-1;
                WRB_NRTX(wrb)  = 0;
                WRB_COPYIN(wrb, (FAR uint8_t *)buf, len);

                /* Dump I/O buffer chain */

                WRB_DUMP("I/O buffer chain", wrb, WRB_PKTLEN(wrb), 0);

                /* psock_send_interrupt() will send data in FIFO order from the
                 * conn->write_q
                 */

                sq_addlast(&wrb->wb_node, &conn->write_q);
                nvdbg("Queued WRB=%p pktlen=%u write_q(%p,%p)\n",
                      wrb, WRB_PKTLEN(wrb),
                      conn->write_q.head, conn->write_q.tail);

                /* Notify the device driver of the availability of TX data */

#ifdef CONFIG_NET_MULTILINK
                netdev_txnotify(conn->lipaddr, conn->ripaddr);
#else
                netdev_txnotify(conn->ripaddr);
#endif
                result = len;
            }

            /* A buffer allocation error occurred */

            else
            {
                ndbg("ERROR: Failed to allocate write buffer\n");
                result = -ENOMEM;
            }
        }
    }

    net_unlock(save);

    /* Set the socket state to idle */

    psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE);

    /* Check for a errors.  Errors are signaled by negative errno values
     * for the send length
     */

    if (result < 0)
    {
        err = result;
        goto errout;
    }

    /* If net_lockedwait failed, then we were probably reawakened by a signal.
     * In this case, net_lockedwait will have set errno appropriately.
     */

    if (ret < 0)
    {
        err = -ret;
        goto errout;
    }

    /* Return the number of bytes actually sent */

    return result;

errout:
    set_errno(err);
    return ERROR;
}
Beispiel #2
0
ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf,
                       size_t len)
{
  FAR struct tcp_conn_s *conn;
  FAR struct tcp_wrbuffer_s *wrb;
  net_lock_t save;
  ssize_t    result = 0;
  int        errcode;
  int        ret = OK;

  if (!psock || psock->s_crefs <= 0)
    {
      nerr("ERROR: Invalid socket\n");
      errcode = EBADF;
      goto errout;
    }

  if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(psock->s_flags))
    {
      nerr("ERROR: Not connected\n");
      errcode = ENOTCONN;
      goto errout;
    }

  /* Make sure that we have the IP address mapping */

  conn = (FAR struct tcp_conn_s *)psock->s_conn;
  DEBUGASSERT(conn);

#if defined(CONFIG_NET_ARP_SEND) || defined(CONFIG_NET_ICMPv6_NEIGHBOR)
#ifdef CONFIG_NET_ARP_SEND
#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
  if (psock->s_domain == PF_INET)
#endif
    {
      /* Make sure that the IP address mapping is in the ARP table */

      ret = arp_send(conn->u.ipv4.raddr);
    }
#endif /* CONFIG_NET_ARP_SEND */

#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
#ifdef CONFIG_NET_ARP_SEND
  else
#endif
    {
      /* Make sure that the IP address mapping is in the Neighbor Table */

      ret = icmpv6_neighbor(conn->u.ipv6.raddr);
    }
#endif /* CONFIG_NET_ICMPv6_NEIGHBOR */

  /* Did we successfully get the address mapping? */

  if (ret < 0)
    {
      nerr("ERROR: Not reachable\n");
      errcode = ENETUNREACH;
      goto errout;
    }
#endif /* CONFIG_NET_ARP_SEND || CONFIG_NET_ICMPv6_NEIGHBOR */

  /* Dump the incoming buffer */

  BUF_DUMP("psock_tcp_send", buf, len);

  /* Set the socket state to sending */

  psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND);

  if (len > 0)
    {
      /* Allocate a write buffer.  Careful, the network will be momentarily
       * unlocked here.
       */

      save = net_lock();
      wrb = tcp_wrbuffer_alloc();
      if (!wrb)
        {
          /* A buffer allocation error occurred */

          nerr("ERROR: Failed to allocate write buffer\n");
          errcode = ENOMEM;
          goto errout_with_lock;
        }

      /* Allocate resources to receive a callback */

      if (!psock->s_sndcb)
        {
          psock->s_sndcb = tcp_callback_alloc(conn);
        }

      /* Test if the callback has been allocated */

      if (!psock->s_sndcb)
        {
          /* A buffer allocation error occurred */

          nerr("ERROR: Failed to allocate callback\n");
          errcode = ENOMEM;
          goto errout_with_wrb;
        }

      /* Set up the callback in the connection */

      psock->s_sndcb->flags = (TCP_ACKDATA | TCP_REXMIT | TCP_POLL |
                               TCP_DISCONN_EVENTS);
      psock->s_sndcb->priv  = (FAR void *)psock;
      psock->s_sndcb->event = psock_send_interrupt;

      /* Initialize the write buffer */

      WRB_SEQNO(wrb) = (unsigned)-1;
      WRB_NRTX(wrb)  = 0;
      result = WRB_COPYIN(wrb, (FAR uint8_t *)buf, len);

      /* Dump I/O buffer chain */

      WRB_DUMP("I/O buffer chain", wrb, WRB_PKTLEN(wrb), 0);

      /* psock_send_interrupt() will send data in FIFO order from the
       * conn->write_q
       */

      sq_addlast(&wrb->wb_node, &conn->write_q);
      ninfo("Queued WRB=%p pktlen=%u write_q(%p,%p)\n",
            wrb, WRB_PKTLEN(wrb),
            conn->write_q.head, conn->write_q.tail);

      /* Notify the device driver of the availability of TX data */

      send_txnotify(psock, conn);
      net_unlock(save);
    }

  /* Set the socket state to idle */

  psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE);

  /* Check for errors.  Errors are signalled by negative errno values
   * for the send length
   */

  if (result < 0)
    {
      errcode = result;
      goto errout;
    }

  /* If net_lockedwait failed, then we were probably reawakened by a signal.
   * In this case, net_lockedwait will have set errno appropriately.
   */

  if (ret < 0)
    {
      errcode = -ret;
      goto errout;
    }

  /* Return the number of bytes actually sent */

  return result;

errout_with_wrb:
  tcp_wrbuffer_release(wrb);

errout_with_lock:
  net_unlock(save);

errout:
  set_errno(errcode);
  return ERROR;
}