www.pudn.com > TCPmodbushy.rar > tcp.c


/** 
 * @file 
 * 
 * Transmission Control Protocol for IP 
 * 
 * This file contains common functions for the TCP implementation, such as functinos 
 * for manipulating the data structures and the TCP timer functions. TCP functions 
 * related to input and output is found in tcp_in.c and tcp_out.c respectively. 
 * 
 */ 
 
/* 
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 
 * 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. The name of the author may not be used to endorse or promote products 
 *    derived from this software without specific prior written permission.  
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. 
 * 
 * This file is part of the lwIP TCP/IP stack. 
 *  
 * Author: Adam Dunkels  
 * 
 */ 
 
#include  
 
#include "o:/lwip/opt.h" 
#include "o:/lwip/def.h" 
#include "o:/lwip/mem.h" 
#include "o:/lwip/memp.h" 
 
#include "o:/lwip/tcp.h" 
#if LWIP_TCP 
 
/* Incremented every coarse grained timer shot 
   (typically every 500 ms, determined by TCP_COARSE_TIMEOUT). */ 
u32_t data tcp_ticks; 
/*code u8_t tcp_backoff[13] = 
    { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};*/ 
 
/* The TCP PCB lists. */ 
 
/** List of all TCP PCBs in LISTEN state */ 
union tcp_listen_pcbs_t xdata tcp_listen_pcbs; 
/** List of all TCP PCBs that are in a state in which 
 * they accept or send data. */ 
struct tcp_pcb xdata * xdata tcp_active_pcbs;   
/** List of all TCP PCBs in TIME-WAIT state */ 
struct tcp_pcb xdata * xdata tcp_tw_pcbs; 
 
struct tcp_pcb xdata * xdata tcp_tmp_pcb; 
 
static u8_t data tcp_timer; 
static u16_t tcp_new_port(void); 
 
struct tcp_manager_block xdata Manager_Tcp_Table[MEMP_NUM_TCP_PCB]; 
 
/** 
 * Initializes the TCP layer. 
 */ 
void 
tcp_init(void) 
{ 
  /* Clear globals. */ 
  tcp_listen_pcbs.listen_pcbs = NULL; 
  tcp_active_pcbs = NULL; 
  tcp_tw_pcbs = NULL; 
  tcp_tmp_pcb = NULL; 
   
  /* initialize timer */ 
  tcp_ticks = 0; 
  tcp_timer = 0; 
  Init_Manager_Tcp_Table(); 
} 
 
/****************************************************************************** 
* TCP open a port for listen 
******************************************************************************/ 
void StartListen( void ) 
{ 
  struct tcp_pcb_listen xdata *lpcb; 
 
  lpcb = memp_malloc(MEMP_TCP_PCB_LISTEN); 
  if (lpcb == NULL) { 
    return; 
  } 
  lpcb->local_port = TCP_LOCAL_PORT; 
  lpcb->state = LISTEN; 
  TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb); 
  return; 
} 
 
/****************************************************************************** 
* Close a port for listen 
******************************************************************************/ 
void StopListen( void ) 
{ 
  if( tcp_listen_pcbs.listen_pcbs != NULL ) 
  { 
    struct tcp_pcb xdata *tmp; 
	tmp = tcp_listen_pcbs.pcbs; 
    tcp_pcb_remove((struct tcp_pcb xdata * xdata *)&tcp_listen_pcbs.pcbs, tcp_listen_pcbs.pcbs); 
    memp_free(MEMP_TCP_PCB_LISTEN, tmp); 
  }  
} 
 
/****************************************************************************** 
* TBOX as a Master send data should create LINK if no have Links 
* NULL: create failed 
******************************************************************************/ 
struct tcp_pcb xdata* ConnectedDest( u16_t SelfPort, u32_t DestIP, u16_t DestPor ) 
{ 
  struct tcp_pcb xdata* Tmp; 
 
#ifdef TESTDEBUG 
test[9]++; 
#endif 
 
