OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [ecos-2.0/] [packages/] [redboot/] [v2_0/] [src/] [net/] [tcp.c] - Diff between revs 1254 and 1765

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 1254 Rev 1765
//==========================================================================
//==========================================================================
//
//
//      net/tcp.c
//      net/tcp.c
//
//
//      Stand-alone TCP networking support for RedBoot
//      Stand-alone TCP networking support for RedBoot
//
//
//==========================================================================
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
//
// eCos is free software; you can redistribute it and/or modify it under
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
// Software Foundation; either version 2 or (at your option) any later version.
//
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
// for more details.
//
//
// You should have received a copy of the GNU General Public License along
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
//
// As a special exception, if other files instantiate templates or use macros
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
// in accordance with section (3) of the GNU General Public License.
//
//
// This exception does not invalidate any other reasons why a work based on
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
// this file might be covered by the GNU General Public License.
//
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//==========================================================================
//#####DESCRIPTIONBEGIN####
//#####DESCRIPTIONBEGIN####
//
//
// Author(s):    gthomas
// Author(s):    gthomas
// Contributors: gthomas
// Contributors: gthomas
// Date:         2000-07-14
// Date:         2000-07-14
// Purpose:      
// Purpose:      
// Description:  
// Description:  
//              
//              
// This code is part of RedBoot (tm).
// This code is part of RedBoot (tm).
//
//
//####DESCRIPTIONEND####
//####DESCRIPTIONEND####
//
//
//==========================================================================
//==========================================================================
 
 
#include <net/net.h>
#include <net/net.h>
 
 
#define MAX_TCP_SEGMENT (ETH_MAX_PKTLEN - (sizeof(eth_header_t) + sizeof(ip_header_t)))
#define MAX_TCP_SEGMENT (ETH_MAX_PKTLEN - (sizeof(eth_header_t) + sizeof(ip_header_t)))
#define MAX_TCP_DATA    (MAX_TCP_SEGMENT - sizeof(tcp_header_t))
#define MAX_TCP_DATA    (MAX_TCP_SEGMENT - sizeof(tcp_header_t))
 
 
 
 
/* sequence number comparison macros */
/* sequence number comparison macros */
#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
#define SEQ_LE(a,b) ((int)((a)-(b)) <= 0)
#define SEQ_LE(a,b) ((int)((a)-(b)) <= 0)
#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
#define SEQ_GE(a,b) ((int)((a)-(b)) >= 0)
#define SEQ_GE(a,b) ((int)((a)-(b)) >= 0)
 
 
/* Set a timer which will send an RST and abort a connection. */
/* Set a timer which will send an RST and abort a connection. */
static timer_t abort_timer;
static timer_t abort_timer;
 
 
static void do_retrans(void *p);
static void do_retrans(void *p);
static void do_close(void *p);
static void do_close(void *p);
 
 
#ifdef BSP_LOG
#ifdef BSP_LOG
static char *
static char *
flags_to_str(octet f)
flags_to_str(octet f)
{
{
    static char str[7], *p;
    static char str[7], *p;
 
 
    p = str;
    p = str;
 
 
    if (f & TCP_FLAG_FIN)
    if (f & TCP_FLAG_FIN)
        *p++ = 'F';
        *p++ = 'F';
    if (f & TCP_FLAG_SYN)
    if (f & TCP_FLAG_SYN)
        *p++ = 'S';
        *p++ = 'S';
    if (f & TCP_FLAG_RST)
    if (f & TCP_FLAG_RST)
        *p++ = 'R';
        *p++ = 'R';
    if (f & TCP_FLAG_PSH)
    if (f & TCP_FLAG_PSH)
        *p++ = 'P';
        *p++ = 'P';
    if (f & TCP_FLAG_ACK)
    if (f & TCP_FLAG_ACK)
        *p++ = 'A';
        *p++ = 'A';
    if (f & TCP_FLAG_URG)
    if (f & TCP_FLAG_URG)
        *p++ = 'U';
        *p++ = 'U';
    *p = '\0';
    *p = '\0';
    return str;
    return str;
}
}
#endif
#endif
 
 
/*
/*
 * A major assumption is that only a very small number of sockets will
 * A major assumption is that only a very small number of sockets will
 * active, so a simple linear search of those sockets is acceptible.
 * active, so a simple linear search of those sockets is acceptible.
 */
 */
static tcp_socket_t *tcp_list;
static tcp_socket_t *tcp_list;
 
 
/*
/*
 * Format and send an outgoing segment.
 * Format and send an outgoing segment.
 */
 */
