blob: 4aef8c9fbce6e9e6c3540f0477bfaef19c16e51c [file] [log] [blame]
/*
* Copyright (c) 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. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: tcpip.c,v 1.6 2005/02/27 09:44:33 adamdunkels Exp $
*/
#include "tcpip.h"
#include "ek-service.h"
#include "tcpip.h"
#include "uip.h"
#include "uip-fw.h"
#include "timer.h"
#include "packet-service.h"
#include "uip-split.h"
#include <string.h>
ek_event_t tcpip_event;
EK_SERVICE(packetservice, PACKET_SERVICE_NAME);
/**
* \internal Structure for holding a TCP port and a process ID.
*/
struct listenport {
u16_t port;
ek_id_t id;
};
/*static struct tcpip_event_args ev_args;*/
static struct timer periodic;
static struct internal_state {
struct listenport listenports[UIP_LISTENPORTS];
ek_event_t event;
ek_id_t id;
} s;
enum {
TCP_POLL,
UDP_POLL
};
static unsigned char forwarding = 0;
EK_EVENTHANDLER(eventhandler, ev, data);
EK_POLLHANDLER(pollhandler);
EK_PROCESS(proc, "TCP/IP stack", EK_PRIO_NORMAL,
eventhandler, pollhandler, NULL);
/*---------------------------------------------------------------------------*/
EK_PROCESS_INIT(tcpip_init, arg)
{
uip_init();
ek_start(&proc);
forwarding = 0;
}
/*---------------------------------------------------------------------------*/
void
tcpip_set_forwarding(unsigned char f)
{
forwarding = f;
}
/*---------------------------------------------------------------------------*/
void
tcpip_input(void)
{
if(uip_len > 0) {
if(forwarding) {
if(uip_fw_forward() == 0) {
uip_input();
if(uip_len > 0) {
#if UIP_CONF_TCP_SPLIT
uip_split_output();
#else
tcpip_output();
#endif
}
}
} else {
uip_input();
if(uip_len > 0) {
#if UIP_CONF_TCP_SPLIT
uip_split_output();
#else
tcpip_output();
#endif
}
}
}
}
/*---------------------------------------------------------------------------*/
void
tcpip_output(void)
{
struct packet_service_state *state;
u16_t hdrlen, datalen;
state = (struct packet_service_state *)ek_service_state(&packetservice);
if(state != NULL &&
state->version == PACKET_SERVICE_VERSION) {
hdrlen = UIP_TCPIP_HLEN;
if(uip_len < UIP_TCPIP_HLEN) {
hdrlen = uip_len;
datalen = 0;
} else {
datalen = uip_len - UIP_TCPIP_HLEN;
}
state->output(&uip_buf[UIP_LLH_LEN], hdrlen, uip_appdata, datalen);
}
}
/*---------------------------------------------------------------------------*/
struct uip_conn *
tcp_connect(u16_t *ripaddr, u16_t port, void *appstate)
{
struct uip_conn *c;
c = uip_connect(ripaddr, port);
if(c == NULL) {
return NULL;
}
((struct tcpip_uipstate *)(c->appstate))->id = EK_PROC_ID(EK_CURRENT());
((struct tcpip_uipstate *)(c->appstate))->state = appstate;
ek_post(s.id, TCP_POLL, c);
return c;
}
/*---------------------------------------------------------------------------*/
void
tcp_unlisten(u16_t port)
{
static unsigned char i;
struct listenport *l;
l = s.listenports;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == port &&
l->id == EK_PROC_ID(EK_CURRENT())) {
l->port = 0;
uip_unlisten(port);
break;
}
++l;
}
}
/*---------------------------------------------------------------------------*/
void
tcp_listen(u16_t port)
{
static unsigned char i;
struct listenport *l;
l = s.listenports;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == 0) {
l->port = port;
l->id = EK_PROC_ID(EK_CURRENT());
uip_listen(port);
break;
}
++l;
}
}
/*---------------------------------------------------------------------------*/
void
tcp_markconn(struct uip_conn *conn,
void *appstate)
{
register struct tcpip_uipstate *s;
s = (struct tcpip_uipstate *)conn->appstate;
s->id = ek_current->id;
s->state = appstate;
}
/*---------------------------------------------------------------------------*/
struct uip_udp_conn *
udp_new(u16_t *ripaddr, u16_t port, void *appstate)
{
struct uip_udp_conn *c;
struct tcpip_uipstate *s;
c = uip_udp_new(ripaddr, port);
if(c == NULL) {
return NULL;
}
s = (struct tcpip_uipstate *)c->appstate;
s->id = EK_PROC_ID(EK_CURRENT());
s->state = appstate;
return c;
}
/*---------------------------------------------------------------------------*/
EK_EVENTHANDLER(eventhandler, ev, data)
{
static unsigned char i;
register struct listenport *l;
ek_id_t id;
struct internal_state *state;
switch(ev) {
case EK_EVENT_INIT:
for(i = 0; i < UIP_LISTENPORTS; ++i) {
s.listenports[i].port = 0;
}
s.id = EK_PROC_ID(EK_CURRENT());
tcpip_event = s.event = ek_alloc_event();
timer_set(&periodic, CLOCK_SECOND/2);
break;
case EK_EVENT_REPLACE:
memcpy(&s, data, sizeof(s));
arg_free(data);
break;
case EK_EVENT_REQUEST_REPLACE:
state = (struct internal_state *)arg_alloc(sizeof(s));
/* Copy state */
memcpy(state, &s, sizeof(s));
ek_replace((struct ek_proc *)data, state);
break;
case EK_EVENT_EXITED:
id = (ek_id_t)data;
l = s.listenports;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->id == id) {
uip_unlisten(l->port);
l->port = 0;
l->id = EK_ID_NONE;
}
++l;
}
/* for(i = 0; i < UIP_CONNS; ++i) {
if(((struct tcpip_uipstate *)uip_conns[i].appstate)->id == id) {
((struct tcpip_uipstate *)uip_conns[i].appstate)->id = EK_ID_NONE;
uip_conns[i].tcpstateflags = CLOSED;
}
}*/
{
register struct uip_conn *cptr;
for(cptr = &uip_conns[0]; cptr < &uip_conns[UIP_CONNS]; ++cptr) {
if(((struct tcpip_uipstate *)cptr->appstate)->id == id) {
((struct tcpip_uipstate *)cptr->appstate)->id = EK_ID_NONE;
cptr->tcpstateflags = CLOSED;
}
}
}
#if UIP_UDP
{
register struct uip_udp_conn *cptr;
for(cptr = &uip_udp_conns[0];
cptr < &uip_udp_conns[UIP_UDP_CONNS]; ++cptr) {
if(((struct tcpip_uipstate *)cptr->appstate)->id == id) {
cptr->lport = 0;
}
}
}
/* for(i = 0; i < UIP_UDP_CONNS; ++i) {
if(((struct tcpip_uipstate *)uip_udp_conns[i].appstate)->id == id) {
uip_udp_conns[i].lport = 0;
}
}*/
#endif /* UIP_UDP */
break;
case TCP_POLL:
if(data != NULL) {
uip_poll_conn(data);
if(uip_len > 0) {
tcpip_output();
}
}
break;
case UDP_POLL:
if(data != NULL) {
uip_udp_periodic_conn(data);
if(uip_len > 0) {
tcpip_output();
}
}
break;
};
}
/*---------------------------------------------------------------------------*/
void
tcpip_poll_udp(struct uip_udp_conn *conn)
{
ek_post(s.id, UDP_POLL, conn);
}
/*---------------------------------------------------------------------------*/
void
tcpip_poll_tcp(struct uip_conn *conn)
{
ek_post(s.id, TCP_POLL, conn);
}
/*---------------------------------------------------------------------------*/
void
tcpip_uipcall(void)
{
register struct tcpip_uipstate *ts;
static unsigned char i;
register struct listenport *l;
if(uip_conn != NULL) {
ts = (struct tcpip_uipstate *)uip_conn->appstate;
} else {
ts = (struct tcpip_uipstate *)uip_udp_conn->appstate;
}
/* If this is a connection request for a listening port, we must
mark the connection with the right process ID. */
if(uip_connected()) {
l = &s.listenports[0];
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == uip_conn->lport &&
l->id != EK_ID_NONE) {
ts->id = l->id;
ts->state = NULL;
break;
}
++l;
}
}
ek_post_synch(ts->id, s.event, ts->state);
}
/*---------------------------------------------------------------------------*/
EK_POLLHANDLER(pollhandler)
{
static unsigned char i;
/* Check the clock so see if we should call the periodic uIP
processing. */
if(timer_expired(&periodic)) {
timer_restart(&periodic);
for(i = 0; i < UIP_CONNS; ++i) {
uip_periodic(i);
if(uip_len > 0) {
tcpip_output();
/* uip_fw_output();*/
}
}
for(i = 0; i < UIP_UDP_CONNS; i++) {
uip_udp_periodic(i);
if(uip_len > 0) {
tcpip_output();
/* uip_fw_output();*/
}
}
uip_fw_periodic();
}
}
/*---------------------------------------------------------------------------*/