  Tmp = tcp_new(); 
  if( Tmp != NULL) 
  { 
    if( tcp_bind( Tmp, SelfPort ) == ERR_OK ) 
	{ 
      struct ip_addr xdata ipDestIP; 
	  ipDestIP.addr = DestIP; 
	  if( tcp_connect( Tmp, (struct ip_addr xdata *)&ipDestIP, DestPor) == ERR_OK ) 
	    return Tmp; 
	} 
    tcp_close(Tmp); 
  } 
} 
 
/****************************************************************************** 
* TBOX as a Master send data should create LINK if no have Links 
* NULL: create failed 
******************************************************************************/ 
err_t SendTcpDATA( struct tcp_pcb xdata* pTcpLINK, struct MEMB xdata *pMemb ) 
{ 
//struct pbuf xdata *p 
  u16_t len; 
  struct MEMB xdata *Tmp; 
  Tmp = pMemb; 
  len = 0; 
 
  while( Tmp != NULL ) 
  { 
    len += Tmp->size; 
    Tmp = Tmp->link; 
  } 
 
  if(pTcpLINK->state == ESTABLISHED || 
     pTcpLINK->state == CLOSE_WAIT || 
     pTcpLINK->state == SYN_SENT || 
     pTcpLINK->state == SYN_RCVD) { 
    if ( len > 0) { 
      if( tcp_enqueue(pTcpLINK, (void xdata *)pMemb, len, 0, 1, NULL, 0) == ERR_OK ) { 
        tcp_output(pTcpLINK); 
        return ERR_OK; 
      } 
	  else 
	  { 
#ifdef TESTDEBUG 
test[6]++; 
#endif 
	  } 
    } 
  } else { 
#ifdef TESTDEBUG 
test[4]++; 
#endif 
    return ERR_CONN; 
  } 
} 
 
/****************************************************************************** 
* Init TCP LINK TABLE 
******************************************************************************/ 
void Init_Manager_Tcp_Table( void ) 
{ 
  u8_t i; 
  for( i=0; iunsent != NULL ) 
	  	||( Manager_Tcp_Table[i].pPointTcp_Pcb->unacked != NULL ) ) 
	  return 1; 
	} 
  } 
  return 0; 
} 
 
/** 
 * Called periodically to dispatch TCP timers. 
 * 
 */ 
void tcp_tmr(void) 
{ 
  /* Call tcp_fasttmr() every 250 ms */ 
  tcp_fasttmr(); 
 
  if (++tcp_timer & 1) { 
    /* Call tcp_tmr() every 500 ms, i.e., every other timer 
       tcp_tmr() is called. */ 
    tcp_slowtmr(); 
  } 
} 
 
void tcp_close_AllLink( void ) 
{ 
  struct tcp_pcb xdata *pcb; 
 
  pcb = tcp_active_pcbs; 
  while (pcb != NULL) { 
    /* this connection must be aborted */ 
    struct tcp_pcb xdata *next = pcb->next; 
    tcp_abort(pcb); 
    pcb = next; 
  } 
} 
 
/** 
 * Closes the connection held by the PCB. 
 * 
 */ 