static void
static void
tcp_send(tcp_socket_t *s, int flags, int resend)
tcp_send(tcp_socket_t *s, int flags, int resend)
{
{
    tcp_header_t *tcp;
    tcp_header_t *tcp;
    ip_header_t  *ip;
    ip_header_t  *ip;
    pktbuf_t     *pkt = &s->pkt;
    pktbuf_t     *pkt = &s->pkt;
    unsigned short cksum;
    unsigned short cksum;
    dword         tcp_magic;
    dword         tcp_magic;
    int           tcp_magic_size = sizeof(tcp_magic);
    int           tcp_magic_size = sizeof(tcp_magic);
 
 
    ip = pkt->ip_hdr;
    ip = pkt->ip_hdr;
    tcp = pkt->tcp_hdr;
    tcp = pkt->tcp_hdr;
 
 
    if (flags & TCP_FLAG_SYN) {
    if (flags & TCP_FLAG_SYN) {
        /* If SYN, assume no data and send MSS option in tcp header */
        /* If SYN, assume no data and send MSS option in tcp header */
        pkt->pkt_bytes = sizeof(tcp_header_t) + 4;
        pkt->pkt_bytes = sizeof(tcp_header_t) + 4;
        tcp->hdr_len = 6;
        tcp->hdr_len = 6;
        tcp_magic = htonl(0x02040000 | MAX_TCP_DATA);
        tcp_magic = htonl(0x02040000 | MAX_TCP_DATA);
        memcpy((unsigned char *)(tcp+1), &tcp_magic, tcp_magic_size);
        memcpy((unsigned char *)(tcp+1), &tcp_magic, tcp_magic_size);
        s->data_bytes = 0;
        s->data_bytes = 0;
    } else {
    } else {
        pkt->pkt_bytes = s->data_bytes + sizeof(tcp_header_t);
        pkt->pkt_bytes = s->data_bytes + sizeof(tcp_header_t);
        tcp->hdr_len = 5;
        tcp->hdr_len = 5;
    }
    }
 
 
    /* tcp header */
    /* tcp header */
    tcp->reserved = 0;
    tcp->reserved = 0;
    tcp->seqnum = htonl(s->seq);
    tcp->seqnum = htonl(s->seq);
    tcp->acknum = htonl(s->ack);
    tcp->acknum = htonl(s->ack);
    tcp->checksum = 0;
    tcp->checksum = 0;
 
 
    if (!resend) {
    if (!resend) {
        tcp->src_port = htons(s->our_port);
        tcp->src_port = htons(s->our_port);
        tcp->dest_port = htons(s->his_port);
        tcp->dest_port = htons(s->his_port);
        tcp->flags = flags;
        tcp->flags = flags;
        /* always set PUSH flag if sending data */
        /* always set PUSH flag if sending data */
        if (s->data_bytes)
        if (s->data_bytes)
            tcp->flags |= TCP_FLAG_PSH;
            tcp->flags |= TCP_FLAG_PSH;
        tcp->window = htons(MAX_TCP_DATA);
        tcp->window = htons(MAX_TCP_DATA);
        tcp->urgent = 0;
        tcp->urgent = 0;
 
 
        /* fill in some pseudo-header fields */
        /* fill in some pseudo-header fields */
        memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
        memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
        memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
        memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
        ip->protocol = IP_PROTO_TCP;
        ip->protocol = IP_PROTO_TCP;
    }
    }
 
 
    /* another pseudo-header field */
    /* another pseudo-header field */
    ip->length = htons(pkt->pkt_bytes);
    ip->length = htons(pkt->pkt_bytes);
 
 
    /* compute tcp checksum */
    /* compute tcp checksum */
    cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
    cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
    tcp->checksum = htons(cksum);
    tcp->checksum = htons(cksum);
 
 
    __ip_send(pkt, IP_PROTO_TCP, &s->his_addr);
    __ip_send(pkt, IP_PROTO_TCP, &s->his_addr);
 
 
    BSPLOG(bsp_log("tcp_send: state[%d] flags[%s] ack[%x] data[%d].\n",
    BSPLOG(bsp_log("tcp_send: state[%d] flags[%s] ack[%x] data[%d].\n",
                   s->state, flags_to_str(tcp->flags), s->ack, s->data_bytes));
                   s->state, flags_to_str(tcp->flags), s->ack, s->data_bytes));
 
 
    if (s->state == _TIME_WAIT) {
    if (s->state == _TIME_WAIT) {
        // If 'reuse' is set on socket, close after 1 second, otherwise 2 minutes
        // If 'reuse' is set on socket, close after 1 second, otherwise 2 minutes
        __timer_set(&s->timer, s->reuse ? 1000 : 120000, do_close, s);
        __timer_set(&s->timer, s->reuse ? 1000 : 120000, do_close, s);
    }
    }
    else if ((tcp->flags & (TCP_FLAG_FIN | TCP_FLAG_SYN)) || s->data_bytes)
    else if ((tcp->flags & (TCP_FLAG_FIN | TCP_FLAG_SYN)) || s->data_bytes)
        __timer_set(&s->timer, 1000, do_retrans, s);
        __timer_set(&s->timer, 1000, do_retrans, s);
}
}
 
 
static pktbuf_t ack_pkt;
static pktbuf_t ack_pkt;
static word     ack_buf[ETH_MIN_PKTLEN/sizeof(word)];
static word     ack_buf[ETH_MIN_PKTLEN/sizeof(word)];
 
 
/*
/*
 * Send an ack.
 * Send an ack.
 */
 */
static void
static void
send_ack(tcp_socket_t *s)
send_ack(tcp_socket_t *s)
{
{
    tcp_header_t *tcp;
    tcp_header_t *tcp;
    ip_header_t  *ip;
    ip_header_t  *ip;
    unsigned short cksum;
    unsigned short cksum;
 
 
    ack_pkt.buf = ack_buf;
    ack_pkt.buf = ack_buf;
    ack_pkt.bufsize = sizeof(ack_buf);
    ack_pkt.bufsize = sizeof(ack_buf);
    ack_pkt.ip_hdr = ip = (ip_header_t *)ack_buf;
    ack_pkt.ip_hdr = ip = (ip_header_t *)ack_buf;
    ack_pkt.tcp_hdr = tcp = (tcp_header_t *)(ip + 1);
    ack_pkt.tcp_hdr = tcp = (tcp_header_t *)(ip + 1);
    ack_pkt.pkt_bytes = sizeof(tcp_header_t);
    ack_pkt.pkt_bytes = sizeof(tcp_header_t);
 
 
    /* tcp header */
    /* tcp header */
    tcp->hdr_len = 5;
    tcp->hdr_len = 5;
    tcp->reserved = 0;
    tcp->reserved = 0;
    tcp->seqnum = htonl(s->seq);
    tcp->seqnum = htonl(s->seq);
    tcp->acknum = htonl(s->ack);
    tcp->acknum = htonl(s->ack);
    tcp->checksum = 0;
    tcp->checksum = 0;
 
 
    tcp->src_port = htons(s->our_port);
    tcp->src_port = htons(s->our_port);
    tcp->dest_port = htons(s->his_port);
    tcp->dest_port = htons(s->his_port);
    tcp->flags = TCP_FLAG_ACK;
    tcp->flags = TCP_FLAG_ACK;
 
 
    tcp->window = htons(MAX_TCP_DATA);
    tcp->window = htons(MAX_TCP_DATA);
    tcp->urgent = 0;
    tcp->urgent = 0;
 
 
    /* fill in some pseudo-header fields */
    /* fill in some pseudo-header fields */
    memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
    memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
    memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
    memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
    ip->protocol = IP_PROTO_TCP;
    ip->protocol = IP_PROTO_TCP;
 
 
    /* another pseudo-header field */
    /* another pseudo-header field */
    ip->length = htons(sizeof(tcp_header_t));
    ip->length = htons(sizeof(tcp_header_t));
 
 
    /* compute tcp checksum */
    /* compute tcp checksum */
    cksum = __sum((word *)tcp, sizeof(*tcp), __pseudo_sum(ip));
    cksum = __sum((word *)tcp, sizeof(*tcp), __pseudo_sum(ip));
    tcp->checksum = htons(cksum);
    tcp->checksum = htons(cksum);
 
 
    __ip_send(&ack_pkt, IP_PROTO_TCP, &s->his_addr);
    __ip_send(&ack_pkt, IP_PROTO_TCP, &s->his_addr);
}
}
 
 
 
 
/*
/*
 * Send a reset for a bogus incoming segment.
 * Send a reset for a bogus incoming segment.
 */
 */
