static inline void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, int status) { FAR struct tcp_conn_s *conn = pstate->tc_conn; /* Make sure that no further interrupts are processed */ tcp_callback_free(conn, pstate->tc_cb); pstate->tc_cb = NULL; /* If we successfully connected, we will continue to monitor the connection * state via callbacks. */ if (status < 0) { /* Failed to connect. Stop the connection event monitor */ net_stopmonitor(conn); } }
int tcp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds) { FAR struct tcp_conn_s *conn = psock->s_conn; FAR struct tcp_poll_s *info; net_lock_t flags; /* Sanity check */ #ifdef CONFIG_DEBUG if (!conn || !fds->priv) { return -EINVAL; } #endif /* Recover the socket descriptor poll state info from the poll structure */ info = (FAR struct tcp_poll_s *)fds->priv; DEBUGASSERT(info && info->fds && info->cb); if (info) { /* Release the callback */ flags = net_lock(); tcp_callback_free(conn, info->cb); net_unlock(flags); /* Release the poll/select data slot */ info->fds->priv = NULL; /* Then free the poll info container */ kmm_free(info); } return OK; }
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_conn_s *)psock->s_conn; struct send_s state; net_lock_t save; int err; int ret = OK; /* Verify that the sockfd corresponds to valid, allocated socket */ if (!psock || psock->s_crefs <= 0) { ndbg("ERROR: Invalid socket\n"); 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)) { ndbg("ERROR: Not connected\n"); err = 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) { ndbg("ERROR: Not reachable\n"); err = ENETUNREACH; goto errout; } #endif /* CONFIG_NET_ARP_SEND || CONFIG_NET_ICMPv6_NEIGHBOR */ /* 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 = net_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) { /* Allocate resources to receive a callback */ state.snd_cb = tcp_callback_alloc(conn); if (state.snd_cb) { /* Get the initial sequence number that will be used */ state.snd_isn = tcp_getsequence(conn->sndseq); /* There is no outstanding, unacknowledged data after this * initial sequence number. */ conn->unacked = 0; /* Set the initial time for calculating timeouts */ #ifdef CONFIG_NET_SOCKOPTS state.snd_time = clock_systimer(); #endif /* Set up the callback in the connection */ state.snd_cb->flags = (TCP_ACKDATA | TCP_REXMIT | TCP_POLL | TCP_DISCONN_EVENTS); state.snd_cb->priv = (FAR void *)&state; state.snd_cb->event = tcpsend_interrupt; /* Notify the device driver of the availability of TX data */ send_txnotify(psock, conn); /* Wait for the send to complete or an error to occur: NOTES: (1) * net_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 = net_lockedwait(&state.snd_sem); /* Make sure that no further interrupts are processed */ tcp_callback_free(conn, state.snd_cb); } } sem_destroy(&state.snd_sem); 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 signalled by negative errno values * for the send length */ if (state.snd_sent < 0) { err = state.snd_sent; 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 state.snd_sent; errout: set_errno(err); return ERROR; }
ssize_t net_sendfile(int outfd, struct file *infile, off_t *offset, size_t count) { FAR struct socket *psock = sockfd_socket(outfd); FAR struct tcp_conn_s *conn; struct sendfile_s state; net_lock_t save; int err; /* Verify that the sockfd corresponds to valid, allocated socket */ if (!psock || psock->s_crefs <= 0) { ndbg("ERROR: Invalid socket\n"); 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)) { ndbg("ERROR: Not connected\n"); err = 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) { ndbg("ERROR: Not reachable\n"); err = ENETUNREACH; goto errout; } #endif /* CONFIG_NET_ARP_SEND || CONFIG_NET_ICMPv6_NEIGHBOR */ /* 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 = net_lock(); memset(&state, 0, sizeof(struct sendfile_s)); sem_init(&state. snd_sem, 0, 0); /* Doesn't really fail */ state.snd_sock = psock; /* Socket descriptor to use */ state.snd_foffset = offset ? *offset : 0; /* Input file offset */ state.snd_flen = count; /* Number of bytes to send */ state.snd_file = infile; /* File to read from */ /* Allocate resources to receive a callback */ state.snd_datacb = tcp_callback_alloc(conn); if (state.snd_datacb == NULL) { nlldbg("Failed to allocate data callback\n"); err = ENOMEM; goto errout_locked; } state.snd_ackcb = tcp_callback_alloc(conn); if (state.snd_ackcb == NULL) { nlldbg("Failed to allocate ack callback\n"); err = ENOMEM; goto errout_datacb; } /* Get the initial sequence number that will be used */ state.snd_isn = tcp_getsequence(conn->sndseq); /* There is no outstanding, unacknowledged data after this * initial sequence number. */ conn->unacked = 0; #ifdef CONFIG_NET_SOCKOPTS /* Set the initial time for calculating timeouts */ state.snd_time = clock_systimer(); #endif /* Set up the ACK callback in the connection */ state.snd_ackcb->flags = (TCP_ACKDATA | TCP_REXMIT | TCP_DISCONN_EVENTS); state.snd_ackcb->priv = (FAR void *)&state; state.snd_ackcb->event = ack_interrupt; /* Perform the TCP send operation */ do { state.snd_datacb->flags = TCP_POLL; state.snd_datacb->priv = (FAR void *)&state; state.snd_datacb->event = sendfile_interrupt; /* Notify the device driver of the availability of TX data */ sendfile_txnotify(psock, conn); net_lockedwait(&state.snd_sem); } while (state.snd_sent >= 0 && state.snd_acked < state.snd_flen); /* Set the socket state to idle */ psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); tcp_callback_free(conn, state.snd_ackcb); errout_datacb: tcp_callback_free(conn, state.snd_datacb); errout_locked: sem_destroy(&state. snd_sem); net_unlock(save); errout: if (err) { set_errno(err); return ERROR; } else if (state.snd_sent < 0) { set_errno(-state.snd_sent); return ERROR; } else { return state.snd_sent; } }
static inline int netclose_disconnect(FAR struct socket *psock) { struct tcp_close_s state; FAR struct tcp_conn_s *conn; net_lock_t flags; #ifdef CONFIG_NET_SOLINGER bool linger; #endif int ret = OK; /* Interrupts are disabled here to avoid race conditions */ flags = net_lock(); conn = (FAR struct tcp_conn_s *)psock->s_conn; /* If we have a semi-permanent write buffer callback in place, then * release it now. */ #ifdef CONFIG_NET_TCP_WRITE_BUFFERS if (psock->s_sndcb) { tcp_callback_free(conn, psock->s_sndcb); psock->s_sndcb = NULL; } #endif /* There shouldn't be any callbacks registered. */ DEBUGASSERT(conn && conn->list == NULL); /* Check for the case where the host beat us and disconnected first */ if (conn->tcpstateflags == TCP_ESTABLISHED && (state.cl_cb = tcp_callback_alloc(conn)) != NULL) { /* Set up to receive TCP data event callbacks */ state.cl_cb->flags = (TCP_NEWDATA | TCP_POLL | TCP_CLOSE | TCP_ABORT | TCP_TIMEDOUT); state.cl_cb->event = netclose_interrupt; #ifdef CONFIG_NET_SOLINGER /* Check for a lingering close */ linger = _SO_GETOPT(psock->s_options, SO_LINGER); /* Has a lingering close been requested */ if (linger) { /* A non-NULL value of the priv field means that lingering is * enabled. */ state.cl_cb->priv = (FAR void *)&state; /* Set up for the lingering wait */ state.cl_psock = psock; state.cl_result = -EBUSY; sem_init(&state.cl_sem, 0, 0); /* Record the time that we started the wait (in ticks) */ state.cl_start = clock_systimer(); } else #endif /* CONFIG_NET_SOLINGER */ { /* We will close immediately. The NULL priv field signals this */ state.cl_cb->priv = NULL; /* No further references on the connection */ conn->crefs = 0; } /* Notify the device driver of the availability of TX data */ netdev_txnotify(conn->ripaddr); #ifdef CONFIG_NET_SOLINGER /* Wait only if we are lingering */ if (linger) { /* Wait for the disconnect event */ (void)net_lockedwait(&state.cl_sem); /* We are now disconnected */ sem_destroy(&state.cl_sem); tcp_callback_free(conn, state.cl_cb); /* Free the connection */ conn->crefs = 0; /* No more references on the connection */ tcp_free(conn); /* Free uIP resources */ /* Get the result of the close */ ret = state.cl_result; } #endif /* CONFIG_NET_SOLINGER */ } else { tcp_free(conn); } net_unlock(flags); return ret; }
void tcp_free(FAR struct tcp_conn_s *conn) { FAR struct devif_callback_s *cb; FAR struct devif_callback_s *next; #ifdef CONFIG_NET_TCP_WRITE_BUFFERS FAR struct tcp_wrbuffer_s *wrbuffer; #endif net_lock_t flags; /* Because g_free_tcp_connections is accessed from user level and interrupt * level, code, it is necessary to keep interrupts disabled during this * operation. */ DEBUGASSERT(conn->crefs == 0); flags = net_lock(); /* Free remaining callbacks, actually there should be only the close callback * left. */ for (cb = conn->list; cb; cb = next) { next = cb->flink; tcp_callback_free(conn, cb); } /* TCP_ALLOCATED means that that the connection is not in the active list * yet. */ if (conn->tcpstateflags != TCP_ALLOCATED) { /* Remove the connection from the active list */ dq_rem(&conn->node, &g_active_tcp_connections); } #ifdef CONFIG_NET_TCP_READAHEAD /* Release any read-ahead buffers attached to the connection */ iob_free_queue(&conn->readahead); #endif #ifdef CONFIG_NET_TCP_WRITE_BUFFERS /* Release any write buffers attached to the connection */ while ((wrbuffer = (struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q)) != NULL) { tcp_wrbuffer_release(wrbuffer); } while ((wrbuffer = (struct tcp_wrbuffer_s *)sq_remfirst(&conn->unacked_q)) != NULL) { tcp_wrbuffer_release(wrbuffer); } #endif #ifdef CONFIG_NET_TCPBACKLOG /* Remove any backlog attached to this connection */ if (conn->backlog) { tcp_backlogdestroy(conn); } /* If this connection is, itself, backlogged, then remove it from the * parent connection's backlog list. */ if (conn->blparent) { tcp_backlogdelete(conn->blparent, conn); } #endif /* Mark the connection available and put it into the free list */ conn->tcpstateflags = TCP_CLOSED; dq_addlast(&conn->node, &g_free_tcp_connections); net_unlock(flags); }