err_t tcp_close(struct tcp_pcb xdata *pcb) 
{ 
  err_t err; 
 
  RemoveAItemFromTable( pcb ); 
 
  switch (pcb->state) { 
  case CLOSED: 
    /* Closing a pcb in the CLOSED state might seem erroneous, 
     * however, it is in this state once allocated and as yet unused 
     * and the user needs some way to free it should the need arise. 
     * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) 
     * or for a pcb that has been used and then entered the CLOSED state  
     * is erroneous, but this should never happen as the pcb has in those cases 
     * been freed, and so any remaining handles are bogus. */ 
    err = ERR_OK; 
    memp_free(MEMP_TCP_PCB, pcb); 
    pcb = NULL; 
    break; 
  case LISTEN: 
    err = ERR_OK; 
    tcp_pcb_remove((struct tcp_pcb xdata * xdata *)&tcp_listen_pcbs.pcbs, pcb); 
    memp_free(MEMP_TCP_PCB_LISTEN, pcb); 
    pcb = NULL; 
    break; 
  case SYN_SENT: 
    err = ERR_OK; 
    tcp_pcb_remove(&tcp_active_pcbs, pcb); 
    memp_free(MEMP_TCP_PCB, pcb); 
    pcb = NULL; 
    break; 
  case SYN_RCVD: 
  case ESTABLISHED: 
    err = tcp_send_ctrl(pcb, TCP_FIN); 
    if (err == ERR_OK) { 
      pcb->state = FIN_WAIT_1; 
    } 
    break; 
  case CLOSE_WAIT: 
    err = tcp_send_ctrl(pcb, TCP_FIN); 
    if (err == ERR_OK) { 
      pcb->state = LAST_ACK; 
    } 
    break; 
  default: 
    /* Has already been closed, do nothing. */ 
    err = ERR_OK; 
    pcb = NULL; 
    break; 
  } 
 
  if (pcb != NULL && err == ERR_OK) { 
    err = tcp_output(pcb); 
  } 
  return err; 
} 
 
/** 
 * Aborts a connection by sending a RST to the remote host and deletes 
 * the local protocol control block. This is done when a connection is 
 * killed because of shortage of memory. 
 * 
 */ 
void 
tcp_abort(struct tcp_pcb xdata *pcb) 
{ 
  u32_t seqno, ackno; 
  u16_t remote_port, local_port; 
  struct ip_addr xdata remote_ip, local_ip; 
 
  RemoveAItemFromTable( pcb ); 
 
  /* Figure out on which TCP PCB list we are, and remove us. If we 
     are in an active state, call the receive function associated with 
     the PCB with a NULL argument, and send an RST to the remote end. */ 
  if (pcb->state == TIME_WAIT) { 
    tcp_pcb_remove(&tcp_tw_pcbs, pcb); 
    memp_free(MEMP_TCP_PCB, pcb); 
  } else { 
    seqno = pcb->snd_nxt; 
    ackno = pcb->rcv_nxt; 
    ip_addr_set(&local_ip, &(netif->ip_addr)); 
    ip_addr_set(&remote_ip, &(pcb->remote_ip)); 
    local_port = pcb->local_port; 
    remote_port = pcb->remote_port; 
    tcp_pcb_remove(&tcp_active_pcbs, pcb); 
    if (pcb->unacked != NULL) { 
      tcp_segs_free(pcb->unacked); 
    } 
    if (pcb->unsent != NULL) { 
      tcp_segs_free(pcb->unsent); 
    } 
#if TCP_QUEUE_OOSEQ     
    if (pcb->ooseq != NULL) { 
      tcp_segs_free(pcb->ooseq); 
    } 
#endif /* TCP_QUEUE_OOSEQ */ 
    memp_free(MEMP_TCP_PCB, pcb); 
 
/****************************************************************************** 
add err( TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT) ) 
******************************************************************************/ 
    // sending RST 
    tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); 
  } 
} 
 
/** 
 * Binds the connection to a local portnumber and IP address. If the 
 * IP address is not given (i.e., ipaddr == NULL), the IP address of 
 * the outgoing network interface is used instead. 
 * 
 */ 
err_t tcp_bind(struct tcp_pcb xdata *pcb, u16_t port) 
{ 
  struct tcp_pcb xdata *cpcb; 
 
  if (port == 0) { 
    port = tcp_new_port(); 
  } 
 
  /* Check if the address already is in use. */ 
  for(cpcb = (struct tcp_pcb xdata *)tcp_listen_pcbs.pcbs; cpcb != NULL; cpcb = cpcb->next) { 
    if (cpcb->local_port == port) { 
          return ERR_USE; 
    } 
  } 
  for(cpcb = tcp_active_pcbs; cpcb != NULL; cpcb = cpcb->next) { 
    if (cpcb->local_port == port) { 
         return ERR_USE; 
    } 
  } 
  pcb->local_port = port; 
  return ERR_OK; 
} 
 
/** 
 * A nastly hack featuring 'goto' statements that allocates a 
 * new TCP local port. 
 */ 