static void
static void
send_reset(pktbuf_t *pkt, ip_route_t *r)
send_reset(pktbuf_t *pkt, ip_route_t *r)
{
{
    ip_header_t   *ip = pkt->ip_hdr;
    ip_header_t   *ip = pkt->ip_hdr;
    tcp_header_t  *tcp = pkt->tcp_hdr;
    tcp_header_t  *tcp = pkt->tcp_hdr;
    dword         seq, ack;
    dword         seq, ack;
    word          src, dest;
    word          src, dest;
    word          cksum;
    word          cksum;
 
 
    seq = ntohl(tcp->acknum);
    seq = ntohl(tcp->acknum);
    ack = ntohl(tcp->seqnum);
    ack = ntohl(tcp->seqnum);
    src = ntohs(tcp->dest_port);
    src = ntohs(tcp->dest_port);
    dest = ntohs(tcp->src_port);
    dest = ntohs(tcp->src_port);
 
 
    tcp = (tcp_header_t *)(ip + 1);
    tcp = (tcp_header_t *)(ip + 1);
    pkt->pkt_bytes = sizeof(tcp_header_t);
    pkt->pkt_bytes = sizeof(tcp_header_t);
 
 
    /* tcp header */
    /* tcp header */
    tcp->hdr_len = 5;
    tcp->hdr_len = 5;
    tcp->reserved = 0;
    tcp->reserved = 0;
    tcp->seqnum = htonl(seq);
    tcp->seqnum = htonl(seq);
    tcp->acknum = htonl(ack);
    tcp->acknum = htonl(ack);
    tcp->window = htons(1024);
    tcp->window = htons(1024);
    tcp->urgent = 0;
    tcp->urgent = 0;
    tcp->checksum = 0;
    tcp->checksum = 0;
    tcp->src_port = htons(src);
    tcp->src_port = htons(src);
    tcp->dest_port = htons(dest);
    tcp->dest_port = htons(dest);
    tcp->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
    tcp->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
 
 
    /* fill in some pseudo-header fields */
    /* fill in some pseudo-header fields */
    memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
    memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
    memcpy(ip->destination, r->ip_addr, sizeof(ip_addr_t));
    memcpy(ip->destination, r->ip_addr, sizeof(ip_addr_t));
    ip->protocol = IP_PROTO_TCP;
    ip->protocol = IP_PROTO_TCP;
    ip->length = htons(pkt->pkt_bytes);
    ip->length = htons(pkt->pkt_bytes);
 
 
    /* compute tcp checksum */
    /* compute tcp checksum */
    cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
    cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
    tcp->checksum = htons(cksum);
    tcp->checksum = htons(cksum);
 
 
    __ip_send(pkt, IP_PROTO_TCP, r);
    __ip_send(pkt, IP_PROTO_TCP, r);
}
}
 
 
 
 
 
 
/*
/*
 * Remove given socket from socket list.
 * Remove given socket from socket list.
 */
 */
static void
static void
unlink_socket(tcp_socket_t *s)
unlink_socket(tcp_socket_t *s)
{
{
    tcp_socket_t *prev, *tp;
    tcp_socket_t *prev, *tp;
 
 
    for (prev = NULL, tp = tcp_list; tp; prev = tp, tp = tp->next)
    for (prev = NULL, tp = tcp_list; tp; prev = tp, tp = tp->next)
        if (tp == s) {
        if (tp == s) {
            BSPLOG(bsp_log("unlink tcp socket.\n"));
            BSPLOG(bsp_log("unlink tcp socket.\n"));
            if (prev)
            if (prev)
                prev->next = s->next;
                prev->next = s->next;
            else
            else
                tcp_list = s->next;
                tcp_list = s->next;
        }
        }
}
}
 
 
/*
/*
 * Retransmit last packet.
 * Retransmit last packet.
 */
 */
static void
static void
do_retrans(void *p)
do_retrans(void *p)
{
{
    BSPLOG(bsp_log("tcp do_retrans.\n"));
    BSPLOG(bsp_log("tcp do_retrans.\n"));
    tcp_send((tcp_socket_t *)p, 0, 1);
    tcp_send((tcp_socket_t *)p, 0, 1);
}
}
 
 
 
 
static void
static void
do_close(void *p)
do_close(void *p)
{
{
    BSPLOG(bsp_log("tcp do_close.\n"));
    BSPLOG(bsp_log("tcp do_close.\n"));
    /* close connection */
    /* close connection */
    ((tcp_socket_t *)p)->state = _CLOSED;
    ((tcp_socket_t *)p)->state = _CLOSED;
    unlink_socket(p);
    unlink_socket(p);
}
}
 
 
 
 
static void
static void
free_rxlist(tcp_socket_t *s)
free_rxlist(tcp_socket_t *s)
{
{
    pktbuf_t *p;
    pktbuf_t *p;
 
 
    BSPLOG(bsp_log("tcp free_rxlist.\n"));
    BSPLOG(bsp_log("tcp free_rxlist.\n"));
 
 
    while ((p = s->rxlist) != NULL) {
    while ((p = s->rxlist) != NULL) {
        s->rxlist = p->next;
        s->rxlist = p->next;
        __pktbuf_free(p);
        __pktbuf_free(p);
    }
    }
}
}
 
 
 
 
/*
/*
 * Handle a conection reset.
 * Handle a conection reset.
 */
 */
static void
static void
do_reset(tcp_socket_t *s)
do_reset(tcp_socket_t *s)
{
{
    /* close connection */
    /* close connection */
    s->state = _CLOSED;
    s->state = _CLOSED;
    __timer_cancel(&s->timer);
    __timer_cancel(&s->timer);
    free_rxlist(s);
    free_rxlist(s);
    unlink_socket(s);
    unlink_socket(s);
}
}
 
 
 
 
/*
/*
 * Extract data from incoming tcp segment.
 * Extract data from incoming tcp segment.
 * Returns true if packet is queued on rxlist, false otherwise.
 * Returns true if packet is queued on rxlist, false otherwise.
 */
 */
