void uip_igmpwaitmsg(FAR struct igmp_group_s *group, uint8_t msgid) { uip_lock_t flags; /* Schedule to send the message */ flags = uip_lock(); DEBUGASSERT(!IS_WAITMSG(group->flags)); SET_WAITMSG(group->flags); uip_igmpschedmsg(group, msgid); /* Then wait for the message to be sent */ while (IS_SCHEDMSG(group->flags)) { /* Wait for the semaphore to be posted */ while (uip_lockedwait(&group->sem) != 0) { /* The only error that should occur from uip_lockedwait() is if * the wait is awakened by a signal. */ ASSERT(errno == EINTR); } } /* The message has been sent and we are no longer waiting */ CLR_WAITMSG(group->flags); uip_unlock(flags); }
static inline void _uip_semtake(sem_t *sem) { /* Take the semaphore (perhaps waiting) */ while (uip_lockedwait(sem) != 0) { /* The only case that an error should occur here is if * the wait was awakened by a signal. */ ASSERT(*get_errno_ptr() == EINTR); } }
static inline void netclose_disconnect(FAR struct socket *psock) { struct tcp_close_s state; uip_lock_t flags; /* Interrupts are disabled here to avoid race conditions */ flags = uip_lock(); /* Is the TCP socket in a connected state? */ if (_SS_ISCONNECTED(psock->s_flags)) { struct uip_conn *conn = (struct uip_conn*)psock->s_conn; /* Check for the case where the host beat us and disconnected first */ if (conn->tcpstateflags == UIP_ESTABLISHED) { /* Set up to receive TCP data event callbacks */ state.cl_cb = uip_tcpcallbackalloc(conn); if (state.cl_cb) { state.cl_psock = psock; sem_init(&state.cl_sem, 0, 0); state.cl_cb->flags = UIP_NEWDATA|UIP_POLL|UIP_CLOSE|UIP_ABORT; state.cl_cb->priv = (void*)&state; state.cl_cb->event = netclose_interrupt; /* Notify the device driver of the availaibilty of TX data */ netdev_txnotify(&conn->ripaddr); /* Wait for the disconnect event */ (void)uip_lockedwait(&state.cl_sem); /* We are now disconnected */ sem_destroy(&state.cl_sem); uip_tcpcallbackfree(conn, state.cl_cb); } } } uip_unlock(flags); }
ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, int flags, FAR const struct sockaddr *to, socklen_t tolen) { #ifdef CONFIG_NET_UDP FAR struct uip_udp_conn *conn; #ifdef CONFIG_NET_IPv6 FAR const struct sockaddr_in6 *into = (const struct sockaddr_in6 *)to; #else FAR const struct sockaddr_in *into = (const struct sockaddr_in *)to; #endif struct sendto_s state; uip_lock_t save; int ret; #endif int err; /* If to is NULL or tolen is zero, then this function is same as send (for * connected socket types) */ if (!to || !tolen) { #ifdef CONFIG_NET_TCP return psock_send(psock, buf, len, flags); #else err = EINVAL; goto errout; #endif } /* Verify that a valid address has been provided */ #ifdef CONFIG_NET_IPv6 if (to->sa_family != AF_INET6 || tolen < sizeof(struct sockaddr_in6)) #else if (to->sa_family != AF_INET || tolen < sizeof(struct sockaddr_in)) #endif { err = EBADF; goto errout; } /* Verify that the psock corresponds to valid, allocated socket */ if (!psock || psock->s_crefs <= 0) { err = EBADF; goto errout; } /* If this is a connected socket, then return EISCONN */ if (psock->s_type != SOCK_DGRAM) { err = EISCONN; goto errout; } /* Perform the UDP sendto operation */ #ifdef CONFIG_NET_UDP /* Set the socket state to sending */ psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); /* Initialize the state structure. This is done with interrupts * disabled because we don't want anything to happen until we * are ready. */ save = uip_lock(); memset(&state, 0, sizeof(struct sendto_s)); sem_init(&state.st_sem, 0, 0); state.st_buflen = len; state.st_buffer = buf; /* Set the initial time for calculating timeouts */ #ifdef CONFIG_NET_SENDTO_TIMEOUT state.st_sock = psock; state.st_time = clock_systimer(); #endif /* Setup the UDP socket */ conn = (struct uip_udp_conn *)psock->s_conn; ret = uip_udpconnect(conn, into); if (ret < 0) { uip_unlock(save); err = -ret; goto errout; } /* Set up the callback in the connection */ state.st_cb = uip_udpcallbackalloc(conn); if (state.st_cb) { state.st_cb->flags = UIP_POLL; state.st_cb->priv = (void*)&state; state.st_cb->event = sendto_interrupt; /* Notify the device driver of the availabilty of TX data */ netdev_txnotify(conn->ripaddr); /* Wait for either the receive to complete or for an error/timeout to occur. * NOTES: (1) uip_lockedwait will also terminate if a signal is received, (2) * interrupts may be disabled! They will be re-enabled while the task sleeps * and automatically re-enabled when the task restarts. */ uip_lockedwait(&state.st_sem); /* Make sure that no further interrupts are processed */ uip_udpcallbackfree(conn, state.st_cb); } uip_unlock(save); sem_destroy(&state.st_sem); /* Set the socket state to idle */ psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); /* Check for errors */ if (state.st_sndlen < 0) { err = -state.st_sndlen; goto errout; } /* Sucess */ return state.st_sndlen; #else err = ENOSYS; #endif errout: set_errno(err); return ERROR; }
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { FAR struct socket *psock = sockfd_socket(sockfd); FAR struct socket *pnewsock; FAR struct uip_conn *conn; struct accept_s state; #ifdef CONFIG_NET_IPv6 FAR struct sockaddr_in6 *inaddr = (struct sockaddr_in6 *)addr; #else FAR struct sockaddr_in *inaddr = (struct sockaddr_in *)addr; #endif uip_lock_t save; int newfd; int err; int ret; /* Verify that the sockfd corresponds to valid, allocated socket */ if (!psock || psock->s_crefs <= 0) { /* It is not a valid socket description. Distinguish between the cases * where sockfd is a just valid and when it is a valid file descriptor used * in the wrong context. */ #if CONFIG_NFILE_DESCRIPTORS > 0 if ((unsigned int)sockfd < CONFIG_NFILE_DESCRIPTORS) { err = ENOTSOCK; } else #endif { err = EBADF; } goto errout; } /* We have a socket descriptor, but it is a stream? */ if (psock->s_type != SOCK_STREAM) { err = EOPNOTSUPP; goto errout; } /* Is the socket listening for a connection? */ if (!_SS_ISLISTENING(psock->s_flags)) { err = EINVAL; goto errout; } /* Verify that a valid memory block has been provided to receive the * address */ if (addr) { #ifdef CONFIG_NET_IPv6 if (*addrlen < sizeof(struct sockaddr_in6)) #else if (*addrlen < sizeof(struct sockaddr_in)) #endif { err = EBADF; goto errout; } } /* Allocate a socket descriptor for the new connection now (so that it * cannot fail later) */ newfd = sockfd_allocate(0); if (newfd < 0) { err = ENFILE; goto errout; } pnewsock = sockfd_socket(newfd); if (!pnewsock) { err = ENFILE; goto errout_with_socket; } /* Check the backlog to see if there is a connection already pending for * this listener. */ save = uip_lock(); conn = (struct uip_conn *)psock->s_conn; #ifdef CONFIG_NET_TCPBACKLOG state.acpt_newconn = uip_backlogremove(conn); if (state.acpt_newconn) { /* Yes... get the address of the connected client */ nvdbg("Pending conn=%p\n", state.acpt_newconn); accept_tcpsender(state.acpt_newconn, inaddr); } /* In general, this uIP-based implementation will not support non-blocking * socket operations... except in a few cases: Here for TCP accept with backlog * enabled. If this socket is configured as non-blocking then return EAGAIN * if there is no pending connection in the backlog. */ else if (_SS_ISNONBLOCK(psock->s_flags)) { err = EAGAIN; goto errout_with_lock; } else #endif { /* Set the socket state to accepting */ psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_ACCEPT); /* Perform the TCP accept operation */ /* Initialize the state structure. This is done with interrupts * disabled because we don't want anything to happen until we * are ready. */ state.acpt_addr = inaddr; state.acpt_newconn = NULL; state.acpt_result = OK; sem_init(&state.acpt_sem, 0, 0); /* Set up the callback in the connection */ conn->accept_private = (void*)&state; conn->accept = accept_interrupt; /* Wait for the send to complete or an error to occur: NOTES: (1) * uip_lockedwait will also terminate if a signal is received, (2) interrupts * may be disabled! They will be re-enabled while the task sleeps and * automatically re-enabled when the task restarts. */ ret = uip_lockedwait(&state.acpt_sem); /* Make sure that no further interrupts are processed */ conn->accept_private = NULL; conn->accept = NULL; sem_destroy(&state. acpt_sem); /* 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 (state.acpt_result != 0) { err = state.acpt_result; goto errout_with_lock; } /* If uip_lockedwait failed, then we were probably reawakened by a signal. In * this case, uip_lockedwait will have set errno appropriately. */ if (ret < 0) { err = -ret; goto errout_with_lock; } } /* Initialize the socket structure and mark the socket as connected. * (The reference count on the new connection structure was set in the * interrupt handler). */ pnewsock->s_type = SOCK_STREAM; pnewsock->s_conn = state.acpt_newconn; pnewsock->s_flags |= _SF_CONNECTED; pnewsock->s_flags &= ~_SF_CLOSED; /* Begin monitoring for TCP connection events on the newly connected socket */ net_startmonitor(pnewsock); uip_unlock(save); return newfd; errout_with_lock: uip_unlock(save); errout_with_socket: sockfd_release(newfd); errout: errno = err; return ERROR; }
int uip_ping(uip_ipaddr_t addr, uint16_t id, uint16_t seqno, uint16_t datalen, int dsecs) { struct icmp_ping_s state; uip_lock_t save; /* Initialize the state structure */ sem_init(&state.png_sem, 0, 0); state.png_ticks = DSEC2TICK(dsecs); /* System ticks to wait */ state.png_result = -ENOMEM; /* Assume allocation failure */ state.png_addr = addr; /* Address of the peer to be ping'ed */ state.png_id = id; /* The ID to use in the ECHO request */ state.png_seqno = seqno; /* The seqno to use int the ECHO request */ state.png_datlen = datalen; /* The length of data to send in the ECHO request */ state.png_sent = false; /* ECHO request not yet sent */ save = uip_lock(); state.png_time = clock_systimer(); /* Set up the callback */ state.png_cb = uip_icmpcallbackalloc(); if (state.png_cb) { state.png_cb->flags = UIP_POLL|UIP_ECHOREPLY; state.png_cb->priv = (void*)&state; state.png_cb->event = ping_interrupt; state.png_result = -EINTR; /* Assume sem-wait interrupted by signal */ /* Notify the device driver of the availaibilty of TX data */ netdev_txnotify(&state.png_addr); /* Wait for either the full round trip transfer to complete or * for timeout to occur. (1) uip_lockedwait will also terminate if a * signal is received, (2) interrupts may be disabled! They will * be re-enabled while the task sleeps and automatically * re-enabled when the task restarts. */ nlldbg("Start time: 0x%08x seqno: %d\n", state.png_time, seqno); uip_lockedwait(&state.png_sem); uip_icmpcallbackfree(state.png_cb); } uip_unlock(save); /* Return the negated error number in the event of a failure, or the * sequence number of the ECHO reply on success. */ if (!state.png_result) { nlldbg("Return seqno=%d\n", state.png_seqno); return (int)state.png_seqno; } else { nlldbg("Return error=%d\n", -state.png_result); return state.png_result; } }
ssize_t send(int sockfd, const void *buf, size_t len, int flags) { FAR struct socket *psock = sockfd_socket(sockfd); struct send_s state; uip_lock_t save; int err; int ret = OK; /* Verify that the sockfd corresponds to valid, allocated socket */ if (!psock || psock->s_crefs <= 0) { err = EBADF; goto errout; } /* If this is an un-connected socket, then return ENOTCONN */ if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(psock->s_flags)) { err = ENOTCONN; goto errout; } /* Set the socket state to sending */ psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); /* Perform the TCP send operation */ /* Initialize the state structure. This is done with interrupts * disabled because we don't want anything to happen until we * are ready. */ save = uip_lock(); memset(&state, 0, sizeof(struct send_s)); (void)sem_init(&state. snd_sem, 0, 0); /* Doesn't really fail */ state.snd_sock = psock; /* Socket descriptor to use */ state.snd_buflen = len; /* Number of bytes to send */ state.snd_buffer = buf; /* Buffer to send from */ if (len > 0) { struct uip_conn *conn = (struct uip_conn*)psock->s_conn; /* Allocate resources to receive a callback */ state.snd_cb = uip_tcpcallbackalloc(conn); if (state.snd_cb) { /* Get the initial sequence number that will be used */ state.snd_isn = uip_tcpgetsequence(conn->sndseq); /* There is no outstanding, unacknowledged data after this * initial sequence number. */ conn->unacked = 0; /* Update the initial time for calculating timeouts */ #if defined(CONFIG_NET_SOCKOPTS) && !defined(CONFIG_DISABLE_CLOCK) state.snd_time = clock_systimer(); #endif /* Set up the callback in the connection */ state.snd_cb->flags = UIP_ACKDATA|UIP_REXMIT|UIP_POLL|UIP_CLOSE|UIP_ABORT|UIP_TIMEDOUT; state.snd_cb->priv = (void*)&state; state.snd_cb->event = send_interrupt; /* Notify the device driver of the availaibilty of TX data */ netdev_txnotify(&conn->ripaddr); /* Wait for the send to complete or an error to occur: NOTES: (1) * uip_lockedwait will also terminate if a signal is received, (2) interrupts * may be disabled! They will be re-enabled while the task sleeps and * automatically re-enabled when the task restarts. */ ret = uip_lockedwait(&state. snd_sem); /* Make sure that no further interrupts are processed */ uip_tcpcallbackfree(conn, state.snd_cb); } } sem_destroy(&state. snd_sem); uip_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 (state.snd_sent < 0) { err = state.snd_sent; goto errout; } /* If uip_lockedwait failed, then we were probably reawakened by a signal. In * this case, uip_lockedwait will have set errno appropriately. */ if (ret < 0) { err = -ret; goto errout; } /* Return the number of bytes actually sent */ return state.snd_sent; errout: *get_errno_ptr() = err; return ERROR; }
static ssize_t tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, FAR struct sockaddr_in *infrom ) #endif { struct recvfrom_s state; uip_lock_t save; int ret; /* Initialize the state structure. This is done with interrupts * disabled because we don't want anything to happen until we * are ready. */ save = uip_lock(); recvfrom_init(psock, buf, len, infrom, &state); /* Handle any any TCP data already buffered in a read-ahead buffer. NOTE * that there may be read-ahead data to be retrieved even after the * socket has been disconnected. */ #if CONFIG_NET_NTCP_READAHEAD_BUFFERS > 0 recvfrom_readahead(&state); /* The default return value is the number of bytes that we just copied into * the user buffer. We will return this if the socket has become disconnected * or if the user request was completely satisfied with data from the readahead * buffers. */ ret = state.rf_recvlen; #else /* Otherwise, the default return value of zero is used (only for the case * where len == state.rf_buflen is zero). */ ret = 0; #endif /* Verify that the SOCK_STREAM has been and still is connected */ if (!_SS_ISCONNECTED(psock->s_flags)) { /* Was any data transferred from the readahead buffer after we were * disconnected? If so, then return the number of bytes received. We * will wait to return end disconnection indications the next time that * recvfrom() is called. * * If no data was received (i.e., ret == 0 -- it will not be negative) * and the connection was gracefully closed by the remote peer, then return * success. If rf_recvlen is zero, the caller of recvfrom() will get an * end-of-file indication. */ #if CONFIG_NET_NTCP_READAHEAD_BUFFERS > 0 if (ret <= 0 && !_SS_ISCLOSED(psock->s_flags)) #else if (!_SS_ISCLOSED(psock->s_flags)) #endif { /* Nothing was previously received from the readahead buffers. * The SOCK_STREAM must be (re-)connected in order to receive any * additional data. */ ret = -ENOTCONN; } } /* In general, this uIP-based implementation will not support non-blocking * socket operations... except in a few cases: Here for TCP receive with read-ahead * enabled. If this socket is configured as non-blocking then return EAGAIN * if no data was obtained from the read-ahead buffers. */ else #if CONFIG_NET_NTCP_READAHEAD_BUFFERS > 0 if (_SS_ISNONBLOCK(psock->s_flags)) { /* Return the number of bytes read from the read-ahead buffer if * something was received (already in 'ret'); EAGAIN if not. */ if (ret <= 0) { /* Nothing was received */ ret = -EAGAIN; } } /* It is okay to block if we need to. If there is space to receive anything * more, then we will wait to receive the data. Otherwise return the number * of bytes read from the read-ahead buffer (already in 'ret'). */ else #endif /* We get here when we we decide that we need to setup the wait for incoming * TCP/IP data. Just a few more conditions to check: * * 1) Make sure thet there is buffer space to receive additional data * (state.rf_buflen > 0). This could be zero, for example, if read-ahead * buffering was enabled and we filled the user buffer with data from * the read-ahead buffers. Aand * 2) if read-ahead buffering is enabled (CONFIG_NET_NTCP_READAHEAD_BUFFERS > 0) * and delay logic is disabled (CONFIG_NET_TCP_RECVDELAY == 0), then we * not want to wait if we already obtained some data from the read-ahead * buffer. In that case, return now with what we have (don't want for more * because there may be no timeout). */ #if CONFIG_NET_TCP_RECVDELAY == 0 && CONFIG_NET_NTCP_READAHEAD_BUFFERS > 0 if (state.rf_recvlen == 0 && state.rf_buflen > 0) #else if (state.rf_buflen > 0) #endif { struct uip_conn *conn = (struct uip_conn *)psock->s_conn; /* Set up the callback in the connection */ state.rf_cb = uip_tcpcallbackalloc(conn); if (state.rf_cb) { state.rf_cb->flags = UIP_NEWDATA|UIP_POLL|UIP_CLOSE|UIP_ABORT|UIP_TIMEDOUT; state.rf_cb->priv = (void*)&state; state.rf_cb->event = recvfrom_tcpinterrupt; /* Wait for either the receive to complete or for an error/timeout to occur. * NOTES: (1) uip_lockedwait will also terminate if a signal is received, (2) * interrupts may be disabled! They will be re-enabled while the task sleeps * and automatically re-enabled when the task restarts. */ ret = uip_lockedwait(&state.rf_sem); /* Make sure that no further interrupts are processed */ uip_tcpcallbackfree(conn, state.rf_cb); ret = recvfrom_result(ret, &state); } else { ret = -EBUSY; } } uip_unlock(save); recvfrom_uninit(&state); return (ssize_t)ret; }
static ssize_t udp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, FAR struct sockaddr_in *infrom ) #endif { struct uip_udp_conn *conn = (struct uip_udp_conn *)psock->s_conn; struct recvfrom_s state; uip_lock_t save; int ret; /* Perform the UDP recvfrom() operation */ /* Initialize the state structure. This is done with interrupts * disabled because we don't want anything to happen until we * are ready. */ save = uip_lock(); recvfrom_init(psock, buf, len, infrom, &state); /* Setup the UDP remote connection */ ret = uip_udpconnect(conn, NULL); if (ret < 0) { goto errout_with_state; } /* Set up the callback in the connection */ state.rf_cb = uip_udpcallbackalloc(conn); if (state.rf_cb) { /* Set up the callback in the connection */ state.rf_cb->flags = UIP_NEWDATA|UIP_POLL; state.rf_cb->priv = (void*)&state; state.rf_cb->event = recvfrom_udpinterrupt; /* Enable the UDP socket */ uip_udpenable(conn); /* Wait for either the receive to complete or for an error/timeout to occur. * NOTES: (1) uip_lockedwait will also terminate if a signal is received, (2) * interrupts are disabled! They will be re-enabled while the task sleeps * and automatically re-enabled when the task restarts. */ ret = uip_lockedwait(&state. rf_sem); /* Make sure that no further interrupts are processed */ uip_udpdisable(conn); uip_udpcallbackfree(conn, state.rf_cb); ret = recvfrom_result(ret, &state); } else { ret = -EBUSY; } errout_with_state: uip_unlock(save); recvfrom_uninit(&state); return ret; }
static inline int tcp_connect(FAR struct socket *psock, const struct sockaddr_in *inaddr) #endif { struct tcp_connect_s state; uip_lock_t flags; int ret = OK; /* Interrupts must be disabled through all of the following because * we cannot allow the network callback to occur until we are completely * setup. */ flags = uip_lock(); /* Get the connection reference from the socket */ if (!psock->s_conn) /* Should always be non-NULL */ { ret = -EINVAL; } else { /* Perform the uIP connection operation */ ret = uip_tcpconnect(psock->s_conn, inaddr); } if (ret >= 0) { /* Set up the callbacks in the connection */ ret = tcp_setup_callbacks(psock, &state); if (ret >= 0) { /* Wait for either the connect to complete or for an error/timeout * to occur. NOTES: (1) uip_lockedwait will also terminate if a signal * is received, (2) interrupts may be disabled! They will be re- * enabled while the task sleeps and automatically re-disabled * when the task restarts. */ ret = uip_lockedwait(&state.tc_sem); /* Uninitialize the state structure */ (void)sem_destroy(&state.tc_sem); /* If uip_lockedwait failed, recover the negated error (probably -EINTR) */ if (ret < 0) { ret = -errno; } else { /* If the wait succeeded, then get the new error value from * the state structure */ ret = state.tc_result; } /* Make sure that no further interrupts are processed */ tcp_teardown_callbacks(&state, ret); } /* Mark the connection bound and connected */ if (ret >= 0) { psock->s_flags |= (_SF_BOUND|_SF_CONNECTED); } } uip_unlock(flags); return ret; }