static u16_t 
tcp_new_port(void) 
{ 
  struct tcp_pcb xdata *pcb; 
#ifndef TCP_LOCAL_PORT_RANGE_START 
#define TCP_LOCAL_PORT_RANGE_START 4096 
#define TCP_LOCAL_PORT_RANGE_END   0x7fff 
#endif 
  static u16_t data port = TCP_LOCAL_PORT_RANGE_START; 
   
 again: 
  if (++port > TCP_LOCAL_PORT_RANGE_END) { 
    port = TCP_LOCAL_PORT_RANGE_START; 
  } 
   
  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { 
    if (pcb->local_port == port) { 
      goto again; 
    } 
  } 
  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { 
    if (pcb->local_port == port) { 
      goto again; 
    } 
  } 
  for(pcb = (struct tcp_pcb xdata *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { 
    if (pcb->local_port == port) { 
      goto again; 
    } 
  } 
  return port; 
} 
 
/** 
 * Connects to another host. The function given as the "connected" 
 * argument will be called when the connection has been established. 
 * 
 */ 
err_t 
tcp_connect(struct tcp_pcb xdata *pcb, struct ip_addr xdata *ipaddr, u16_t port) 
{ 
  u32_t optdata; 
  err_t ret; 
  u32_t iss; 
 
  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); 
  if (ipaddr != NULL) { 
    pcb->remote_ip = *ipaddr; 
  } else { 
    return ERR_VAL; 
  } 
  pcb->remote_port = port; 
  if (pcb->local_port == 0) { 
    pcb->local_port = tcp_new_port(); 
  } 
  iss = tcp_next_iss(); 
  pcb->rcv_nxt = 0; 
  pcb->snd_nxt = iss; 
  pcb->lastack = iss - 1; 
  pcb->snd_lbb = iss - 1; 
//  pcb->rcv_wnd = TCP_WND; 
  pcb->snd_wnd = TCP_WND; 
  pcb->mss = TCP_MSS; 
  pcb->cwnd = 1; 
  pcb->ssthresh = TCP_MSS * 10;//pcb->mss*10 
  pcb->state = SYN_SENT; 
 
  TCP_REG(&tcp_active_pcbs, pcb); 
   
  /* Build an MSS option */ 
  optdata = htonl(((u32_t)2 << 24) |  
      ((u32_t)4 << 16) |  
      (((u32_t)pcb->mss / 256) << 8) | 
      (pcb->mss & 255)); 
 
  ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (u8_t xdata *)&optdata, 4); 
  if (ret == ERR_OK) { 
    AddAItemToTable(pcb); 
    tcp_output(pcb); 
  } 
  return ret; 
} 
 
/** 
 * Called every 500 ms and implements the retransmission timer and the timer that 
 * removes PCBs that have been in TIME-WAIT for enough time. It also increments 
 * various timers such as the inactivity timer in each PCB. 
 */ 