static int
static int
handle_data(tcp_socket_t *s, pktbuf_t *pkt)
handle_data(tcp_socket_t *s, pktbuf_t *pkt)
{
{
    tcp_header_t  *tcp = pkt->tcp_hdr;
    tcp_header_t  *tcp = pkt->tcp_hdr;
    unsigned int  diff, seq;
    unsigned int  diff, seq;
    int           data_len;
    int           data_len;
    char          *data_ptr;
    char          *data_ptr;
    pktbuf_t      *p;
    pktbuf_t      *p;
 
 
    data_len = pkt->pkt_bytes - (tcp->hdr_len << 2);
    data_len = pkt->pkt_bytes - (tcp->hdr_len << 2);
    data_ptr = ((char *)tcp)  + (tcp->hdr_len << 2);
    data_ptr = ((char *)tcp)  + (tcp->hdr_len << 2);
 
 
    seq = ntohl(tcp->seqnum);
    seq = ntohl(tcp->seqnum);
 
 
    BSPLOG(bsp_log("tcp data: seq[%x] len[%d].\n", seq, data_len));
    BSPLOG(bsp_log("tcp data: seq[%x] len[%d].\n", seq, data_len));
 
 
    if (SEQ_LE(seq, s->ack)) {
    if (SEQ_LE(seq, s->ack)) {
        /*
        /*
         * Figure difference between which byte we're expecting and which byte
         * Figure difference between which byte we're expecting and which byte
         * is sent first. Adjust data length and data pointer accordingly.
         * is sent first. Adjust data length and data pointer accordingly.
         */
         */
        diff = s->ack - seq;
        diff = s->ack - seq;
        data_len -= diff;
        data_len -= diff;
        data_ptr += diff;
        data_ptr += diff;
 
 
        if (data_len > 0) {
        if (data_len > 0) {
            /* queue the new data */
            /* queue the new data */
            s->ack += data_len;
            s->ack += data_len;
            pkt->next = NULL;
            pkt->next = NULL;
            if ((p = s->rxlist) != NULL) {
            if ((p = s->rxlist) != NULL) {
                while (p->next)
                while (p->next)
                    p = p->next;
                    p = p->next;
                p->next = pkt;
                p->next = pkt;
                BSPLOG(bsp_log("tcp data: Add pkt[%x] len[%d].\n",
                BSPLOG(bsp_log("tcp data: Add pkt[%x] len[%d].\n",
                               pkt, data_len));
                               pkt, data_len));
            } else {
            } else {
                s->rxlist = pkt;
                s->rxlist = pkt;
                s->rxcnt = data_len;
                s->rxcnt = data_len;
                s->rxptr = data_ptr;
                s->rxptr = data_ptr;
                BSPLOG(bsp_log("tcp data: pkt[%x] len[%d].\n",
                BSPLOG(bsp_log("tcp data: pkt[%x] len[%d].\n",
                               pkt, data_len));
                               pkt, data_len));
            }
            }
            return 1;
            return 1;
        }
        }
    }
    }
    return 0;
    return 0;
}
}
 
 
 
 
static void
static void
handle_ack(tcp_socket_t *s, pktbuf_t *pkt)
handle_ack(tcp_socket_t *s, pktbuf_t *pkt)
{
{
    tcp_header_t *tcp = pkt->tcp_hdr;
    tcp_header_t *tcp = pkt->tcp_hdr;
    dword        ack;
    dword        ack;
    int          advance;
    int          advance;
    char         *dp;
    char         *dp;
 
 
    /* process ack value in packet */
    /* process ack value in packet */
    ack = ntohl(tcp->acknum);
    ack = ntohl(tcp->acknum);
 
 
    BSPLOG(bsp_log("Rcvd tcp ACK %x\n", ack));
    BSPLOG(bsp_log("Rcvd tcp ACK %x\n", ack));
 
 
    if (SEQ_GT(ack, s->seq)) {
    if (SEQ_GT(ack, s->seq)) {
        __timer_cancel(&s->timer);
        __timer_cancel(&s->timer);
        advance = ack - s->seq;
        advance = ack - s->seq;
        if (advance > s->data_bytes)
        if (advance > s->data_bytes)
            advance = s->data_bytes;
            advance = s->data_bytes;
 
 
        BSPLOG(bsp_log("seq advance %d", advance));
        BSPLOG(bsp_log("seq advance %d", advance));
 
 
        if (advance > 0) {
        if (advance > 0) {
            s->seq += advance;
            s->seq += advance;
            s->data_bytes -= advance;
            s->data_bytes -= advance;
            if (s->data_bytes) {
            if (s->data_bytes) {
                /* other end ack'd only part of the pkt */
                /* other end ack'd only part of the pkt */
                BSPLOG(bsp_log(" %d bytes left", s->data_bytes));
                BSPLOG(bsp_log(" %d bytes left", s->data_bytes));
                dp = (char *)(s->pkt.tcp_hdr + 1);
                dp = (char *)(s->pkt.tcp_hdr + 1);
                memcpy(dp, dp + advance, s->data_bytes);
                memcpy(dp, dp + advance, s->data_bytes);
            }
            }
        }
        }
    }
    }
    BSPLOG(bsp_log("\n"));
    BSPLOG(bsp_log("\n"));
}
}
 
 
 
 
/*
/*
 * Handle incoming TCP packets.
 * Handle incoming TCP packets.
 */
 */
