| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054 |
- /*
- * Copyright (C) 2001-2005 by egnite Software GmbH
- * Copyright (c) 1993 by Digital Equipment Corporation
- * Copyright (c) 1983, 1993 by The Regents of the University of California
- *
- * All rights reserved.
- *
- * 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 of the copyright holders nor the names of
- * 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.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*!
- * \file net/tcpsock.c
- * \brief TCP socket interface.
- *
- * \verbatim
- * $Id: tcpsock.c 5542 2014-01-10 17:35:18Z olereinhardt $
- * \endverbatim
- */
- #include <cfg/os.h>
- #include <cfg/crt.h>
- #include <sys/types.h>
- #include <string.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <sys/atom.h>
- #include <sys/heap.h>
- #include <sys/thread.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <net/route.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <netinet/icmp.h>
- #include <netinet/ip_icmp.h>
- #include <netinet/ipcsum.h>
- #include <sys/socket.h>
- #include <netinet/tcp.h>
- #include <stdio.h>
- #include <io.h>
- #include <memdebug.h>
- #ifdef NUTDEBUG
- #include <net/netdebug.h>
- #endif
- /*!
- * \addtogroup xgTcpSocket
- */
- /*@{*/
- TCPSOCKET *tcpSocketList = 0; /*!< Global linked list of all TCP sockets. */
- static uint16_t last_local_port; /* Unassigned local port. */
- static uint_fast8_t registered;
- void NutTcpDiscardBuffers(TCPSOCKET * sock)
- {
- NETBUF *nb;
- while ((nb = sock->so_rx_buf) != 0) {
- sock->so_rx_buf = nb->nb_next;
- NutNetBufFree(nb);
- }
- while ((nb = sock->so_tx_nbq) != 0) {
- sock->so_tx_nbq = nb->nb_next;
- NutNetBufFree(nb);
- }
- while ((nb = sock->so_rx_nbq) != 0) {
- sock->so_rx_nbq = nb->nb_next;
- NutNetBufFree(nb);
- }
- }
- /*!
- * \brief Destroy a previously allocated socket.
- *
- * Release occupied socket memory.
- *
- * Applications must not call this function. It is automatically called
- * by a timer after the socket has been closed by NutTcpCloseSocket().
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- */
- void NutTcpDestroySocket(TCPSOCKET * sock)
- {
- //@@@printf ("[%04X] Calling destroy.\n", (u_short) sock);
- /*
- * Free all memory occupied by the socket.
- */
- NutTcpDiscardBuffers(sock);
- if (sock->so_devocnt)
- {
- free(sock->so_devobuf);
- sock->so_devocnt = 0;
- }
- memset(sock, 0, sizeof(TCPSOCKET));
- free(sock);
- }
- /*!
- * \brief Find a matching socket.
- *
- * Loop through all sockets and find a matching connection (preferred)
- * or a listening socket.
- *
- * Applications typically do not call this function.
- *
- * \param lport Local port number.
- * \param rport Remote port number.
- * \param raddr Remote IP address in network byte order.
- *
- * \return Socket descriptor.
- */
- TCPSOCKET *NutTcpFindSocket(uint16_t lport, uint16_t rport, uint32_t raddr)
- {
- TCPSOCKET *sp;
- TCPSOCKET *sock = 0;
- /*
- * Try to find an exact match for the remote
- * address and port first.
- */
- for (sp = tcpSocketList; sp; sp = sp->so_next) {
- if (sp->so_local_port == lport) {
- if (sp->so_remote_addr == raddr && sp->so_remote_port == rport && sp->so_state != TCPS_CLOSED && sp->so_state != TCPS_DESTROY) {
- sock = sp;
- break;
- }
- }
- }
- /*
- * If no exact match exists, try a listening socket.
- * This part had been totally wrong, because it
- * didn't check the local port number and accepted
- * incoming requests on any port. Thanks to
- * Alejandro Lopez, who pointed this out.
- */
- if (sock == 0) {
- for (sp = tcpSocketList; sp; sp = sp->so_next) {
- if (sp->so_state == TCPS_LISTEN && sp->so_local_port == lport) {
- sock = sp;
- break;
- }
- }
- }
- return sock;
- }
- /*!
- * \brief Create a TCP socket.
- *
- * Allocates a TCPSOCKET structure from heap memory, initializes
- * it and returns a pointer to that structure.
- *
- * The very first call will also start the TCP state machine,
- * which is running in a separate thread.
- *
- * \return Socket descriptor of the newly created TCP socket or
- * 0 if there is not enough memory left.
- */
- TCPSOCKET *NutTcpCreateSocket(void)
- {
- TCPSOCKET *sock = 0;
- if (!registered) {
- if (NutRegisterIpHandler(IPPROTO_TCP, NutTcpInput) || NutTcpInitStateMachine()) {
- return NULL;
- }
- registered = 1;
- }
- if ((sock = calloc(1, sizeof(TCPSOCKET))) != 0) {
- sock->so_state = TCPS_CLOSED;
- /*
- * Initialize the virtual device interface.
- */
- sock->so_devtype = IFTYP_TCPSOCK;
- sock->so_devread = NutTcpDeviceRead;
- sock->so_devwrite = NutTcpDeviceWrite;
- #ifdef __HARVARD_ARCH__
- sock->so_devwrite_P = NutTcpDeviceWrite_P;
- #endif
- sock->so_devioctl = NutTcpDeviceIOCtl;
- sock->so_devselect = NutTcpDeviceSelect;
- sock->so_tx_isn = NutGetTickCount(); /* Generate the ISN from the nut_ticks counter */
- sock->so_tx_una = sock->so_tx_isn;
- sock->so_tx_nxt = sock->so_tx_isn;
- sock->so_rx_bsz = sock->so_rx_win = TCP_WINSIZE;
- sock->so_mss = TCP_MSS;
- sock->so_rtto = 1000; /* Initial retransmission time out */
- sock->so_next = tcpSocketList;
- sock->so_devobsz = TCP_MSS; /* Default output buffer size is TCP_MSS bytes */
- tcpSocketList = sock;
- }
- return sock;
- }
- /*!
- * \brief Set value of a TCP socket option.
- *
- * The following values can be set:
- *
- * - #TCP_MAXSEG Maximum segment size (#uint16_t). Can only be set if
- socket is not yet connected.
- * - #SO_SNDTIMEO Socket send timeout (#uint32_t).
- * - #SO_RCVTIMEO Socket receive timeout (#uint32_t).
- * - #SO_SNDBUF Socket output buffer size (#uint16_t).
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param optname Option to set.
- * \param optval Pointer to the value.
- * \param optlen Length of the value.
- * \return 0 on success, -1 otherwise. The specific error code
- * can be retrieved by calling NutTcpError().
- */
- int NutTcpSetSockOpt(TCPSOCKET * sock, int optname, const void *optval, int optlen)
- {
- int rc = -1;
- if (sock == 0)
- return -1;
- switch (optname) {
- case TCP_MAXSEG:
- if (optval == 0 || optlen != sizeof(uint16_t))
- sock->so_last_error = EINVAL;
- else if (sock->so_state != TCPS_CLOSED)
- sock->so_last_error = EISCONN;
- else {
- sock->so_mss = *((uint16_t *) optval);
- rc = 0;
- }
- break;
- case SO_RCVBUF:
- if (optval == 0 || optlen != sizeof(uint16_t))
- sock->so_last_error = EINVAL;
- else {
- sock->so_rx_bsz = *((uint16_t *) optval);
- sock->so_rx_win = sock->so_rx_bsz;
- rc = 0;
- }
- break;
- case SO_SNDTIMEO:
- if (optval == 0 || optlen != sizeof(uint32_t))
- sock->so_last_error = EINVAL;
- else {
- sock->so_write_to = *((uint32_t *) optval);
- rc = 0;
- }
- break;
- case SO_RCVTIMEO:
- if (optval == 0 || optlen != sizeof(uint32_t))
- sock->so_last_error = EINVAL;
- else {
- sock->so_read_to = *((uint32_t *) optval);
- rc = 0;
- }
- break;
- case SO_SNDBUF:
- if (optval == 0 || optlen != sizeof(uint16_t))
- sock->so_last_error = EINVAL;
- else {
- NutTcpDeviceWrite(sock, 0, 0);
- sock->so_devobsz = *((uint16_t *) optval);
- rc = 0;
- }
- break;
- default:
- sock->so_last_error = ENOPROTOOPT;
- break;
- }
- return rc;
- }
- /*!
- * \brief Get a TCP socket option value.
- *
- * The following values can be set:
- *
- * - #TCP_MAXSEG Maximum segment size (#uint16_t).
- * - #SO_SNDTIMEO Socket send timeout (#uint32_t).
- * - #SO_RCVTIMEO Socket receive timeout (#uint32_t).
- * - #SO_SNDBUF Socket output buffer size (#uint16_t).
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param optname Option to get.
- * \param optval Points to a buffer receiving the value.
- * \param optlen Length of the value buffer.
- *
- * \return 0 on success, -1 otherwise. The specific error code
- * can be retrieved by calling NutTcpError().
- */
- int NutTcpGetSockOpt(TCPSOCKET * sock, int optname, void *optval, int optlen)
- {
- int rc = -1;
- if (sock == 0)
- return -1;
- switch (optname) {
- case TCP_MAXSEG:
- if (optval == 0 || optlen != sizeof(uint16_t))
- sock->so_last_error = EINVAL;
- else {
- *((uint16_t *) optval) = sock->so_mss;
- rc = 0;
- }
- break;
- case SO_RCVBUF:
- if (optval == 0 || optlen != sizeof(uint16_t))
- sock->so_last_error = EINVAL;
- else {
- *((uint16_t *) optval) = sock->so_rx_bsz;
- rc = 0;
- }
- break;
- case SO_SNDTIMEO:
- if (optval == 0 || optlen != sizeof(uint32_t))
- sock->so_last_error = EINVAL;
- else {
- *((uint32_t *) optval) = sock->so_write_to;
- rc = 0;
- }
- break;
- case SO_RCVTIMEO:
- if (optval == 0 || optlen != sizeof(uint32_t))
- sock->so_last_error = EINVAL;
- else {
- *((uint32_t *) optval) = sock->so_read_to;
- rc = 0;
- }
- break;
- case SO_SNDBUF:
- if (optval == 0 || optlen != sizeof(uint16_t))
- sock->so_last_error = EINVAL;
- else {
- *((uint16_t *) optval) = sock->so_devobsz;
- rc = 0;
- }
- break;
- default:
- sock->so_last_error = ENOPROTOOPT;
- break;
- }
- return rc;
- }
- /*!
- * \brief Connect to a remote socket.
- *
- * This function tries to establish a connection to the specified
- * remote port of the specified remote server. The calling thread
- * will be suspended until a connection is successfully established
- * or an error occurs.
- *
- * If a write timeout is set for this socket, the function will return
- * an error, if the connection could not be established before the
- * timeout. In this case the last error is set to ETIMEOUT.
- *
- * This function is typically used by TCP client applications.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param addr IP address of the host to connect (network byte order).
- * \param port Port number to connect (host byte order).
- *
- * \return 0 on success, -1 otherwise. The specific error code
- * can be retrieved by calling NutTcpError().
- */
- int NutTcpConnect(TCPSOCKET * sock, uint32_t addr, uint16_t port)
- {
- TCPSOCKET *sp;
- NUTDEVICE *dev;
- uint16_t ticks;
- if (sock == 0)
- return -1;
- /*
- * Despite RFC793 we do not allow a passive
- * open to become active.
- */
- if (sock->so_state == TCPS_LISTEN) {
- sock->so_last_error = EOPNOTSUPP;
- return -1;
- } else if (sock->so_state != TCPS_CLOSED) {
- sock->so_last_error = EISCONN;
- return -1;
- }
- /*
- * Find an unused local port.
- */
- do {
- /* Each time a new socket is created the local port number in incremented
- by a more or less randomized value between 1 and 15 (the lowest 4 bit of
- NutGetMillis() | 1). The highest two bits are always set to 1.
- This way a port range of 49152 to 65535 is used according to the IANA
- suggestions for ephemeral port usage.
- */
- ticks = (uint16_t) NutGetMillis();
- if (last_local_port) {
- last_local_port += (uint16_t) ((ticks & 0x000F) | 1);
- } else {
- last_local_port = ticks;
- }
- last_local_port |= 0xC000;
- sp = tcpSocketList;
- while (sp) {
- /* Thanks to Ralph Mason for fixing the byte order bug. */
- if (sp->so_local_port == htons(last_local_port))
- break;
- sp = sp->so_next;
- }
- } while (sp);
- /*
- * OK - we've got a new port. Now fill
- * remaining parts of the socket structure.
- */
- sock->so_local_port = htons(last_local_port);
- sock->so_remote_port = htons(port);
- sock->so_remote_addr = addr;
- /*
- * Get local address for this destination.
- */
- if ((dev = NutIpRouteQuery(addr, 0)) != 0) {
- IFNET *nif = dev->dev_icb;
- sock->so_local_addr = nif->if_local_ip;
- } else {
- sock->so_last_error = EHOSTUNREACH;
- return -1;
- }
- /*
- * Trigger active open event for the state machine.
- * This will suspend the thread until synchronized.
- */
- return NutTcpStateActiveOpenEvent(sock);
- }
- /*!
- * \brief Wait for incoming connect from a remote socket.
- *
- * The calling thread will be suspended until until an
- * incoming connection request is received.
- *
- * This function is typically used by TCP server applications.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param port Port number to listen to (host byte order).
- *
- * \return 0 on success, -1 otherwise. The specific error code
- * can be retrieved by calling NutTcpError().
- */
- int NutTcpAccept(TCPSOCKET * sock, uint16_t port)
- {
- sock->so_local_port = htons(port);
- return NutTcpStatePassiveOpenEvent(sock);
- }
- /*!
- * \brief Send data on a connected TCP socket.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket(). In
- * addition a connection must have been established
- * by calling NutTcpConnect or NutTcpAccept.
- * \param data Pointer to a buffer containing the data to send.
- * \param len Number of bytes to be sent.
- *
- * \return If successful, the number of bytes added to the socket transmit
- * buffer. This is limited to the maximum segment size of the
- * connection and thus may be less than the specified number of
- * bytes to send. The return value -1 indicates a fatal error.
- * On time out, a value of 0 is returned.
- */
- int NutTcpSend(TCPSOCKET * sock, const void *data, int len)
- {
- uint16_t unacked;
- /*
- * Check parameters.
- */
- NutThreadYield();
- if (sock == 0)
- return -1;
- if (data == 0 || len == 0)
- return 0;
- /*
- * Limit the transmission size to our maximum segment size.
- */
- if (len > sock->so_mss)
- len = sock->so_mss;
- for (;;) {
- /*
- * We can only send on an established connection.
- */
- if (sock->so_state != TCPS_ESTABLISHED) {
- sock->so_last_error = ENOTCONN;
- return -1;
- }
- /*
- * Limit the size of unacknowledged data to four full segments.
- * Also wait for peer's window open wide enough to take all our
- * data. This also avoids silly window syndrome on our side.
- */
- unacked = sock->so_tx_nxt - sock->so_tx_una;
- if ((unacked >> 2) < sock->so_mss && len <= sock->so_tx_win - unacked) {
- break;
- }
- if (NutEventWait(&sock->so_tx_tq, sock->so_write_to)) {
- return 0;
- }
- }
- /*
- * The segment will be automatically retransmitted if not
- * acknowledged in time. If this returns an error, it's a
- * fatal one.
- */
- sock->so_tx_flags |= SO_ACK;
- if (NutTcpOutput(sock, data, len))
- return -1;
- return len;
- }
- /*!
- * \brief Receive data on a connected TCP socket.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket(). In
- * addition a connection must have been established
- * by calling NutTcpConnect or NutTcpAccept.
- * \param data Pointer to the buffer that receives the data.
- * \param size Size of the buffer.
- *
- * \return If successful, the number of received data bytes
- * is returned. This may be less than the specified
- * size of the buffer. The return value 0 indicates
- * a timeout, while -1 is returned in case of an error
- * or broken connection. Call NutTcpError() to determine
- * the specific error code.
- */
- int NutTcpReceive(TCPSOCKET * sock, void *data, int size)
- {
- int i;
- NutThreadYield();
- /*
- * Check parameters.
- */
- if (sock == 0)
- return -1;
- if (sock->so_state != TCPS_ESTABLISHED && sock->so_state != TCPS_CLOSE_WAIT) {
- sock->so_last_error = ENOTCONN;
- return -1;
- }
- if (data == 0 || size == 0)
- return 0;
- /*
- * Wait until any data arrived, a timeout occurs
- * or the connection terminates.
- */
- while (sock->so_rx_cnt - sock->so_rd_cnt == 0) {
- if (sock->so_state != TCPS_ESTABLISHED) {
- sock->so_last_error = ENOTCONN;
- return -1;
- }
- if (NutEventWait(&sock->so_rx_tq, sock->so_read_to))
- return 0;
- }
- if (size > sock->so_rx_cnt - sock->so_rd_cnt)
- size = sock->so_rx_cnt - sock->so_rd_cnt;
- if (size) {
- NETBUF *nb;
- uint16_t rd_cnt; /* Bytes read from NETBUF. */
- uint16_t nb_cnt; /* Bytes left in NETBUF. */
- uint16_t ab_cnt; /* Total bytes in app buffer. */
- uint16_t mv_cnt; /* Bytes to move to app buffer. */
- rd_cnt = sock->so_rd_cnt;
- ab_cnt = 0;
- while (ab_cnt < size) {
- nb = sock->so_rx_buf;
- nb_cnt = nb->nb_ap.sz - rd_cnt;
- mv_cnt = size - ab_cnt;
- if (mv_cnt > nb_cnt)
- mv_cnt = nb_cnt;
- memcpy((char *) data + ab_cnt, (char *) (nb->nb_ap.vp) + rd_cnt, mv_cnt);
- ab_cnt += mv_cnt;
- rd_cnt += mv_cnt;
- if (mv_cnt >= nb_cnt) {
- sock->so_rx_buf = nb->nb_next;
- sock->so_rx_cnt -= rd_cnt;
- NutNetBufFree(nb);
- sock->so_rx_apc--;
- nb = sock->so_rx_buf;
- rd_cnt = 0;
- }
- }
- sock->so_rd_cnt = rd_cnt;
- /*
- * Update our receive window.
- */
- if (sock->so_state == TCPS_ESTABLISHED) {
- i = sock->so_rx_win;
- if ((i += size) > sock->so_rx_bsz)
- i = sock->so_rx_bsz;
- if (sock->so_rx_win <= sock->so_mss && i > sock->so_mss) {
- sock->so_rx_win = i;
- NutTcpStateWindowEvent(sock);
- } else {
- sock->so_rx_win = i;
- }
- }
- }
- return size;
- }
- /*!
- * \brief Close TCP socket.
- *
- * Note, that the socket may not be immediately destroyed
- * after calling this function. However, the application
- * must not use the socket after this call.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- *
- * \return 0 on success, -1 otherwise.
- */
- int NutTcpCloseSocket(TCPSOCKET * sock)
- {
- /* Flush buffer first */
- //@@@printf ("[%04X] Calling close\n", (u_short) sock);
- NutTcpDeviceWrite(sock, 0, 0);
- return NutTcpStateCloseEvent(sock);
- }
- /*!
- * \brief Return specific code of the last error.
- *
- * Possible error codes (net/errno.h) are:
- *
- * - EWOULDBLOCK: Operation would block
- * - EINPROGRESS: Operation now in progress
- * - EALREADY: Operation already in progress
- * - ENOTSOCK: Socket operation on non-socket
- * - EDESTADDRREQ: Destination address required
- * - EMSGSIZE: Message too long
- * - EPROTOTYPE: Protocol wrong type for socket
- * - ENOPROTOOPT: Protocol not available
- * - EPROTONOSUPPORT: Protocol not supported
- * - ESOCKTNOSUPPORT: Socket type not supported
- * - EOPNOTSUPP: Operation not supported on socket
- * - EPFNOSUPPORT: Protocol family not supported
- * - EAFNOSUPPORT: Address family not supported by protocol family
- * - EADDRINUSE: Address already in use
- * - EADDRNOTAVAIL: Can't assign requested address
- * - ENETDOWN: Network is down
- * - ENETUNREACH: Network is unreachable
- * - ENETRESET: Network dropped connection on reset
- * - ECONNABORTED: Software caused connection abort
- * - ECONNRESET: Connection reset by peer
- * - ENOBUFS: No buffer space available
- * - EISCONN: Socket is already connected
- * - ENOTCONN: Socket is not connected
- * - ESHUTDOWN: Can't send after socket shutdown
- * - ETOOMANYREFS: Too many references: can't splice
- * - ETIMEDOUT: Connection timed out
- * - ECONNREFUSED: Connection refused
- * - ELOOP: Too many levels of symbolic links
- * - ENAMETOOLONG: File name too long
- * - EHOSTDOWN: Host is down
- * - EHOSTUNREACH: No route to host
- * - ENOTEMPTY: Directory not empty
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- *
- * \note Applications must not call this function to retrieve the
- * error code if NutTcpCloseSocket() or NutTcpDestroySocket()
- * failed.
- *
- * \todo Not all error codes are properly set right now. Some socket
- * functions return an error without setting an error code.
- */
- int NutTcpError(TCPSOCKET * sock)
- {
- if (sock == 0)
- return ENOTSOCK;
- return sock->so_last_error;
- }
- /*!
- * \brief Read from virtual socket device.
- *
- * TCP sockets can be used like other Nut/OS devices. This routine
- * is part of the virtual socket device driver.
- *
- * This function is called by the low level input routines of the
- * \ref xrCrtLowio "C runtime library", using the _NUTDEVICE::dev_read
- * entry.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param buffer Pointer to the buffer that receives the data.
- * \param size Maximum number of bytes to read.
- *
- * \return The number of bytes read, which may be less than the number
- * of bytes specified. A return value of -1 indicates an error,
- * while zero is returned in case of a timeout.
- */
- int NutTcpDeviceRead(TCPSOCKET * sock, void *buffer, int size)
- {
- return NutTcpReceive(sock, buffer, size);
- }
- static int SendBuffer(TCPSOCKET * sock, const void *buffer, int size)
- {
- int rc;
- int bite;
- for (rc = 0; rc < size; rc += bite) {
- if ((bite = NutTcpSend(sock, (uint8_t *) buffer + rc, size - rc)) <= 0) {
- return -1;
- }
- }
- return rc;
- }
- /*!
- * \brief Write to a socket.
- *
- * TCP sockets can be used like other Nut/OS devices. This routine
- * is part of the virtual socket device driver.
- *
- * This function is called by the low level output routines of the
- * \ref xrCrtLowio "C runtime library", using the
- * \ref _NUTDEVICE::dev_write entry.
- *
- * In contrast to NutTcpSend() this routine provides some buffering.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param buf Pointer to the data to be written.
- * \param size Number of bytes to write. If zero, then the output buffer
- * will be flushed.
- *
- * \return The number of bytes written. A return value of -1 indicates
- * an error.
- *
- */
- int NutTcpDeviceWrite(TCPSOCKET * sock, const void *buf, int size)
- {
- int rc;
- uint16_t sz;
- /* hack alert for ICCAVR */
- uint8_t *buffer = (uint8_t*) buf;
- /*
- * Check parameters.
- */
- if (sock == 0)
- return -1;
- if (sock->so_state != TCPS_ESTABLISHED) {
- sock->so_last_error = ENOTCONN;
- return -1;
- }
- /* Flush buffer? */
- if (size == 0) {
- if (sock->so_devocnt) {
- if (SendBuffer(sock, sock->so_devobuf, sock->so_devocnt) < 0) {
- free(sock->so_devobuf);
- sock->so_devocnt = 0;
- return -1;
- }
- free(sock->so_devobuf);
- sock->so_devocnt = 0;
- }
- return 0;
- }
- /* If we don't have a buffer so far... */
- if (sock->so_devocnt == 0) {
- /* If new data block is bigger or equal than buffer size
- * send first part of data to NIC and store remaining
- * bytes in buffer
- */
- if ((uint16_t) size >= sock->so_devobsz) {
- rc = size % sock->so_devobsz;
- if (SendBuffer(sock, buffer, size - rc) < 0)
- return -1;
- buffer += size - rc;
- } else
- rc = size;
- /* If there are some remainings bytes, allocate buffer
- * and store them
- */
- if (rc) {
- if (!(sock->so_devobuf = malloc(sock->so_devobsz)))
- return -1;
- memcpy(sock->so_devobuf, buffer, rc);
- sock->so_devocnt = rc;
- }
- return size;
- }
- /* Check if new data fully fits in output buffer */
- if (sock->so_devocnt + size < sock->so_devobsz) {
- memcpy(sock->so_devobuf + sock->so_devocnt, buffer, size);
- sock->so_devocnt += size;
- return size;
- }
- /* Otherwise store first bytes of new data in buffer and flush
- * the buffer
- */
- sz = sock->so_devobsz - sock->so_devocnt;
- memcpy(sock->so_devobuf + sock->so_devocnt, buffer, sz);
- buffer += sz;
- if (SendBuffer(sock, sock->so_devobuf, sock->so_devobsz) < 0) {
- free(sock->so_devobuf);
- sock->so_devocnt = 0;
- return -1;
- }
- /* If remaining data is bigger or equal than buffer size
- * send first part of data to NIC and later store remaining
- * bytes in buffer
- */
- sz = size - sz;
- if (sz >= sock->so_devobsz) {
- rc = sz % sock->so_devobsz;
- if (SendBuffer(sock, buffer, sz - rc) < 0) {
- free(sock->so_devobuf);
- sock->so_devocnt = 0;
- return -1;
- }
- buffer += sz - rc;
- } else
- rc = sz;
- /* If there are some remaining bytes, store them in buffer
- */
- if (rc)
- memcpy(sock->so_devobuf, buffer, rc);
- else /* Otherwise free buffer */
- free(sock->so_devobuf);
- sock->so_devocnt = rc;
- return size;
- }
- /*!
- * \brief Write to device.
- *
- * This function is implemented for CPUs with Harvard Architecture
- * only.
- *
- * TCP sockets can be used like other Nut/OS devices. This routine
- * is part of the virtual socket device driver and similar to
- * NutTcpDeviceWrite() except that the data is located in program
- * memory.
- *
- * This function is called by the low level output routines of the
- * \ref xrCrtLowio "C runtime library", using the
- * \ref _NUTDEVICE::dev_write_P entry.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param buffer Pointer to the data in program space to be written.
- * \param size Number of bytes to write.
- *
- * \warning Inefficient implementation. No buffering has been
- * implemented. Thus, each call will result in a separate
- * TCP segment.
- */
- #ifdef __HARVARD_ARCH__
- int NutTcpDeviceWrite_P(TCPSOCKET * sock, PGM_P buffer, int size)
- {
- int rc;
- char *rp = 0;
- /*
- * Hack alert. Neither do we handle out of memory correctly
- * nor do we pass the PGM pointer to lower levels.
- */
- if (size && (rp = NutHeapAlloc(size)) != 0)
- memcpy_P(rp, buffer, size);
- rc = NutTcpDeviceWrite(sock, rp, size);
- if (rp)
- NutHeapFree(rp);
- return rc;
- }
- #endif
- /*!
- * \brief Driver control function.
- *
- * Used by the virtual device driver to modify or query device specific
- * settings.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param cmd Requested control function. May be set to one of the
- * following constants:
- * - \ref IOCTL_GETFILESIZE
- * - \ref IOCTL_GETINBUFCOUNT
- * - \ref IOCTL_GETOUTBUFCOUNT
- *
- * \param param Points to a buffer that contains any data required for
- * the given control function or receives data from that
- * function.
- * \return 0 on success, -1 otherwise.
- */
- int NutTcpDeviceIOCtl(TCPSOCKET * sock, int cmd, void *param)
- {
- uint32_t *lvp = (uint32_t *) param;
- int rc = 0;
- switch (cmd) {
- case IOCTL_GETFILESIZE:
- case IOCTL_GETINBUFCOUNT:
- *lvp = (sock->so_rx_cnt - sock->so_rd_cnt);
- break;
- case IOCTL_GETOUTBUFCOUNT:
- *lvp = (sock->so_devocnt);
- break;
- default:
- rc = -1;
- }
- return rc;
- }
- #ifndef CRT_DISABLE_SELECT_POLL
- /*!
- * \brief Callback function called by select routine to check, if read
- * or write to this socket would block
- * \internal
- *
- * This function is called by select_scan routine of the C runtime library
- * as part of the select() implementation. If select if currently waiting
- * on a waitqueue, this function will be called to check the current state
- * as soon as the select waitqueues got signalled by the tcp statemachine
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param flags Flags representing what we are waiting for (read / write / exception)
- * \param wq Waitqueue, which should be added to the drivers waitqueue list. The
- * thread that called select is waiting on this waitqueue
- * \param cmd Internal command, that is passed to NutSelectManageWq
- *
- * \return Flags representing the current filedescriptor state
- */
- int NutTcpDeviceSelect (TCPSOCKET * sock, int flags, HANDLE *wq, select_cmd_t cmd)
- {
- int rflags = 0;
- /* Manage the wait queue lists for the select call */
- NutSelectManageWq(&sock->so_rx_wq_list, wq, flags & WQ_FLAG_READ, cmd);
- NutSelectManageWq(&sock->so_tx_wq_list, wq, flags & WQ_FLAG_WRITE, cmd);
- if (sock->so_state == TCPS_ESTABLISHED) {
- /* Check if there is some data available for reading (compare to NutTcpReceive) */
- if (sock->so_rx_cnt - sock->so_rd_cnt > 0) {
- rflags |= WQ_FLAG_READ;
- }
- /* Check if there is some free space in the output buffer. */
- if (sock->so_devocnt < sock->so_devobsz) {
- rflags |= WQ_FLAG_WRITE;
- }
- }
- if ((sock->so_state == TCPS_CLOSED) || (sock->so_state == TCPS_CLOSE_WAIT) || (sock->so_state == TCPS_DESTROY)) {
- /* The socket has been closed. Signal for read and write.
- Send and receive routines will then return EOF.
- */
- rflags |= WQ_FLAG_READ | WQ_FLAG_WRITE;
- }
- rflags &= flags;
- return rflags;
- }
- #endif
- /*@}*/
|