void 
tcp_slowtmr(void) 
{ 
  struct tcp_pcb xdata *pcb; 
  struct tcp_pcb xdata *pcb2; 
  struct tcp_pcb xdata *prev; 
  u32_t eff_wnd; 
  u8_t pcb_remove;      /* flag if a PCB should be removed */ 
  err_t err; 
 
  err = ERR_OK; 
 
  ++tcp_ticks; 
 
/* add account TIME */ 
  pcb = tcp_active_pcbs; 
  while( pcb != NULL ) 
  { 
    if( ( pcb->rttest != 0)&&( pcb->rttest != 0xff ) ) 
  	  pcb->rttest++; 
    if( pcb->tmr != 0xff ) 
  	  pcb->tmr++; 
	pcb = pcb->next; 
  } 
  pcb = tcp_tw_pcbs; 
  while( pcb != NULL ) 
  { 
    if( ( pcb->rttest != 0)&&( pcb->rttest != 0xff ) ) 
  	  pcb->rttest++; 
    if( pcb->tmr != 0xff ) 
  	  pcb->tmr++; 
	pcb = pcb->next;	 
  } 
 
  /* Steps through all of the active PCBs. */ 
  prev = NULL; 
  pcb = tcp_active_pcbs; 
 
  while (pcb != NULL) { 
    pcb_remove = 0; 
 
    if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { 
      ++pcb_remove; 
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); 
    } 
    else if (pcb->nrtx == TCP_MAXRTX) { 
      ++pcb_remove; 
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); 
    } else { 
      ++pcb->rtime; 
      if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { 
 
        /* Time for a retransmission. */ 
        /* Double retransmission time-out unless we are trying to 
         * connect to somebody (i.e., we are in SYN_SENT). */ 
        if (pcb->state != SYN_SENT) { 
          if( pcb->rto <= (3000/TCP_SLOW_INTERVAL) ) 
            pcb->rto = pcb->rto + 1; 
        } 
        /* Reduce congestion window and ssthresh. */ 
        eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); 
        pcb->ssthresh = eff_wnd >> 1; 
        if (pcb->ssthresh < pcb->mss) { 
          pcb->ssthresh = pcb->mss * 2; 
        } 
        pcb->cwnd = pcb->mss; 
        LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F" ssthresh %"U16_F"\n", 
                                pcb->cwnd, pcb->ssthresh)); 
  
  
#ifdef TESTDEBUG 
test[7]++; 
#endif 
						         
		/* The following needs to be called AFTER cwnd is set to one mss - STJ */ 
        tcp_rexmit_rto(pcb); 
     } 
    } 
    /* Check if this PCB has stayed too long in FIN-WAIT-2 */ 
    if (pcb->state == FIN_WAIT_2) { 
      if ( pcb->tmr > TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL ) { 
        ++pcb_remove; 
        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); 
      } 
    } 
 
    /* If this PCB has queued out of sequence data, but has been 
       inactive for too long, will drop the data (it will eventually 
       be retransmitted). */ 
#if TCP_QUEUE_OOSEQ     
    if ( (pcb->ooseq != NULL) && 
         (pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) ) { 
      tcp_segs_free(pcb->ooseq); 
      pcb->ooseq = NULL; 
      LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); 
    } 
#endif /* TCP_QUEUE_OOSEQ */ 
 
    /* Check if this PCB has stayed too long in SYN-RCVD */ 
    if (pcb->state == SYN_RCVD) { 
      if ( pcb->tmr > TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL ) { 
        ++pcb_remove; 
        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); 
      } 
    } 
 
    /* Check if this PCB has stayed too long in LAST-ACK */ 
    if (pcb->state == LAST_ACK) { 
      if ( pcb->tmr > 2 * TCP_MSL / TCP_SLOW_INTERVAL ) { 
        ++pcb_remove; 
        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); 
      } 
    } 
 
    /* If the PCB should be removed, do it. */ 
    if (pcb_remove) { 
RemoveAItemFromTable( pcb ); 
      tcp_pcb_purge(pcb);       
      /* Remove PCB from tcp_active_pcbs list. */ 
      if (prev != NULL) { 
  LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); 
        prev->next = pcb->next; 
      } else { 
        /* This PCB was the first. */ 
        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); 
        tcp_active_pcbs = pcb->next; 
      } 
 
/****************************************************************************** 
add err infor( TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT), you socket bei yizhou ) 
******************************************************************************/ 
      pcb2 = pcb->next; 
      memp_free(MEMP_TCP_PCB, pcb); 
      pcb = pcb2; 
    } else {      
      prev = pcb; 
      pcb = pcb->next; 
    } 
  } 
 
  /* Steps through all of the TIME-WAIT PCBs. */ 
  prev = NULL;     
  pcb = tcp_tw_pcbs; 
  while (pcb != NULL) { 
    LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); 
    pcb_remove = 0; 
 
    /* Check if this PCB has stayed long enough in TIME-WAIT */ 
    if ( pcb->tmr > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { 
      ++pcb_remove; 
    } 
 
    /* If the PCB should be removed, do it. */ 
    if (pcb_remove) { 
      tcp_pcb_purge(pcb);       
      /* Remove PCB from tcp_tw_pcbs list. */ 
      if (prev != NULL) { 
  LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); 
        prev->next = pcb->next; 
      } else { 
        /* This PCB was the first. */ 
        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); 
        tcp_tw_pcbs = pcb->next; 
      } 
      pcb2 = pcb->next; 
      memp_free(MEMP_TCP_PCB, pcb); 
      pcb = pcb2; 
    } else { 
      prev = pcb; 
      pcb = pcb->next; 
    } 
  } 
} 
 