void
void
__tcp_handler(pktbuf_t *pkt, ip_route_t *r)
__tcp_handler(pktbuf_t *pkt, ip_route_t *r)
{
{
    tcp_header_t *tcp = pkt->tcp_hdr;
    tcp_header_t *tcp = pkt->tcp_hdr;
    ip_header_t  *ip = pkt->ip_hdr;
    ip_header_t  *ip = pkt->ip_hdr;
    tcp_socket_t *prev,*s;
    tcp_socket_t *prev,*s;
    dword        ack;
    dword        ack;
    int          queued = 0;
    int          queued = 0;
 
 
    /* set length for pseudo sum calculation */
    /* set length for pseudo sum calculation */
    ip->length = htons(pkt->pkt_bytes);
    ip->length = htons(pkt->pkt_bytes);
 
 
    if (__sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip)) == 0) {
    if (__sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip)) == 0) {
        for (prev = NULL, s = tcp_list; s; prev = s, s = s->next) {
        for (prev = NULL, s = tcp_list; s; prev = s, s = s->next) {
            if (s->our_port == ntohs(tcp->dest_port)) {
            if (s->our_port == ntohs(tcp->dest_port)) {
                if (s->his_port == 0)
                if (s->his_port == 0)
                    break;
                    break;
                if (s->his_port == ntohs(tcp->src_port) &&
                if (s->his_port == ntohs(tcp->src_port) &&
                    !memcmp(r->ip_addr, s->his_addr.ip_addr, sizeof(ip_addr_t)))
                    !memcmp(r->ip_addr, s->his_addr.ip_addr, sizeof(ip_addr_t)))
                    break;
                    break;
            }
            }
        }
        }
 
 
        if (s) {
        if (s) {
            /* found the socket this packet belongs to */
            /* found the socket this packet belongs to */
 
 
            /* refresh his ethernet address */
            /* refresh his ethernet address */
            memcpy(s->his_addr.enet_addr, r->enet_addr, sizeof(enet_addr_t));
            memcpy(s->his_addr.enet_addr, r->enet_addr, sizeof(enet_addr_t));
 
 
            if (s->state != _SYN_RCVD && tcp->flags & TCP_FLAG_RST) {
            if (s->state != _SYN_RCVD && tcp->flags & TCP_FLAG_RST) {
                BSPLOG(bsp_log("TCP_FLAG_RST rcvd\n"));
                BSPLOG(bsp_log("TCP_FLAG_RST rcvd\n"));
                do_reset(s);
                do_reset(s);
                __pktbuf_free(pkt);
                __pktbuf_free(pkt);
                return;
                return;
            }
            }
 
 
            switch (s->state) {
            switch (s->state) {
 
 
              case _SYN_SENT:
              case _SYN_SENT:
                /* active open not supported */
                /* active open not supported */
                  if (tcp->flags != (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
                  if (tcp->flags != (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
                      do_reset(s);
                      do_reset(s);
                      __pktbuf_free(pkt);
                      __pktbuf_free(pkt);
                      return;
                      return;
                  }
                  }
                  s->state = _ESTABLISHED;
                  s->state = _ESTABLISHED;
                  s->ack = ntohl(tcp->seqnum) + 1;
                  s->ack = ntohl(tcp->seqnum) + 1;
                  s->seq = ntohl(tcp->acknum);
                  s->seq = ntohl(tcp->acknum);
                  send_ack(s);
                  send_ack(s);
                break;
                break;
 
 
              case _LISTEN:
              case _LISTEN:
                if (tcp->flags & TCP_FLAG_SYN) {
                if (tcp->flags & TCP_FLAG_SYN) {
                    s->state = _SYN_RCVD;
                    s->state = _SYN_RCVD;
                    s->ack = ntohl(tcp->seqnum) + 1;
                    s->ack = ntohl(tcp->seqnum) + 1;
                    s->his_port = ntohs(tcp->src_port);
                    s->his_port = ntohs(tcp->src_port);
                    memcpy(s->his_addr.ip_addr, r->ip_addr, sizeof(ip_addr_t));
                    memcpy(s->his_addr.ip_addr, r->ip_addr, sizeof(ip_addr_t));
                    s->data_bytes = 0;
                    s->data_bytes = 0;
 
 
                    BSPLOG(bsp_log("SYN from %d.%d.%d.%d:%d (seq %x)\n",
                    BSPLOG(bsp_log("SYN from %d.%d.%d.%d:%d (seq %x)\n",
                               s->his_addr.ip_addr[0],s->his_addr.ip_addr[1],
                               s->his_addr.ip_addr[0],s->his_addr.ip_addr[1],
                               s->his_addr.ip_addr[2],s->his_addr.ip_addr[3],
                               s->his_addr.ip_addr[2],s->his_addr.ip_addr[3],
                               s->his_port, ntohl(tcp->seqnum)));
                               s->his_port, ntohl(tcp->seqnum)));
 
 
                    tcp_send(s, TCP_FLAG_SYN | TCP_FLAG_ACK, 0);
                    tcp_send(s, TCP_FLAG_SYN | TCP_FLAG_ACK, 0);
                }
                }
                else
                else
                    send_reset(pkt, r);
                    send_reset(pkt, r);
                break;
                break;
 
 
              case _SYN_RCVD:
              case _SYN_RCVD:
                BSPLOG(bsp_log("_SYN_RCVD timer cancel.\n"));
                BSPLOG(bsp_log("_SYN_RCVD timer cancel.\n"));
                __timer_cancel(&s->timer);
                __timer_cancel(&s->timer);
 
 
                /* go back to _LISTEN state if reset */
                /* go back to _LISTEN state if reset */
                if (tcp->flags & TCP_FLAG_RST) {
                if (tcp->flags & TCP_FLAG_RST) {
                    s->state = _LISTEN;
                    s->state = _LISTEN;
 
 
                    BSPLOG(bsp_log("_SYN_RCVD --> _LISTEN\n"));
                    BSPLOG(bsp_log("_SYN_RCVD --> _LISTEN\n"));
 
 
                } else if (tcp->flags & TCP_FLAG_SYN) {
                } else if (tcp->flags & TCP_FLAG_SYN) {
                    /* apparently our SYN/ACK was lost? */
                    /* apparently our SYN/ACK was lost? */
                    tcp_send(s, 0, 1);
                    tcp_send(s, 0, 1);
 
 
                    BSPLOG(bsp_log("retransmitting SYN/ACK\n"));
                    BSPLOG(bsp_log("retransmitting SYN/ACK\n"));
 
 
                } else if ((tcp->flags & TCP_FLAG_ACK) &&
                } else if ((tcp->flags & TCP_FLAG_ACK) &&
                           ntohl(tcp->acknum) == (s->seq + 1)) {
                           ntohl(tcp->acknum) == (s->seq + 1)) {
                    /* we've established the connection */
                    /* we've established the connection */
                    s->state = _ESTABLISHED;
                    s->state = _ESTABLISHED;
                    s->seq++;
                    s->seq++;
 
 
                    BSPLOG(bsp_log("ACK received - connection established\n"));
                    BSPLOG(bsp_log("ACK received - connection established\n"));
                }
                }
                break;
                break;
 
 
              case _ESTABLISHED:
              case _ESTABLISHED:
              case _CLOSE_WAIT:
              case _CLOSE_WAIT:
                ack = s->ack;  /* save original ack */
                ack = s->ack;  /* save original ack */
                if (tcp->flags & TCP_FLAG_ACK)
                if (tcp->flags & TCP_FLAG_ACK)
                    handle_ack(s, pkt);
                    handle_ack(s, pkt);
 
 
                queued = handle_data(s, pkt);
                queued = handle_data(s, pkt);
 
 
                if ((tcp->flags & TCP_FLAG_FIN) &&
                if ((tcp->flags & TCP_FLAG_FIN) &&
                    ntohl(tcp->seqnum) == s->ack) {
                    ntohl(tcp->seqnum) == s->ack) {
 
 
                    BSPLOG(bsp_log("FIN received - going to _CLOSE_WAIT\n"));
                    BSPLOG(bsp_log("FIN received - going to _CLOSE_WAIT\n"));
 
 
                    s->ack++;
                    s->ack++;
                    s->state = _CLOSE_WAIT;
                    s->state = _CLOSE_WAIT;
                }
                }
                /*
                /*
                 * Send an ack if neccessary.
                 * Send an ack if neccessary.
                 */
                 */
                if (s->ack != ack || pkt->pkt_bytes > (tcp->hdr_len << 2))
                if (s->ack != ack || pkt->pkt_bytes > (tcp->hdr_len << 2))
                    send_ack(s);
                    send_ack(s);
                break;
                break;
 
 
              case _LAST_ACK:
              case _LAST_ACK:
                if (tcp->flags & TCP_FLAG_ACK) {
                if (tcp->flags & TCP_FLAG_ACK) {
                    handle_ack(s, pkt);
                    handle_ack(s, pkt);
                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
                        BSPLOG(bsp_log("_LAST_ACK --> _CLOSED\n"));
                        BSPLOG(bsp_log("_LAST_ACK --> _CLOSED\n"));
                        s->state = _CLOSED;
                        s->state = _CLOSED;
                        unlink_socket(s);
                        unlink_socket(s);
                    }
                    }
                }
                }
                break;
                break;
 
 
              case _FIN_WAIT_1:
              case _FIN_WAIT_1:
                if (tcp->flags & TCP_FLAG_ACK) {
                if (tcp->flags & TCP_FLAG_ACK) {
                    handle_ack(s, pkt);
                    handle_ack(s, pkt);
                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
                        /* got ACK for FIN packet */
                        /* got ACK for FIN packet */
                        s->seq++;
                        s->seq++;
                        if (tcp->flags & TCP_FLAG_FIN) {
                        if (tcp->flags & TCP_FLAG_FIN) {
                            BSPLOG(bsp_log("_FIN_WAIT_1 --> _TIME_WAIT\n"));
                            BSPLOG(bsp_log("_FIN_WAIT_1 --> _TIME_WAIT\n"));
                            s->ack++;
                            s->ack++;
                            s->state = _TIME_WAIT;
                            s->state = _TIME_WAIT;
                            send_ack(s);
                            send_ack(s);
                        } else {
                        } else {
                            s->state = _FIN_WAIT_2;
                            s->state = _FIN_WAIT_2;
                            BSPLOG(bsp_log("_FIN_WAIT_1 --> _FIN_WAIT_2\n"));
                            BSPLOG(bsp_log("_FIN_WAIT_1 --> _FIN_WAIT_2\n"));
                        }
                        }
                        break; /* All done for now */
                        break; /* All done for now */
                    }
                    }
                }
                }
                /* At this point, no ACK for FIN has been seen, so check for
                /* At this point, no ACK for FIN has been seen, so check for
                   simultaneous close */
                   simultaneous close */
                if (tcp->flags & TCP_FLAG_FIN) {
                if (tcp->flags & TCP_FLAG_FIN) {
                    BSPLOG(bsp_log("_FIN_WAIT_1 --> _CLOSING\n"));
                    BSPLOG(bsp_log("_FIN_WAIT_1 --> _CLOSING\n"));
                    __timer_cancel(&s->timer);
                    __timer_cancel(&s->timer);
                    s->ack++;
                    s->ack++;
                    s->state = _CLOSING;
                    s->state = _CLOSING;
                    /* FIN is resent so the timeout and retry for this packet
                    /* FIN is resent so the timeout and retry for this packet
                       will also take care of timeout and resend of the
                       will also take care of timeout and resend of the
                       previously sent FIN (which got us to FIN_WAIT_1). While
                       previously sent FIN (which got us to FIN_WAIT_1). While
                       not technically correct, resending FIN only causes a
                       not technically correct, resending FIN only causes a
                       duplicate FIN (same sequence number) which should be
                       duplicate FIN (same sequence number) which should be
                       ignored by the other end. */
                       ignored by the other end. */
                    tcp_send(s, TCP_FLAG_FIN | TCP_FLAG_ACK, 0);
                    tcp_send(s, TCP_FLAG_FIN | TCP_FLAG_ACK, 0);
                }
                }
                break;
                break;
 
 
              case _FIN_WAIT_2:
              case _FIN_WAIT_2:
                queued = handle_data(s, pkt);
                queued = handle_data(s, pkt);
                if (tcp->flags & TCP_FLAG_FIN) {
                if (tcp->flags & TCP_FLAG_FIN) {
                    BSPLOG(bsp_log("_FIN_WAIT_2 --> _TIME_WAIT\n"));
                    BSPLOG(bsp_log("_FIN_WAIT_2 --> _TIME_WAIT\n"));
                    s->ack++;
                    s->ack++;
                    s->state = _TIME_WAIT;
                    s->state = _TIME_WAIT;
                    send_ack(s);
                    send_ack(s);
                }
                }
                break;
                break;
 
 
              case _CLOSING:
              case _CLOSING:
                if (tcp->flags & TCP_FLAG_ACK) {
                if (tcp->flags & TCP_FLAG_ACK) {
                    handle_ack(s, pkt);
                    handle_ack(s, pkt);
                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
                        /* got ACK for FIN packet */
                        /* got ACK for FIN packet */
                        BSPLOG(bsp_log("_CLOSING --> _TIME_WAIT\n"));
                        BSPLOG(bsp_log("_CLOSING --> _TIME_WAIT\n"));
                        __timer_cancel(&s->timer);
                        __timer_cancel(&s->timer);
                        s->state = _TIME_WAIT;
                        s->state = _TIME_WAIT;
                    }
                    }
                }
                }
                break;
                break;
 
 
              case _TIME_WAIT:
              case _TIME_WAIT:
                BSPLOG(bsp_log("_TIME_WAIT resend.\n"));
                BSPLOG(bsp_log("_TIME_WAIT resend.\n"));
                if (tcp->flags & TCP_FLAG_FIN)
                if (tcp->flags & TCP_FLAG_FIN)
                    tcp_send(s, 0, 1); /* just resend ack */
                    tcp_send(s, 0, 1); /* just resend ack */
                break;
                break;
            }
            }
        } else {
        } else {
            BSPLOG(bsp_log("Unexpected segment from: %d.%d.%d.%d:%d\n",
            BSPLOG(bsp_log("Unexpected segment from: %d.%d.%d.%d:%d\n",
                           r->ip_addr[0], r->ip_addr[1], r->ip_addr[3],
                           r->ip_addr[0], r->ip_addr[1], r->ip_addr[3],
                           r->ip_addr[4], ntohs(tcp->src_port)));
                           r->ip_addr[4], ntohs(tcp->src_port)));
            send_reset(pkt, r);
            send_reset(pkt, r);
        }
        }
    }
    }
    if (!queued)
    if (!queued)
        __pktbuf_free(pkt);
        __pktbuf_free(pkt);
}
}
 
 
 
 
void
void
__tcp_poll(void)
__tcp_poll(void)
{
{
    __enet_poll();
    __enet_poll();
    MS_TICKS_DELAY();
    MS_TICKS_DELAY();
    __timer_poll();
    __timer_poll();
}
}
 
 
 
 
int
int
__tcp_listen(tcp_socket_t *s, word port)
__tcp_listen(tcp_socket_t *s, word port)
{
{
    BSPLOG(bsp_log("tcp_listen: s[%p] port[%x]\n", s, port));
    BSPLOG(bsp_log("tcp_listen: s[%p] port[%x]\n", s, port));
 
 
    memset(s, 0, sizeof(tcp_socket_t));
    memset(s, 0, sizeof(tcp_socket_t));
    s->state    = _LISTEN;
    s->state    = _LISTEN;
    s->our_port = port;
    s->our_port = port;
    s->pkt.buf = (word *)s->pktbuf;
    s->pkt.buf = (word *)s->pktbuf;
    s->pkt.bufsize = ETH_MAX_PKTLEN;
    s->pkt.bufsize = ETH_MAX_PKTLEN;
    s->pkt.ip_hdr  = (ip_header_t *)s->pkt.buf;
    s->pkt.ip_hdr  = (ip_header_t *)s->pkt.buf;
    s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
    s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
 
 
    s->next = tcp_list;
    s->next = tcp_list;
 
 
#if 0
#if 0
    /* limit to one open socket at a time */
    /* limit to one open socket at a time */
    if (s->next) {
    if (s->next) {
        BSPLOG(bsp_log("tcp_listen: recursion error\n"));
        BSPLOG(bsp_log("tcp_listen: recursion error\n"));
        BSPLOG(while(1));
        BSPLOG(while(1));
    }
    }
#endif
#endif
 
 
    tcp_list = s;
    tcp_list = s;
 
 
    return 0;
    return 0;
}
}
 
 
/*
/*
 * SO_REUSEADDR, no 2MSL.
 * SO_REUSEADDR, no 2MSL.
 */
 */
