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 tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) { FAR struct tcp_conn_s *conn = psock->s_conn; FAR struct tcp_poll_s *info; FAR struct devif_callback_s *cb; net_lock_t flags; int ret; /* Sanity check */ #ifdef CONFIG_DEBUG if (!conn || !fds) { return -EINVAL; } #endif /* Allocate a container to hold the poll information */ info = (FAR struct tcp_poll_s *)kmm_malloc(sizeof(struct tcp_poll_s)); if (!info) { return -ENOMEM; } /* Some of the following must be atomic */ flags = net_lock(); /* Allocate a TCP/IP callback structure */ cb = tcp_callback_alloc(conn); if (!cb) { ret = -EBUSY; goto errout_with_lock; } /* Initialize the poll info container */ info->psock = psock; info->fds = fds; info->cb = cb; /* Initialize the callback structure. Save the reference to the info * structure as callback private data so that it will be available during * callback processing. */ cb->flags = (TCP_NEWDATA | TCP_BACKLOG | TCP_POLL | TCP_CLOSE | TCP_ABORT | TCP_TIMEDOUT); cb->priv = (FAR void *)info; cb->event = tcp_poll_interrupt; /* Save the reference in the poll info structure as fds private as well * for use durring poll teardown as well. */ fds->priv = (FAR void *)info; #ifdef CONFIG_NET_TCPBACKLOG /* Check for read data or backlogged connection availability now */ if (!IOB_QEMPTY(&conn->readahead) || tcp_backlogavailable(conn)) #else /* Check for read data availability now */ if (!IOB_QEMPTY(&conn->readahead)) #endif { /* Normal data may be read without blocking. */ fds->revents |= (POLLRDNORM & fds->events); } /* Check for a loss of connection events. We need to be careful here. * There are four possibilities: * * 1) The socket is connected and we are waiting for data availability * events. * * __SS_ISCONNECTED(f) == true * __SS_ISLISTENING(f) == false * __SS_ISCLOSED(f) == false * * Action: Wait for data availability events * * 2) This is a listener socket that was never connected and we are * waiting for connection events. * * __SS_ISCONNECTED(f) == false * __SS_ISLISTENING(f) == true * __SS_ISCLOSED(f) == false * * Action: Wait for connection events * * 3) This socket was previously connected, but the peer has gracefully * closed the connection. * * __SS_ISCONNECTED(f) == false * __SS_ISLISTENING(f) == false * __SS_ISCLOSED(f) == true * * Action: Return with POLLHUP|POLLERR events * * 4) This socket was previously connected, but we lost the connection * due to some exceptional event. * * __SS_ISCONNECTED(f) == false * __SS_ISLISTENING(f) == false * __SS_ISCLOSED(f) == false * * Action: Return with POLLHUP|POLLERR events */ if (!_SS_ISCONNECTED(psock->s_flags) && !_SS_ISLISTENING(psock->s_flags)) { /* We were previously connected but lost the connection either due * to a graceful shutdown by the remote peer or because of some * exceptional event. */ fds->revents |= (POLLERR | POLLHUP); } /* Check if any requested events are already in effect */ if (fds->revents != 0) { /* Yes.. then signal the poll logic */ sem_post(fds->sem); } net_unlock(flags); return OK; errout_with_lock: kmm_free(info); net_unlock(flags); return ret; }