/** 
 * Is called every TCP_FAST_INTERVAL (250 ms) and sends delayed ACKs. 
 */ 
void 
tcp_fasttmr(void) 
{ 
  struct tcp_pcb xdata *pcb; 
 
  /* send delayed ACKs */   
  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { 
    if (pcb->flags & TF_ACK_DELAY) { 
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); 
      tcp_ack_now(pcb); 
      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); 
    } 
  } 
} 
 
/** 
 * Deallocates a list of TCP segments (tcp_seg structures). 
 * 
 */ 
u8_t 
tcp_segs_free(struct tcp_seg xdata *seg) 
{ 
  u8_t count = 0; 
  struct tcp_seg xdata *next; 
  while (seg != NULL) { 
    next = seg->next; 
    count += tcp_seg_free(seg); 
    seg = next; 
  } 
  return count; 
} 
 
/** 
 * Frees a TCP segment. 
 * 
 */ 
u8_t 
tcp_seg_free(struct tcp_seg xdata *seg) 
{ 
  u8_t count = 0; 
   
  if (seg != NULL) { 
    if (seg->p != NULL) { 
      count = pbuf_free(seg->p); 
#if TCP_DEBUG 
      seg->p = NULL; 
#endif /* TCP_DEBUG */ 
    } 
    memp_free(MEMP_TCP_SEG, seg); 
  } 
  return count; 
} 
 
#if TCP_QUEUE_OOSEQ 
 
/** 
 * Returns a copy of the given TCP segment. 
 * 
 */  
struct tcp_seg xdata * 
tcp_seg_copy(struct tcp_seg xdata *seg) 
{ 
  struct tcp_seg xdata *cseg; 
 
  cseg = memp_malloc(MEMP_TCP_SEG); 
  if (cseg == NULL) { 
    return NULL; 
  } 
  memcpy((u8_t xdata *)cseg, (const u8_t xdata *)seg, sizeof(struct tcp_seg));  
  pbuf_ref(cseg->p); 
  return cseg; 
} 
#endif 
 
 
/****************************************************************************** 
* kill activePCB must be a oldest and unactive time highe 12S 
******************************************************************************/ 
#define KILL_UNACTIVE_PCB_TIME_LEVEL (15000/TCP_SLOW_INTERVAL)  /*12S*/ 
static void 
tcp_kill_oldest_active(void) 
{ 
  struct tcp_pcb xdata *pcb;  
  struct tcp_pcb xdata *inactive; 
  u8_t inactivity; 
   
  /* We kill the oldest active connection that has lower priority than 
     prio. */ 
  inactivity = KILL_UNACTIVE_PCB_TIME_LEVEL; 
  inactive = NULL; 
  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { 
    if ( pcb->tmr >= inactivity ) { 
      inactivity = pcb->tmr; 
      inactive = pcb; 
    } 
  } 
  if (inactive != NULL) { 
    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_oldest_active: killing oldest PCB %p (%"S32_F")\n", 
           (void *)inactive, inactivity)); 
    tcp_abort(inactive); 
  }       
} 
 