void
void
__tcp_so_reuseaddr(tcp_socket_t *s)
__tcp_so_reuseaddr(tcp_socket_t *s)
{
{
//    BSPLOG(bsp_log("__tcp_so_reuseaddr.\n"));
//    BSPLOG(bsp_log("__tcp_so_reuseaddr.\n"));
    s->reuse = 0x01;
    s->reuse = 0x01;
}
}
 
 
/*
/*
 * Block while waiting for all data to be transmitted.
 * Block while waiting for all data to be transmitted.
 */
 */
void
void
__tcp_drain(tcp_socket_t *s)
__tcp_drain(tcp_socket_t *s)
{
{
//    BSPLOG(bsp_log("__tcp_drain.\n"));
//    BSPLOG(bsp_log("__tcp_drain.\n"));
    while (s->state != _CLOSED && s->data_bytes)
    while (s->state != _CLOSED && s->data_bytes)
        __tcp_poll();
        __tcp_poll();
//    BSPLOG(bsp_log("__tcp_drain done.\n"));
//    BSPLOG(bsp_log("__tcp_drain done.\n"));
}
}
 
 
 
 
/*
/*
 * Close the tcp connection.
 * Close the tcp connection.
 */
 */
static void
static void
do_abort(void *s)
do_abort(void *s)
{
{
    BSPLOG(bsp_log("do_abort: send RST\n"));
    BSPLOG(bsp_log("do_abort: send RST\n"));
    tcp_send((tcp_socket_t *)s, TCP_FLAG_ACK | TCP_FLAG_RST, 0);
    tcp_send((tcp_socket_t *)s, TCP_FLAG_ACK | TCP_FLAG_RST, 0);
    __timer_cancel(&abort_timer);
    __timer_cancel(&abort_timer);
    ((tcp_socket_t *)s)->state = _CLOSED;
    ((tcp_socket_t *)s)->state = _CLOSED;
    free_rxlist((tcp_socket_t *)s);
    free_rxlist((tcp_socket_t *)s);
    unlink_socket((tcp_socket_t *)s);
    unlink_socket((tcp_socket_t *)s);
}
}
 
 
void
void
__tcp_abort(tcp_socket_t *s, unsigned long delay)
__tcp_abort(tcp_socket_t *s, unsigned long delay)
{
{
  __timer_set(&abort_timer, delay, do_abort, s);
  __timer_set(&abort_timer, delay, do_abort, s);
}
}
 
 
/*
/*
 * Close the tcp connection.
 * Close the tcp connection.
 */
 */
void
void
__tcp_close(tcp_socket_t *s)
__tcp_close(tcp_socket_t *s)
{
{
    __tcp_drain(s);
    __tcp_drain(s);
    if (s->state == _ESTABLISHED || s->state == _SYN_RCVD) {
    if (s->state == _ESTABLISHED || s->state == _SYN_RCVD) {
        BSPLOG(bsp_log("__tcp_close: going to _FIN_WAIT_1\n"));
        BSPLOG(bsp_log("__tcp_close: going to _FIN_WAIT_1\n"));
        s->state = _FIN_WAIT_1;
        s->state = _FIN_WAIT_1;
        tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
        tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
    } else if (s->state == _CLOSE_WAIT) {
    } else if (s->state == _CLOSE_WAIT) {
 
 
        BSPLOG(bsp_log("__tcp_close: going to _LAST_ACK\n"));
        BSPLOG(bsp_log("__tcp_close: going to _LAST_ACK\n"));
 
 
        s->state = _LAST_ACK;
        s->state = _LAST_ACK;
        tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
        tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
    }
    }
    free_rxlist(s);
    free_rxlist(s);
}
}
 
 
 
 
/*
/*
 * Wait for connection to be fully closed.
 * Wait for connection to be fully closed.
 */
 */
void
void
__tcp_close_wait(tcp_socket_t *s)
__tcp_close_wait(tcp_socket_t *s)
{
{
    BSPLOG(bsp_log("__tcp_close_wait.\n"));
    BSPLOG(bsp_log("__tcp_close_wait.\n"));
    while (s->state != _CLOSED)
    while (s->state != _CLOSED)
        __tcp_poll();
        __tcp_poll();
    BSPLOG(bsp_log("__tcp_close_wait done.\n"));
    BSPLOG(bsp_log("__tcp_close_wait done.\n"));
}
}
 
 
 
 
/*
/*
 * Read up to 'len' bytes without blocking.
 * Read up to 'len' bytes without blocking.
 */
 */
int
int
__tcp_read(tcp_socket_t *s, char *buf, int len)
__tcp_read(tcp_socket_t *s, char *buf, int len)
{
{
    int          nread;
    int          nread;
    pktbuf_t     *pkt;
    pktbuf_t     *pkt;
    tcp_header_t *tcp;
    tcp_header_t *tcp;
 
 
    if (len <= 0 || s->rxcnt == 0)
    if (len <= 0 || s->rxcnt == 0)
        return 0;
        return 0;
 
 
    if (s->state != _ESTABLISHED && s->rxcnt == 0)
    if (s->state != _ESTABLISHED && s->rxcnt == 0)
        return -1;
        return -1;
 
 
    nread = 0;
    nread = 0;
    while (len) {
    while (len) {
        if (len < s->rxcnt) {
        if (len < s->rxcnt) {
            memcpy(buf, s->rxptr, len);
            memcpy(buf, s->rxptr, len);
            BSPLOG(bsp_log("tcp_read: read %d bytes.\n", len));
            BSPLOG(bsp_log("tcp_read: read %d bytes.\n", len));
            s->rxptr += len;
            s->rxptr += len;
            s->rxcnt -= len;
            s->rxcnt -= len;
            nread    += len;
            nread    += len;
 
 
            BSPLOG(bsp_log("tcp_read: %d bytes left in rxlist head.\n",
            BSPLOG(bsp_log("tcp_read: %d bytes left in rxlist head.\n",
                       s->rxcnt));
                       s->rxcnt));
 
 
            break;
            break;
        } else {
        } else {
            memcpy(buf, s->rxptr, s->rxcnt);
            memcpy(buf, s->rxptr, s->rxcnt);
            BSPLOG(bsp_log("tcp_read: read %d bytes. pkt[%x] freed.\n",
            BSPLOG(bsp_log("tcp_read: read %d bytes. pkt[%x] freed.\n",
                           s->rxcnt, s->rxlist));
                           s->rxcnt, s->rxlist));
            nread += s->rxcnt;
            nread += s->rxcnt;
            buf   += s->rxcnt;
            buf   += s->rxcnt;
            len   -= s->rxcnt;
            len   -= s->rxcnt;
 
 
            /* setup for next packet in list */
            /* setup for next packet in list */
            pkt = s->rxlist;
            pkt = s->rxlist;
            s->rxlist = pkt->next;
            s->rxlist = pkt->next;
            __pktbuf_free(pkt);
            __pktbuf_free(pkt);
 
 
            if ((pkt = s->rxlist) != NULL) {
            if ((pkt = s->rxlist) != NULL) {
                tcp = pkt->tcp_hdr;
                tcp = pkt->tcp_hdr;
                s->rxcnt = pkt->pkt_bytes - (tcp->hdr_len << 2);
                s->rxcnt = pkt->pkt_bytes - (tcp->hdr_len << 2);
                s->rxptr = ((char *)tcp)  + (tcp->hdr_len << 2);
                s->rxptr = ((char *)tcp)  + (tcp->hdr_len << 2);
 
 
                BSPLOG(bsp_log("tcp_read: next pkt[%x] has %d bytes.\n",
                BSPLOG(bsp_log("tcp_read: next pkt[%x] has %d bytes.\n",
                           s->rxlist, s->rxcnt));
                           s->rxlist, s->rxcnt));
            } else {
            } else {
 
 
                BSPLOG(bsp_log("tcp_read: no more data.\n"));
                BSPLOG(bsp_log("tcp_read: no more data.\n"));
 
 
                s->rxcnt = 0;
                s->rxcnt = 0;
                break;
                break;
            }
            }
        }
        }
    }
    }
    return nread;
    return nread;
}
}
 
 
 
 
/*
/*
 * Write up to 'len' bytes without blocking
 * Write up to 'len' bytes without blocking
 */
 */