static void 
tcp_kill_timewait(void) 
{ 
  struct tcp_pcb xdata *pcb; 
  struct tcp_pcb xdata *inactive; 
  u8_t inactivity; 
 
  inactivity = 0; 
  inactive = NULL; 
  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { 
    if ( pcb->tmr >= inactivity) { 
      inactivity = pcb->tmr; 
      inactive = pcb; 
    } 
  } 
  if (inactive != NULL) { 
    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", 
           (void *)inactive, inactivity)); 
    tcp_abort(inactive); 
  }       
} 
 
 
struct tcp_pcb xdata* 
tcp_alloc(void) 
{ 
  struct tcp_pcb xdata *pcb; 
  u32_t iss; 
   
  pcb = memp_malloc(MEMP_TCP_PCB); 
  if (pcb == NULL) { 
    /* Try killing oldest connection in TIME-WAIT. */ 
    LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); 
    tcp_kill_timewait(); 
    pcb = memp_malloc(MEMP_TCP_PCB); 
    if (pcb == NULL) { 
      tcp_kill_oldest_active();     
      pcb = memp_malloc(MEMP_TCP_PCB); 
    } 
  } 
  if (pcb != NULL) { 
    memset(pcb, 0, sizeof(struct tcp_pcb)); 
//    pcb->snd_buf = TCP_SND_BUF; 
    pcb->snd_queuelen = 0; 
//    pcb->rcv_wnd = TCP_WND; 
    pcb->mss = TCP_MSS; 
    pcb->rto = 1500 / TCP_SLOW_INTERVAL; 
    pcb->rtime = 0; 
    pcb->cwnd = 1; 
    iss = tcp_next_iss(); 
    pcb->snd_wl2 = iss; 
    pcb->snd_nxt = iss; 
    pcb->snd_max = iss; 
    pcb->lastack = iss; 
    pcb->snd_lbb = iss;    
    pcb->tmr = 0; 
  } 
  return pcb; 
} 
 
/** 
 * Creates a new TCP protocol control block but doesn't place it on 
 * any of the TCP PCB lists. 
 * 
 * @internal: Maybe there should be a idle TCP PCB list where these 
 * PCBs are put on. We can then implement port reservation using 
 * tcp_bind(). Currently, we lack this (BSD socket type of) feature. 
 */ 
 
struct tcp_pcb xdata* 
tcp_new(void) 
{ 
  return tcp_alloc(); 
} 
 
/** 
 * Purges a TCP PCB. Removes any buffered data and frees the buffer memory. 
 * 
 */ 
void 
tcp_pcb_purge(struct tcp_pcb xdata *pcb) 
{ 
  if (pcb->state != CLOSED && 
     pcb->state != TIME_WAIT && 
     pcb->state != LISTEN) { 
 
    LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); 
     
    if (pcb->unsent != NULL) {     
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); 
    } 
    if (pcb->unacked != NULL) {     
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); 
    } 
#if TCP_QUEUE_OOSEQ /* LW */ 
    if (pcb->ooseq != NULL) {     
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); 
    } 
     
    tcp_segs_free(pcb->ooseq); 
    pcb->ooseq = NULL; 
#endif /* TCP_QUEUE_OOSEQ */ 
    tcp_segs_free(pcb->unsent); 
    tcp_segs_free(pcb->unacked); 
    pcb->unacked = pcb->unsent = NULL; 
  } 
} 
 
/** 
 * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. 
 * 
 */ 
void 
tcp_pcb_remove(struct tcp_pcb xdata * xdata *pcblist, struct tcp_pcb xdata *pcb) 
{ 
  TCP_RMV(pcblist, pcb); 
 
  tcp_pcb_purge(pcb); 
   
  /* if there is an outstanding delayed ACKs, send it */ 
  if (pcb->state != TIME_WAIT && 
     pcb->state != LISTEN && 
     pcb->flags & TF_ACK_DELAY) { 
    pcb->flags |= TF_ACK_NOW; 
    tcp_output(pcb); 
  }   
  pcb->state = CLOSED; 
 
  LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); 
} 
 
/** 
 * Calculates a new initial sequence number for new connections. 
 * 
 */ 
u32_t 
tcp_next_iss(void) 
{ 
  static u32_t data iss = 6510; 
   
  iss += tcp_ticks;       /* XXX */ 
  return iss; 
} 
 
#endif /* LWIP_TCP */