int
int
__tcp_write(tcp_socket_t *s, char *buf, int len)
__tcp_write(tcp_socket_t *s, char *buf, int len)
{
{
    tcp_header_t *tcp = s->pkt.tcp_hdr;
    tcp_header_t *tcp = s->pkt.tcp_hdr;
 
 
    if (len <= 0)
    if (len <= 0)
        return 0;
        return 0;
 
 
    if (s->state != _ESTABLISHED && s->state != _CLOSE_WAIT)
    if (s->state != _ESTABLISHED && s->state != _CLOSE_WAIT)
        return -1;
        return -1;
 
 
    if (s->data_bytes)
    if (s->data_bytes)
        return 0;
        return 0;
 
 
    if (len > MAX_TCP_DATA)
    if (len > MAX_TCP_DATA)
        len = MAX_TCP_DATA;
        len = MAX_TCP_DATA;
 
 
    memcpy(tcp + 1, buf, len);
    memcpy(tcp + 1, buf, len);
    s->data_bytes = len;
    s->data_bytes = len;
 
 
    tcp_send(s, TCP_FLAG_ACK, 0);
    tcp_send(s, TCP_FLAG_ACK, 0);
 
 
    return len;
    return len;
}
}
 
 
/*
/*
 * Write 'len' bytes from 'buf', blocking until sent.
 * Write 'len' bytes from 'buf', blocking until sent.
 * If connection collapses, return -1
 * If connection collapses, return -1
 */
 */
int
int
__tcp_write_block(tcp_socket_t *s, char *buf, int len)
__tcp_write_block(tcp_socket_t *s, char *buf, int len)
{
{
    int total = 0;
    int total = 0;
    int n;
    int n;
 
 
    while (len) {
    while (len) {
        if (s->state == _CLOSE_WAIT) {
        if (s->state == _CLOSE_WAIT) {
            // This connection is tring to close
            // This connection is tring to close
            // This connection is breaking
            // This connection is breaking
            if (s->data_bytes == 0 && s->rxcnt == 0)
            if (s->data_bytes == 0 && s->rxcnt == 0)
                __tcp_close(s);
                __tcp_close(s);
        }
        }
        if (s->state == _CLOSED) {
        if (s->state == _CLOSED) {
            // The connection is gone!
            // The connection is gone!
            return -1;
            return -1;
        }
        }
        n = __tcp_write(s, buf, len);
        n = __tcp_write(s, buf, len);
        if (n > 0) {
        if (n > 0) {
            len -= n;
            len -= n;
            buf += n;
            buf += n;
        }
        }
        __tcp_poll();
        __tcp_poll();
    }
    }
    __tcp_drain(s);
    __tcp_drain(s);
    return total;
    return total;
}
}
 
 
/*
/*
 * Establish a new [outgoing] connection, with a timeout.
 * Establish a new [outgoing] connection, with a timeout.
 */
 */
int
int
__tcp_open(tcp_socket_t *s, struct sockaddr_in *host,
__tcp_open(tcp_socket_t *s, struct sockaddr_in *host,
           word port, int timeout, int *err)
           word port, int timeout, int *err)
{
{
    // Fill in socket details
    // Fill in socket details
    memset(s, 0, sizeof(tcp_socket_t));
    memset(s, 0, sizeof(tcp_socket_t));
    s->state = _SYN_SENT;
    s->state = _SYN_SENT;
    s->our_port = port;
    s->our_port = port;
    s->his_port = host->sin_port;
    s->his_port = host->sin_port;
    s->pkt.buf = (word *)s->pktbuf;
    s->pkt.buf = (word *)s->pktbuf;
    s->pkt.bufsize = ETH_MAX_PKTLEN;
    s->pkt.bufsize = ETH_MAX_PKTLEN;
    s->pkt.ip_hdr  = (ip_header_t *)s->pkt.buf;
    s->pkt.ip_hdr  = (ip_header_t *)s->pkt.buf;
    s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
    s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
    s->seq = (port << 16) | 0xDE77;
    s->seq = (port << 16) | 0xDE77;
    s->ack = 0;
    s->ack = 0;
    if (__arp_lookup((ip_addr_t *)&host->sin_addr, &s->his_addr) < 0) {
    if (__arp_lookup((ip_addr_t *)&host->sin_addr, &s->his_addr) < 0) {
        diag_printf("%s: Can't find address of server\n", __FUNCTION__);
        diag_printf("%s: Can't find address of server\n", __FUNCTION__);
        return -1;
        return -1;
    }
    }
    s->next = tcp_list;
    s->next = tcp_list;
    tcp_list = s;
    tcp_list = s;
 
 
    // Send off the SYN packet to open the connection
    // Send off the SYN packet to open the connection
    tcp_send(s, TCP_FLAG_SYN, 0);
    tcp_send(s, TCP_FLAG_SYN, 0);
    // Wait for connection to establish
    // Wait for connection to establish
    while (s->state != _ESTABLISHED) {
    while (s->state != _ESTABLISHED) {
        if (s->state == _CLOSED) {
        if (s->state == _CLOSED) {
            diag_printf("TCP open - host closed connection\n");
            diag_printf("TCP open - host closed connection\n");
            return -1;
            return -1;
        }
        }
        if (--timeout <= 0) {
        if (--timeout <= 0) {
            diag_printf("TCP open - connection timed out\n");
            diag_printf("TCP open - connection timed out\n");
            return -1;
            return -1;
        }
        }
        MS_TICKS_DELAY();
        MS_TICKS_DELAY();
        __tcp_poll();
        __tcp_poll();
    }
    }
    return 0;
    return 0;
}
}
 
 
 
 
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.