RetroArch/wii/libogc/lwip/network.c
2022-05-30 22:02:24 -03:00

2253 lines
52 KiB
C

#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <ogcsys.h>
#include <lwp.h>
#include <video.h>
#include <message.h>
#include <mutex.h>
#include <cond.h>
#include <semaphore.h>
#include <processor.h>
#include <lwp_threads.h>
#include <lwp_watchdog.h>
#include <lwip/debug.h>
#include <lwip/opt.h>
#include <lwip/mem.h>
#include <lwip/memp.h>
#include <lwip/sys.h>
#include <lwip/stats.h>
#include <lwip/ip.h>
#include <lwip/raw.h>
#include <lwip/udp.h>
#include <lwip/tcp.h>
#include <lwip/dhcp.h>
#include <lwip/api.h>
#include <lwip/api_msg.h>
#include <lwip/tcpip.h>
#include <netif/etharp.h>
#include <netif/loopif.h>
#include <netif/gcif/gcif.h>
#include <sys/iosupport.h>
#include "network.h"
//#define _NET_DEBUG
#define ARP_TIMER_ID 0x00070041
#define TCP_TIMER_ID 0x00070042
#define DHCPCOARSE_TIMER_ID 0x00070043
#define DHCPFINE_TIMER_ID 0x00070044
#define STACKSIZE 32768
#define MQBOX_SIZE 256
#define NUM_SOCKETS MEMP_NUM_NETCONN
struct netsocket {
struct netconn *conn;
struct netbuf *lastdata;
u16 lastoffset,rcvevt,sendevt,flags;
s32 err;
};
struct netselect_cb {
struct netselect_cb *next;
fd_set *readset;
fd_set *writeset;
fd_set *exceptset;
u32 signaled;
mutex_t cond_lck;
cond_t cond;
};
typedef void (*apimsg_decode)(struct apimsg_msg *);
static u32 g_netinitiated = 0;
static u32 tcpiplayer_inited = 0;
static u64 net_tcp_ticks = 0;
static u64 net_dhcpcoarse_ticks = 0;
static u64 net_dhcpfine_ticks = 0;
static u64 net_arp_ticks = 0;
static wd_cntrl arp_time_cntrl;
static wd_cntrl tcp_timer_cntrl;
static wd_cntrl dhcp_coarsetimer_cntrl;
static wd_cntrl dhcp_finetimer_cntrl;
static struct netif g_hNetIF;
static struct netif g_hLoopIF;
static struct netsocket sockets[NUM_SOCKETS];
static struct netselect_cb *selectcb_list = NULL;
static sys_sem netsocket_sem;
static sys_sem sockselect_sem;
static sys_mbox netthread_mbox;
static sys_thread hnet_thread;
static u8 netthread_stack[STACKSIZE];
static u32 tcp_timer_active = 0;
static struct netbuf* netbuf_new();
static void netbuf_delete(struct netbuf *);
static void netbuf_copypartial(struct netbuf *,void *,u32,u32);
static void netbuf_ref(struct netbuf *,const void *,u32);
static struct netconn* netconn_new_with_callback(enum netconn_type,void (*)(struct netconn *,enum netconn_evt,u32));
static struct netconn* netconn_new_with_proto_and_callback(enum netconn_type,u16,void (*)(struct netconn *,enum netconn_evt,u32));
static err_t netconn_delete(struct netconn *);
static struct netconn* netconn_accept(struct netconn* );
static err_t netconn_peer(struct netconn *,struct ip_addr *,u16 *);
static err_t netconn_bind(struct netconn *,struct ip_addr *,u16);
static err_t netconn_listen(struct netconn *);
static struct netbuf* netconn_recv(struct netconn *);
static err_t netconn_send(struct netconn *,struct netbuf *);
static err_t netconn_write(struct netconn *,const void *,u32,u8);
static err_t netconn_connect(struct netconn *,struct ip_addr *,u16);
static err_t netconn_disconnect(struct netconn *);
static void do_newconn(struct apimsg_msg *);
static void do_delconn(struct apimsg_msg *);
static void do_bind(struct apimsg_msg *);
static void do_listen(struct apimsg_msg *);
static void do_connect(struct apimsg_msg *);
static void do_disconnect(struct apimsg_msg *);
static void do_accept(struct apimsg_msg *);
static void do_send(struct apimsg_msg *);
static void do_recv(struct apimsg_msg *);
static void do_write(struct apimsg_msg *);
static void do_close(struct apimsg_msg *);
static apimsg_decode decode[APIMSG_MAX] = {
do_newconn,
do_delconn,
do_bind,
do_connect,
do_disconnect,
do_listen,
do_accept,
do_send,
do_recv,
do_write,
do_close
};
static void apimsg_post(struct api_msg *);
static err_t net_input(struct pbuf *,struct netif *);
static void net_apimsg(struct api_msg *);
static err_t net_callback(void (*)(void *),void *);
static void* net_thread(void *);
static void tmr_callback(void *arg)
{
void (*functor)() = (void(*)())arg;
if(functor) functor();
}
/* low level stuff */
static void __dhcpcoarse_timer(void *arg)
{
__lwp_thread_dispatchdisable();
net_callback(tmr_callback,(void*)dhcp_coarse_tmr);
__lwp_wd_insert_ticks(&dhcp_coarsetimer_cntrl,net_dhcpcoarse_ticks);
__lwp_thread_dispatchunnest();
}
static void __dhcpfine_timer(void *arg)
{
__lwp_thread_dispatchdisable();
net_callback(tmr_callback,(void*)dhcp_fine_tmr);
__lwp_wd_insert_ticks(&dhcp_finetimer_cntrl,net_dhcpfine_ticks);
__lwp_thread_dispatchunnest();
}
static void __tcp_timer(void *arg)
{
#ifdef _NET_DEBUG
printf("__tcp_timer(%d,%p,%p)\n",tcp_timer_active,tcp_active_pcbs,tcp_tw_pcbs);
#endif
__lwp_thread_dispatchdisable();
net_callback(tmr_callback,(void*)tcp_tmr);
if (tcp_active_pcbs || tcp_tw_pcbs) {
__lwp_wd_insert_ticks(&tcp_timer_cntrl,net_tcp_ticks);
} else
tcp_timer_active = 0;
__lwp_thread_dispatchunnest();
}
static void __arp_timer(void *arg)
{
__lwp_thread_dispatchdisable();
net_callback(tmr_callback,(void*)etharp_tmr);
__lwp_wd_insert_ticks(&arp_time_cntrl,net_arp_ticks);
__lwp_thread_dispatchunnest();
}
void tcp_timer_needed(void)
{
#ifdef _NET_DEBUG
printf("tcp_timer_needed()\n");
#endif
if(!tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
tcp_timer_active = 1;
__lwp_wd_insert_ticks(&tcp_timer_cntrl,net_tcp_ticks);
}
}
/* netbuf part */
static __inline__ u16 netbuf_len(struct netbuf *buf)
{
return ((buf && buf->p)?buf->p->tot_len:0);
}
static __inline__ struct ip_addr* netbuf_fromaddr(struct netbuf *buf)
{
return (buf?buf->fromaddr:NULL);
}
static __inline__ u16 netbuf_fromport(struct netbuf *buf)
{
return (buf?buf->fromport:0);
}
static void netbuf_copypartial(struct netbuf *buf,void *dataptr,u32 len,u32 offset)
{
struct pbuf *p;
u16 i,left;
left = 0;
if(buf==NULL || dataptr==NULL) return;
for(p=buf->p;left<len && p!=NULL;p=p->next) {
if(offset!=0 && offset>=p->len)
offset -= p->len;
else {
for(i=offset;i<p->len;i++) {
((u8*)dataptr)[left] = ((u8*)p->payload)[i];
if(++left>=len) return;
}
offset = 0;
}
}
}
static struct netbuf* netbuf_new()
{
struct netbuf *buf = NULL;
buf = memp_malloc(MEMP_NETBUF);
if(buf) {
buf->p = NULL;
buf->ptr = NULL;
}
return buf;
}
static void netbuf_delete(struct netbuf *buf)
{
if(buf!=NULL) {
if(buf->p!=NULL) pbuf_free(buf->p);
memp_free(MEMP_NETBUF,buf);
}
}
static void netbuf_ref(struct netbuf *buf, const void *dataptr,u32 size)
{
if(buf->p!=NULL) pbuf_free(buf->p);
buf->p = pbuf_alloc(PBUF_TRANSPORT,0,PBUF_REF);
buf->p->payload = (void*)dataptr;
buf->p->len = buf->p->tot_len = size;
buf->ptr = buf->p;
}
/* netconn part */
static inline enum netconn_type netconn_type(struct netconn *conn)
{
return conn->type;
}
static struct netconn* netconn_new_with_callback(enum netconn_type t,void (*cb)(struct netconn*,enum netconn_evt,u32))
{
return netconn_new_with_proto_and_callback(t,0,cb);
}
static struct netconn* netconn_new_with_proto_and_callback(enum netconn_type t,u16 proto,void (*cb)(struct netconn *,enum netconn_evt,u32))
{
u32 dummy = 0;
struct netconn *conn;
struct api_msg *msg;
conn = memp_malloc(MEMP_NETCONN);
if(!conn) return NULL;
conn->err = ERR_OK;
conn->type = t;
conn->pcb.tcp = NULL;
if(MQ_Init(&conn->mbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) {
memp_free(MEMP_NETCONN,conn);
return NULL;
}
if(LWP_SemInit(&conn->sem,0,1)==-1) {
MQ_Close(conn->mbox);
memp_free(MEMP_NETCONN,conn);
return NULL;
}
conn->acceptmbox = SYS_MBOX_NULL;
conn->recvmbox = SYS_MBOX_NULL;
conn->state = NETCONN_NONE;
conn->socket = 0;
conn->callback = cb;
conn->recvavail = 0;
msg = memp_malloc(MEMP_API_MSG);
if(!msg) {
MQ_Close(conn->mbox);
memp_free(MEMP_NETCONN,conn);
return NULL;
}
msg->type = APIMSG_NEWCONN;
msg->msg.msg.bc.port = proto;
msg->msg.conn = conn;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
memp_free(MEMP_API_MSG,msg);
if(conn->err!=ERR_OK) {
MQ_Close(conn->mbox);
memp_free(MEMP_NETCONN,conn);
return NULL;
}
return conn;
}
static err_t netconn_delete(struct netconn *conn)
{
u32 dummy = 0;
struct api_msg *msg;
sys_mbox mbox;
void *mem;
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_delete(%p)\n", conn));
if(!conn) return ERR_OK;
msg = memp_malloc(MEMP_API_MSG);
if(!msg) return ERR_MEM;
msg->type = APIMSG_DELCONN;
msg->msg.conn = conn;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
memp_free(MEMP_API_MSG,msg);
mbox = conn->recvmbox;
conn->recvmbox = SYS_MBOX_NULL;
if(mbox!=SYS_MBOX_NULL) {
while(MQ_Receive(mbox,(mqmsg_t)&mem,MQ_MSG_NOBLOCK)==TRUE) {
if(mem!=NULL) {
if(conn->type==NETCONN_TCP)
pbuf_free((struct pbuf*)mem);
else
netbuf_delete((struct netbuf*)mem);
}
}
MQ_Close(mbox);
}
mbox = conn->acceptmbox;
conn->acceptmbox = SYS_MBOX_NULL;
if(mbox!=SYS_MBOX_NULL) {
while(MQ_Receive(mbox,(mqmsg_t)&mem,MQ_MSG_NOBLOCK)==TRUE) {
if(mem!=NULL) netconn_delete((struct netconn*)mem);
}
MQ_Close(mbox);
}
MQ_Close(conn->mbox);
conn->mbox = SYS_MBOX_NULL;
LWP_SemDestroy(conn->sem);
conn->sem = SYS_SEM_NULL;
memp_free(MEMP_NETCONN,conn);
return ERR_OK;
}
static struct netconn* netconn_accept(struct netconn* conn)
{
struct netconn *newconn;
if(conn==NULL) return NULL;
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_accept(%p)\n", conn));
MQ_Receive(conn->acceptmbox,(mqmsg_t)&newconn,MQ_MSG_BLOCK);
if(conn->callback)
(*conn->callback)(conn,NETCONN_EVTRCVMINUS,0);
return newconn;
}
static err_t netconn_peer(struct netconn *conn,struct ip_addr *addr,u16 *port)
{
switch(conn->type) {
case NETCONN_RAW:
return ERR_CONN;
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
if(conn->pcb.udp==NULL || ((conn->pcb.udp->flags&UDP_FLAGS_CONNECTED)==0))
return ERR_CONN;
*addr = conn->pcb.udp->remote_ip;
*port = conn->pcb.udp->remote_port;
break;
case NETCONN_TCP:
if(conn->pcb.tcp==NULL)
return ERR_CONN;
*addr = conn->pcb.tcp->remote_ip;
*port = conn->pcb.tcp->remote_port;
break;
}
return (conn->err = ERR_OK);
}
static err_t netconn_bind(struct netconn *conn,struct ip_addr *addr,u16 port)
{
u32 dummy = 0;
struct api_msg *msg;
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_bind(%p)\n", conn));
if(conn==NULL) return ERR_VAL;
if(conn->type!=NETCONN_TCP && conn->recvmbox==SYS_MBOX_NULL) {
if(MQ_Init(&conn->recvmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) return ERR_MEM;
}
if((msg=memp_malloc(MEMP_API_MSG))==NULL)
return (conn->err = ERR_MEM);
msg->type = APIMSG_BIND;
msg->msg.conn = conn;
msg->msg.msg.bc.ipaddr = addr;
msg->msg.msg.bc.port = port;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
memp_free(MEMP_API_MSG,msg);
return conn->err;
}
static err_t netconn_listen(struct netconn *conn)
{
u32 dummy = 0;
struct api_msg *msg;
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_listen(%p)\n", conn));
if(conn==NULL) return -1;
if(conn->acceptmbox==SYS_MBOX_NULL) {
if(MQ_Init(&conn->acceptmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) return ERR_MEM;
}
if((msg=memp_malloc(MEMP_API_MSG))==NULL) return (conn->err = ERR_MEM);
msg->type = APIMSG_LISTEN;
msg->msg.conn = conn;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
memp_free(MEMP_API_MSG,msg);
return conn->err;
}
static struct netbuf* netconn_recv(struct netconn *conn)
{
u32 dummy = 0;
struct api_msg *msg;
struct netbuf *buf;
struct pbuf *p;
u16 len;
if(conn==NULL) return NULL;
if(conn->recvmbox==SYS_MBOX_NULL) {
conn->err = ERR_CONN;
return NULL;
}
if(conn->err!=ERR_OK) return NULL;
if(conn->type==NETCONN_TCP) {
if(conn->pcb.tcp->state==LISTEN) {
conn->err = ERR_CONN;
return NULL;
}
buf = memp_malloc(MEMP_NETBUF);
if(buf==NULL) {
conn->err = ERR_MEM;
return NULL;
}
MQ_Receive(conn->recvmbox,(mqmsg_t)&p,MQ_MSG_BLOCK);
if(p!=NULL) {
len = p->tot_len;
conn->recvavail -= len;
} else
len = 0;
if(conn->callback)
(*conn->callback)(conn,NETCONN_EVTRCVMINUS,len);
if(p==NULL) {
memp_free(MEMP_NETBUF,buf);
MQ_Close(conn->recvmbox);
conn->recvmbox = SYS_MBOX_NULL;
return NULL;
}
buf->p = p;
buf->ptr = p;
buf->fromport = 0;
buf->fromaddr = NULL;
if((msg=memp_malloc(MEMP_API_MSG))==NULL) {
conn->err = ERR_MEM;
return buf;
}
msg->type = APIMSG_RECV;
msg->msg.conn = conn;
if(buf!=NULL)
msg->msg.msg.len = len;
else
msg->msg.msg.len = 1;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
memp_free(MEMP_API_MSG,msg);
} else {
MQ_Receive(conn->recvmbox,(mqmsg_t)&buf,MQ_MSG_BLOCK);
conn->recvavail -= buf->p->tot_len;
if(conn->callback)
(*conn->callback)(conn,NETCONN_EVTRCVMINUS,buf->p->tot_len);
}
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p (err %d)\n", (void *)buf, conn->err));
return buf;
}
static err_t netconn_send(struct netconn *conn,struct netbuf *buf)
{
u32 dummy = 0;
struct api_msg *msg;
if(conn==NULL) return ERR_VAL;
if(conn->err!=ERR_OK) return conn->err;
if((msg=memp_malloc(MEMP_API_MSG))==NULL) return (conn->err = ERR_MEM);
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %d bytes\n", buf->p->tot_len));
msg->type = APIMSG_SEND;
msg->msg.conn = conn;
msg->msg.msg.p = buf->p;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
memp_free(MEMP_API_MSG,msg);
return conn->err;
}
static err_t netconn_write(struct netconn *conn,const void *dataptr,u32 size,u8 copy)
{
u32 dummy = 0;
struct api_msg *msg;
u16 len,snd_buf;
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write(%d)\n",conn->err));
if(conn==NULL) return ERR_VAL;
if(conn->err!=ERR_OK) return conn->err;
if((msg=memp_malloc(MEMP_API_MSG))==NULL) return (conn->err = ERR_MEM);
msg->type = APIMSG_WRITE;
msg->msg.conn = conn;
conn->state = NETCONN_WRITE;
while(conn->err==ERR_OK && size>0) {
msg->msg.msg.w.dataptr = (void*)dataptr;
msg->msg.msg.w.copy = copy;
if(conn->type==NETCONN_TCP) {
while((snd_buf=tcp_sndbuf(conn->pcb.tcp))==0) {
LWIP_DEBUGF(API_LIB_DEBUG,("netconn_write: tcp_sndbuf = 0,err = %d\n", conn->err));
LWP_SemWait(conn->sem);
if(conn->err!=ERR_OK) goto ret;
}
if(size>snd_buf)
len = snd_buf;
else
len = size;
} else
len = size;
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: writing %d bytes (%d)\n", len, copy));
msg->msg.msg.w.len = len;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
if(conn->err==ERR_OK) {
LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: %d bytes written\n",len));
dataptr = (void*)((char*)dataptr+len);
size -= len;
} else if(conn->err==ERR_MEM) {
LWIP_DEBUGF(API_LIB_DEBUG,("netconn_write: mem err\n"));
conn->err = ERR_OK;
LWP_SemWait(conn->sem);
} else {
LWIP_DEBUGF(API_LIB_DEBUG,("netconn_write: err = %d\n", conn->err));
break;
}
}
ret:
memp_free(MEMP_API_MSG,msg);
conn->state = NETCONN_NONE;
return conn->err;
}
static err_t netconn_connect(struct netconn *conn,struct ip_addr *addr,u16 port)
{
u32 dummy = 0;
struct api_msg *msg;
if(conn==NULL) return -1;
if(conn->recvmbox==SYS_MBOX_NULL) {
if(MQ_Init(&conn->recvmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) return ERR_MEM;
}
if((msg=memp_malloc(MEMP_API_MSG))==NULL) return ERR_MEM;
msg->type = APIMSG_CONNECT;
msg->msg.conn = conn;
msg->msg.msg.bc.ipaddr = addr;
msg->msg.msg.bc.port = port;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
memp_free(MEMP_API_MSG,msg);
return conn->err;
}
static err_t netconn_disconnect(struct netconn *conn)
{
u32 dummy = 0;
struct api_msg *msg;
if(conn==NULL) return ERR_VAL;
if((msg=memp_malloc(MEMP_API_MSG))==NULL) return ERR_MEM;
msg->type = APIMSG_DISCONNECT;
msg->msg.conn = conn;
apimsg_post(msg);
MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK);
memp_free(MEMP_API_MSG,msg);
return conn->err;
}
/* api msg part */
static u8_t recv_raw(void *arg,struct raw_pcb *pcb,struct pbuf *p,struct ip_addr *addr)
{
struct netbuf *buf;
struct netconn *conn = (struct netconn*)arg;
if(!conn) return 0;
if(conn->recvmbox!=SYS_MBOX_NULL) {
if((buf=memp_malloc(MEMP_NETBUF))==NULL) return 0;
pbuf_ref(p);
buf->p = p;
buf->ptr = p;
buf->fromaddr = addr;
buf->fromport = pcb->protocol;
conn->recvavail += p->tot_len;
if(conn->callback)
(*conn->callback)(conn,NETCONN_EVTRCVPLUS,p->tot_len);
MQ_Send(conn->recvmbox,buf,MQ_MSG_BLOCK);
}
return 0;
}
static void recv_udp(void *arg,struct udp_pcb *pcb,struct pbuf *p,struct ip_addr *addr,u16 port)
{
struct netbuf *buf;
struct netconn *conn = (struct netconn*)arg;
if(!conn) {
pbuf_free(p);
return;
}
if(conn->recvmbox!=SYS_MBOX_NULL) {
buf = memp_malloc(MEMP_NETBUF);
if(!buf) {
pbuf_free(p);
return;
}
buf->p = p;
buf->ptr = p;
buf->fromaddr = addr;
buf->fromport = port;
conn->recvavail += p->tot_len;
if(conn->callback)
(*conn->callback)(conn,NETCONN_EVTRCVPLUS,p->tot_len);
MQ_Send(conn->recvmbox,buf,MQ_MSG_BLOCK);
}
}
static err_t recv_tcp(void *arg,struct tcp_pcb *pcb,struct pbuf *p,err_t err)
{
u16 len;
struct netconn *conn = (struct netconn*)arg;
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: recv_tcp(%p,%p,%p,%d)\n",arg,pcb,p,err));
if(conn==NULL) {
pbuf_free(p);
return ERR_VAL;
}
if(conn->recvmbox!=SYS_MBOX_NULL) {
conn->err = err;
if(p!=NULL) {
len = p->tot_len;
conn->recvavail += len;
} else len = 0;
if(conn->callback)
(*conn->callback)(conn,NETCONN_EVTRCVPLUS,len);
MQ_Send(conn->recvmbox,p,MQ_MSG_BLOCK);
}
return ERR_OK;
}
static void err_tcp(void *arg,err_t err)
{
u32 dummy = 0;
struct netconn *conn = (struct netconn*)arg;
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: err_tcp: %d\n",err));
if(conn) {
conn->err = err;
conn->pcb.tcp = NULL;
if(conn->recvmbox!=SYS_MBOX_NULL) {
if(conn->callback) (*conn->callback)(conn,NETCONN_EVTRCVPLUS,0);
MQ_Send(conn->recvmbox,&dummy,MQ_MSG_BLOCK);
}
if(conn->mbox!=SYS_MBOX_NULL) {
MQ_Send(conn->mbox,&dummy,MQ_MSG_BLOCK);
}
if(conn->acceptmbox!=SYS_MBOX_NULL) {
if(conn->callback) (*conn->callback)(conn,NETCONN_EVTRCVPLUS,0);
MQ_Send(conn->acceptmbox,&dummy,MQ_MSG_BLOCK);
}
if(conn->sem!=SYS_SEM_NULL) {
LWP_SemPost(conn->sem);
}
}
}
static err_t poll_tcp(void *arg,struct tcp_pcb *pcb)
{
struct netconn *conn = (struct netconn*)arg;
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: poll_tcp\n"));
if(conn && conn->sem!=SYS_SEM_NULL && (conn->state==NETCONN_WRITE || conn->state==NETCONN_CLOSE))
LWP_SemPost(conn->sem);
return ERR_OK;
}
static err_t sent_tcp(void *arg,struct tcp_pcb *pcb,u16 len)
{
struct netconn *conn = (struct netconn*)arg;
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: sent_tcp: sent %d bytes\n",len));
if(conn && conn->sem!=SYS_SEM_NULL)
LWP_SemPost(conn->sem);
if(conn && conn->callback) {
if(tcp_sndbuf(conn->pcb.tcp)>TCP_SNDLOWAT)
(*conn->callback)(conn,NETCONN_EVTSENDPLUS,len);
}
return ERR_OK;
}
static void setuptcp(struct netconn *conn)
{
struct tcp_pcb *pcb = conn->pcb.tcp;
tcp_arg(pcb,conn);
tcp_recv(pcb,recv_tcp);
tcp_sent(pcb,sent_tcp);
tcp_poll(pcb,poll_tcp,4);
tcp_err(pcb,err_tcp);
}
static err_t accept_func(void *arg,struct tcp_pcb *newpcb,err_t err)
{
sys_mbox mbox;
struct netconn *newconn,*conn = (struct netconn*)arg;
LWIP_DEBUGF(API_LIB_DEBUG, ("accept_func: %p %p %d\n",arg,newpcb,err));
mbox = conn->acceptmbox;
newconn = memp_malloc(MEMP_NETCONN);
if(newconn==NULL) return ERR_MEM;
if(MQ_Init(&newconn->recvmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) {
memp_free(MEMP_NETCONN,newconn);
return ERR_MEM;
}
if(MQ_Init(&newconn->mbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) {
MQ_Close(newconn->recvmbox);
memp_free(MEMP_NETCONN,newconn);
return ERR_MEM;
}
if(LWP_SemInit(&newconn->sem,0,1)==-1) {
MQ_Close(newconn->recvmbox);
MQ_Close(newconn->mbox);
memp_free(MEMP_NETCONN,newconn);
return ERR_MEM;
}
newconn->type = NETCONN_TCP;
newconn->pcb.tcp = newpcb;
setuptcp(newconn);
newconn->acceptmbox = SYS_MBOX_NULL;
newconn->err = err;
if(conn->callback) {
(*conn->callback)(conn,NETCONN_EVTRCVPLUS,0);
}
newconn->callback = conn->callback;
newconn->socket = -1;
newconn->recvavail = 0;
MQ_Send(mbox,newconn,MQ_MSG_BLOCK);
return ERR_OK;
}
static void do_newconn(struct apimsg_msg *msg)
{
u32 dummy = 0;
if(msg->conn->pcb.tcp) {
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
return;
}
msg->conn->err = ERR_OK;
switch(msg->conn->type) {
case NETCONN_RAW:
msg->conn->pcb.raw = raw_new(msg->msg.bc.port);
raw_recv(msg->conn->pcb.raw,recv_raw,msg->conn);
break;
case NETCONN_UDPLITE:
msg->conn->pcb.udp = udp_new();
if(!msg->conn->pcb.udp) {
msg->conn->err = ERR_MEM;
break;
}
udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_UDPLITE);
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_UDPNOCHKSUM:
msg->conn->pcb.udp = udp_new();
if(!msg->conn->pcb.udp) {
msg->conn->err = ERR_MEM;
break;
}
udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_NOCHKSUM);
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_UDP:
msg->conn->pcb.udp = udp_new();
if(!msg->conn->pcb.udp) {
msg->conn->err = ERR_MEM;
break;
}
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_TCP:
msg->conn->pcb.tcp = tcp_new();
if(!msg->conn->pcb.tcp) {
msg->conn->err = ERR_MEM;
break;
}
setuptcp(msg->conn);
break;
default:
break;
}
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static void do_delconn(struct apimsg_msg *msg)
{
u32 dummy = 0;
if(msg->conn->pcb.tcp) {
switch(msg->conn->type) {
case NETCONN_RAW:
raw_remove(msg->conn->pcb.raw);
break;
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
msg->conn->pcb.udp->recv_arg = NULL;
udp_remove(msg->conn->pcb.udp);
break;
case NETCONN_TCP:
if(msg->conn->pcb.tcp->state==LISTEN) {
tcp_arg(msg->conn->pcb.tcp,NULL);
tcp_accept(msg->conn->pcb.tcp,NULL);
tcp_close(msg->conn->pcb.tcp);
} else {
tcp_arg(msg->conn->pcb.tcp,NULL);
tcp_sent(msg->conn->pcb.tcp,NULL);
tcp_recv(msg->conn->pcb.tcp,NULL);
tcp_poll(msg->conn->pcb.tcp,NULL,0);
tcp_err(msg->conn->pcb.tcp,NULL);
if(tcp_close(msg->conn->pcb.tcp)!=ERR_OK)
tcp_abort(msg->conn->pcb.tcp);
}
break;
default:
break;
}
}
if(msg->conn->callback) {
(*msg->conn->callback)(msg->conn,NETCONN_EVTRCVPLUS,0);
(*msg->conn->callback)(msg->conn,NETCONN_EVTSENDPLUS,0);
}
if(msg->conn->mbox!=SYS_MBOX_NULL)
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static void do_bind(struct apimsg_msg *msg)
{
u32 dummy = 0;
if(msg->conn->pcb.tcp==NULL) {
switch(msg->conn->type) {
case NETCONN_RAW:
msg->conn->pcb.raw = raw_new(msg->msg.bc.port);
raw_recv(msg->conn->pcb.raw,recv_raw,msg->conn);
break;
case NETCONN_UDPLITE:
msg->conn->pcb.udp = udp_new();
udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_UDPLITE);
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_UDPNOCHKSUM:
msg->conn->pcb.udp = udp_new();
udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_NOCHKSUM);
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_UDP:
msg->conn->pcb.udp = udp_new();
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_TCP:
msg->conn->pcb.tcp = tcp_new();
setuptcp(msg->conn);
break;
default:
break;
}
}
switch(msg->conn->type) {
case NETCONN_RAW:
msg->conn->err = raw_bind(msg->conn->pcb.raw,msg->msg.bc.ipaddr);
break;
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
msg->conn->err = udp_bind(msg->conn->pcb.udp,msg->msg.bc.ipaddr,msg->msg.bc.port);
break;
case NETCONN_TCP:
msg->conn->err = tcp_bind(msg->conn->pcb.tcp,msg->msg.bc.ipaddr,msg->msg.bc.port);
break;
default:
break;
}
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static err_t do_connected(void *arg,struct tcp_pcb *pcb,err_t err)
{
u32 dummy = 0;
struct netconn *conn = (struct netconn*)arg;
if(!conn) return ERR_VAL;
conn->err = err;
if(conn->type==NETCONN_TCP && err==ERR_OK) setuptcp(conn);
MQ_Send(conn->mbox,&dummy,MQ_MSG_BLOCK);
return ERR_OK;
}
static void do_connect(struct apimsg_msg *msg)
{
u32 dummy = 0;
if(!msg->conn->pcb.tcp) {
switch(msg->conn->type) {
case NETCONN_RAW:
msg->conn->pcb.raw = raw_new(msg->msg.bc.port);
raw_recv(msg->conn->pcb.raw,recv_raw,msg->conn);
break;
case NETCONN_UDPLITE:
msg->conn->pcb.udp = udp_new();
if(!msg->conn->pcb.udp) {
msg->conn->err = ERR_MEM;
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
return;
}
udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_UDPLITE);
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_UDPNOCHKSUM:
msg->conn->pcb.udp = udp_new();
if(!msg->conn->pcb.udp) {
msg->conn->err = ERR_MEM;
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
return;
}
udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_NOCHKSUM);
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_UDP:
msg->conn->pcb.udp = udp_new();
if(!msg->conn->pcb.udp) {
msg->conn->err = ERR_MEM;
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
return;
}
udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn);
break;
case NETCONN_TCP:
msg->conn->pcb.tcp = tcp_new();
if(!msg->conn->pcb.tcp) {
msg->conn->err = ERR_MEM;
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
return;
}
break;
default:
break;
}
}
switch(msg->conn->type) {
case NETCONN_RAW:
raw_connect(msg->conn->pcb.raw,msg->msg.bc.ipaddr);
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
break;
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
udp_connect(msg->conn->pcb.udp,msg->msg.bc.ipaddr,msg->msg.bc.port);
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
break;
case NETCONN_TCP:
setuptcp(msg->conn);
tcp_connect(msg->conn->pcb.tcp,msg->msg.bc.ipaddr,msg->msg.bc.port,do_connected);
break;
default:
break;
}
}
static void do_disconnect(struct apimsg_msg *msg)
{
u32 dummy = 0;
switch(msg->conn->type) {
case NETCONN_RAW:
break;
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
udp_disconnect(msg->conn->pcb.udp);
break;
case NETCONN_TCP:
break;
}
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static void do_listen(struct apimsg_msg *msg)
{
u32 dummy = 0;
if(msg->conn->pcb.tcp!=NULL) {
switch(msg->conn->type) {
case NETCONN_RAW:
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: listen RAW: cannot listen for RAW.\n"));
break;
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: listen UDP: cannot listen for UDP.\n"));
break;
case NETCONN_TCP:
msg->conn->pcb.tcp = tcp_listen(msg->conn->pcb.tcp);
if(msg->conn->pcb.tcp==NULL)
msg->conn->err = ERR_MEM;
else {
if(msg->conn->acceptmbox==SYS_MBOX_NULL) {
if(MQ_Init(&msg->conn->acceptmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) {
msg->conn->err = ERR_MEM;
break;
}
}
tcp_arg(msg->conn->pcb.tcp,msg->conn);
tcp_accept(msg->conn->pcb.tcp,accept_func);
}
break;
default:
break;
}
}
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static void do_accept(struct apimsg_msg *msg)
{
if(msg->conn->pcb.tcp) {
switch(msg->conn->type) {
case NETCONN_RAW:
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: accept RAW: cannot accept for RAW.\n"));
break;
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: accept UDP: cannot accept for UDP.\n"));
break;
case NETCONN_TCP:
break;
}
}
}
static void do_send(struct apimsg_msg *msg)
{
u32 dummy = 0;
if(msg->conn->pcb.tcp) {
switch(msg->conn->type) {
case NETCONN_RAW:
raw_send(msg->conn->pcb.raw,msg->msg.p);
break;
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
udp_send(msg->conn->pcb.udp,msg->msg.p);
break;
case NETCONN_TCP:
break;
}
}
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static void do_recv(struct apimsg_msg *msg)
{
u32 dummy = 0;
if(msg->conn->pcb.tcp && msg->conn->type==NETCONN_TCP) {
tcp_recved(msg->conn->pcb.tcp,msg->msg.len);
}
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static void do_write(struct apimsg_msg *msg)
{
err_t err;
u32 dummy = 0;
if(msg->conn->pcb.tcp) {
switch(msg->conn->type) {
case NETCONN_RAW:
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
msg->conn->err = ERR_VAL;
break;
case NETCONN_TCP:
err = tcp_write(msg->conn->pcb.tcp,msg->msg.w.dataptr,msg->msg.w.len,msg->msg.w.copy);
if(err==ERR_OK && (!msg->conn->pcb.tcp->unacked || (msg->conn->pcb.tcp->flags&TF_NODELAY)
|| msg->conn->pcb.tcp->snd_queuelen>1)) {
LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: TCP write: tcp_output.\n"));
tcp_output(msg->conn->pcb.tcp);
}
msg->conn->err = err;
if(msg->conn->callback) {
if(err==ERR_OK) {
if(tcp_sndbuf(msg->conn->pcb.tcp)<=TCP_SNDLOWAT)
(*msg->conn->callback)(msg->conn,NETCONN_EVTSENDMINUS,msg->msg.w.len);
}
}
break;
default:
break;
}
}
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static void do_close(struct apimsg_msg *msg)
{
u32 dummy = 0;
err_t err = ERR_OK;
if(msg->conn->pcb.tcp) {
switch(msg->conn->type) {
case NETCONN_RAW:
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
case NETCONN_UDP:
break;
case NETCONN_TCP:
if(msg->conn->pcb.tcp->state==LISTEN)
err = tcp_close(msg->conn->pcb.tcp);
else if(msg->conn->pcb.tcp->state==CLOSE_WAIT)
err = tcp_output(msg->conn->pcb.tcp);
msg->conn->err = err;
break;
default:
break;
}
}
MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK);
}
static void apimsg_input(struct api_msg *msg)
{
decode[msg->type](&(msg->msg));
}
static void apimsg_post(struct api_msg *msg)
{
net_apimsg(msg);
}
/* tcpip thread part */
static err_t net_input(struct pbuf *p,struct netif *inp)
{
struct net_msg *msg = memp_malloc(MEMP_TCPIP_MSG);
LWIP_DEBUGF(TCPIP_DEBUG, ("net_input: %p %p\n", p,inp));
if(msg==NULL) {
LWIP_ERROR(("net_input: msg out of memory.\n"));
pbuf_free(p);
return ERR_MEM;
}
msg->type = NETMSG_INPUT;
msg->msg.inp.p = p;
msg->msg.inp.net = inp;
MQ_Send(netthread_mbox,msg,MQ_MSG_BLOCK);
return ERR_OK;
}
static void net_apimsg(struct api_msg *apimsg)
{
struct net_msg *msg = memp_malloc(MEMP_TCPIP_MSG);
LWIP_DEBUGF(TCPIP_DEBUG, ("net_apimsg: %p\n",apimsg));
if(msg==NULL) {
LWIP_ERROR(("net_apimsg: msg out of memory.\n"));
memp_free(MEMP_API_MSG,apimsg);
return;
}
msg->type = NETMSG_API;
msg->msg.apimsg = apimsg;
MQ_Send(netthread_mbox,msg,MQ_MSG_BLOCK);
}
static err_t net_callback(void (*f)(void *),void *ctx)
{
struct net_msg *msg = memp_malloc(MEMP_TCPIP_MSG);
LWIP_DEBUGF(TCPIP_DEBUG, ("net_callback: %p(%p)\n", f,ctx));
if(msg==NULL) {
LWIP_ERROR(("net_apimsg: msg out of memory.\n"));
return ERR_MEM;
}
msg->type = NETMSG_CALLBACK;
msg->msg.cb.f = f;
msg->msg.cb.ctx = ctx;
MQ_Send(netthread_mbox,msg,MQ_MSG_BLOCK);
return ERR_OK;
}
static void* net_thread(void *arg)
{
struct net_msg *msg;
struct timespec tb;
sys_sem sem = (sys_sem)arg;
etharp_init();
ip_init();
udp_init();
tcp_init();
tb.tv_sec = ARP_TMR_INTERVAL/TB_MSPERSEC;
tb.tv_nsec = 0;
net_arp_ticks = __lwp_wd_calc_ticks(&tb);
__lwp_wd_initialize(&arp_time_cntrl,__arp_timer,ARP_TIMER_ID,NULL);
__lwp_wd_insert_ticks(&arp_time_cntrl,net_arp_ticks);
tb.tv_sec = 0;
tb.tv_nsec = TCP_TMR_INTERVAL*TB_NSPERMS;
net_tcp_ticks = __lwp_wd_calc_ticks(&tb);
__lwp_wd_initialize(&tcp_timer_cntrl,__tcp_timer,TCP_TIMER_ID,NULL);
LWP_SemPost(sem);
LWIP_DEBUGF(TCPIP_DEBUG, ("net_thread(%p)\n",arg));
while(1) {
MQ_Receive(netthread_mbox,(mqmsg_t)&msg,MQ_MSG_BLOCK);
switch(msg->type) {
case NETMSG_API:
LWIP_DEBUGF(TCPIP_DEBUG, ("net_thread: API message %p\n", (void *)msg));
apimsg_input(msg->msg.apimsg);
break;
case NETMSG_INPUT:
LWIP_DEBUGF(TCPIP_DEBUG, ("net_thread: IP packet %p\n", (void *)msg));
bba_process(msg->msg.inp.p,msg->msg.inp.net);
break;
case NETMSG_CALLBACK:
LWIP_DEBUGF(TCPIP_DEBUG, ("net_thread: CALLBACK %p\n", (void *)msg));
msg->msg.cb.f(msg->msg.cb.ctx);
break;
default:
break;
}
memp_free(MEMP_TCPIP_MSG,msg);
}
return NULL;
}
/* sockets part */
static s32 alloc_socket(struct netconn *conn)
{
s32 i;
LWP_SemWait(netsocket_sem);
for(i=0;i<NUM_SOCKETS;i++) {
if(!sockets[i].conn) {
sockets[i].conn = conn;
sockets[i].lastdata = NULL;
sockets[i].lastoffset = 0;
sockets[i].rcvevt = 0;
sockets[i].sendevt = 1;
sockets[i].flags = 0;
sockets[i].err = 0;
LWP_SemPost(netsocket_sem);
return i;
}
}
LWP_SemPost(netsocket_sem);
return -1;
}
static struct netsocket* get_socket(s32 s)
{
struct netsocket *sock;
if(s<0 || s>NUM_SOCKETS) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s));
return NULL;
}
sock = &sockets[s];
if(!sock->conn) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): no active\n", s));
return NULL;
}
return sock;
}
static void evt_callback(struct netconn *conn,enum netconn_evt evt,u32 len)
{
s32 s;
struct netsocket *sock;
struct netselect_cb *scb;
if(conn) {
s = conn->socket;
if(s<0) {
if(evt==NETCONN_EVTRCVPLUS)
conn->socket--;
return;
}
sock = get_socket(s);
if(!sock) return;
} else
return;
LWP_SemWait(sockselect_sem);
switch(evt) {
case NETCONN_EVTRCVPLUS:
sock->rcvevt++;
break;
case NETCONN_EVTRCVMINUS:
sock->rcvevt--;
break;
case NETCONN_EVTSENDPLUS:
sock->sendevt = 1;
break;
case NETCONN_EVTSENDMINUS:
sock->sendevt = 0;
break;
}
LWP_SemPost(sockselect_sem);
while(1) {
LWP_SemWait(sockselect_sem);
for(scb = selectcb_list;scb;scb = scb->next) {
if(scb->signaled==0) {
if(scb->readset && FD_ISSET(s,scb->readset))
if(sock->rcvevt) break;
if(scb->writeset && FD_ISSET(s,scb->writeset))
if(sock->sendevt) break;
}
}
if(scb) {
scb->signaled = 1;
LWP_SemPost(sockselect_sem);
LWP_MutexLock(scb->cond_lck);
LWP_CondSignal(scb->cond);
LWP_MutexUnlock(scb->cond_lck);
} else {
LWP_SemPost(sockselect_sem);
break;
}
}
}
extern const devoptab_t dotab_stdnet;
s32 if_configex(struct in_addr *local_ip,struct in_addr *netmask,struct in_addr *gateway,bool use_dhcp, int max_retries)
{
s32 ret = 0;
struct ip_addr loc_ip, mask, gw;
struct netif *pnet;
struct timespec tb;
dev_s hbba = NULL;
if(g_netinitiated) return 0;
g_netinitiated = 1;
// AddDevice(&dotab_stdnet);
#ifdef STATS
stats_init();
#endif /* STATS */
sys_init();
mem_init();
memp_init();
pbuf_init();
netif_init();
// init tcpip thread message box
if(MQ_Init(&netthread_mbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) return -1;
// create & setup interface
loc_ip.addr = 0;
mask.addr = 0;
gw.addr = 0;
if(use_dhcp==FALSE) {
if( !gateway || gateway->s_addr==0
|| !local_ip || local_ip->s_addr==0
|| !netmask || netmask->s_addr==0 ) return -EINVAL;
loc_ip.addr = local_ip->s_addr;
mask.addr = netmask->s_addr;
gw.addr = gateway->s_addr;
}
hbba = bba_create(&g_hNetIF);
pnet = netif_add(&g_hNetIF,&loc_ip, &mask, &gw, hbba, bba_init, net_input);
if(pnet) {
netif_set_up(pnet);
netif_set_default(pnet);
#if (LWIP_DHCP)
if(use_dhcp==TRUE) {
//setup coarse timer
tb.tv_sec = DHCP_COARSE_TIMER_SECS;
tb.tv_nsec = 0;
net_dhcpcoarse_ticks = __lwp_wd_calc_ticks(&tb);
__lwp_wd_initialize(&dhcp_coarsetimer_cntrl, __dhcpcoarse_timer, DHCPCOARSE_TIMER_ID, NULL);
__lwp_wd_insert_ticks(&dhcp_coarsetimer_cntrl, net_dhcpcoarse_ticks);
//setup fine timer
tb.tv_sec = 0;
tb.tv_nsec = DHCP_FINE_TIMER_MSECS * TB_NSPERMS;
net_dhcpfine_ticks = __lwp_wd_calc_ticks(&tb);
__lwp_wd_initialize(&dhcp_finetimer_cntrl, __dhcpfine_timer, DHCPFINE_TIMER_ID, NULL);
__lwp_wd_insert_ticks(&dhcp_finetimer_cntrl, net_dhcpfine_ticks);
//now start dhcp client
dhcp_start(pnet);
}
#endif
} else
return -ENXIO;
// setup loopinterface
IP4_ADDR(&loc_ip, 127, 0, 0, 1);
IP4_ADDR(&mask, 255, 0, 0, 0);
IP4_ADDR(&gw, 127, 0, 0, 1);
pnet = netif_add(&g_hLoopIF, &loc_ip, &mask, &gw, NULL, loopif_init, net_input);
//last and least start the tcpip layer
ret = net_init();
if ( ret == 0 && use_dhcp == TRUE ) {
int retries = max_retries;
// wait for dhcp to bind
while ( g_hNetIF.dhcp->state != DHCP_BOUND && retries > 0 ) {
retries--;
usleep(500000);
}
if ( retries > 0 ) {
//copy back network addresses
if ( local_ip != NULL ) local_ip->s_addr = g_hNetIF.ip_addr.addr;
if ( gateway != NULL ) gateway->s_addr = g_hNetIF.gw.addr;
if ( netmask != NULL ) netmask->s_addr = g_hNetIF.netmask.addr;
} else {
ret = -ETIMEDOUT;
}
}
return ret;
}
s32 if_config(char *local_ip, char *netmask, char *gateway,bool use_dhcp, int max_retries)
{
s32 ret = 0;
struct in_addr loc_ip, mask, gw;
loc_ip.s_addr = 0;
mask.s_addr = 0;
gw.s_addr = 0;
if ( local_ip != NULL ) loc_ip.s_addr = inet_addr(local_ip);
if ( netmask != NULL ) mask.s_addr = inet_addr(netmask);
if ( gateway != NULL ) gw.s_addr = inet_addr(gateway);
ret = if_configex( &loc_ip, &mask, &gw, use_dhcp, max_retries);
if (ret<0) return ret;
if ( use_dhcp == TRUE ) {
//copy back network addresses
if ( local_ip != NULL ) strcpy(local_ip, inet_ntoa( loc_ip ));
if ( netmask != NULL ) strcpy(netmask, inet_ntoa( mask));
if ( gateway != NULL ) strcpy(gateway, inet_ntoa( gw ));
}
return ret;
}
s32 net_init()
{
sys_sem sem;
if(tcpiplayer_inited) return 1;
if(LWP_SemInit(&netsocket_sem,1,1)==-1) return -1;
if(LWP_SemInit(&sockselect_sem,1,1)==-1) {
LWP_SemDestroy(netsocket_sem);
return -1;
}
if(LWP_SemInit(&sem,0,1)==-1) {
LWP_SemDestroy(netsocket_sem);
LWP_SemDestroy(sockselect_sem);
return -1;
}
if(LWP_CreateThread(&hnet_thread,net_thread,(void*)sem,netthread_stack,STACKSIZE,220)==-1) {
LWP_SemDestroy(netsocket_sem);
LWP_SemDestroy(sockselect_sem);
LWP_SemDestroy(sem);
return -1;
}
LWP_SemWait(sem);
LWP_SemDestroy(sem);
tcpiplayer_inited = 1;
return 0;
}
s32 net_shutdown(s32 s,u32 how)
{
return -1;
}
s32 net_fcntl(s32 s, u32 cmd, u32 flags)
{
return -1;
}
s32 net_socket(u32 domain,u32 type,u32 protocol)
{
s32 i;
struct netconn *conn;
switch(type) {
case SOCK_RAW:
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_socket(SOCK_RAW)\n"));
conn = netconn_new_with_proto_and_callback(NETCONN_RAW,protocol,evt_callback);
break;
case SOCK_DGRAM:
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_socket(SOCK_DGRAM)\n"));
conn = netconn_new_with_callback(NETCONN_UDP,evt_callback);
break;
case SOCK_STREAM:
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_socket(SOCK_STREAM)\n"));
conn = netconn_new_with_callback(NETCONN_TCP,evt_callback);
break;
default:
return -1;
}
if(!conn) return -1;
i = alloc_socket(conn);
if(i==-1) {
netconn_delete(conn);
return -1;
}
conn->socket = i;
return i;
}
s32 net_accept(s32 s,struct sockaddr *addr,socklen_t *addrlen)
{
struct netsocket *sock;
struct netconn *newconn;
struct ip_addr naddr = {0};
u16 port = 0;
s32 newsock;
struct sockaddr_in sin;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_accept(%d)\n", s));
sock = get_socket(s);
if(!sock) return -ENOTSOCK;
newconn = netconn_accept(sock->conn);
netconn_peer(newconn,&naddr,&port);
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = naddr.addr;
if(*addrlen>sizeof(sin))
*addrlen = sizeof(sin);
memcpy(addr,&sin,*addrlen);
newsock = alloc_socket(newconn);
if(newsock==-1) {
netconn_delete(newconn);
return -1;
}
newconn->callback = evt_callback;
sock = get_socket(newsock);
LWP_SemWait(netsocket_sem);
sock->rcvevt += -1 - newconn->socket;
newconn->socket = newsock;
LWP_SemPost(netsocket_sem);
return newsock;
}
s32 net_bind(s32 s,struct sockaddr *name,socklen_t namelen)
{
struct netsocket *sock;
struct ip_addr loc_addr;
u16 loc_port;
err_t err;
sock = get_socket(s);
if(!sock) return -ENOTSOCK;
loc_addr.addr = ((struct sockaddr_in*)name)->sin_addr.s_addr;
loc_port = ((struct sockaddr_in*)name)->sin_port;
err = netconn_bind(sock->conn,&loc_addr,ntohs(loc_port));
if(err!=ERR_OK) return -1;
return 0;
}
s32 net_listen(s32 s,u32 backlog)
{
struct netsocket *sock;
err_t err;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_listen(%d, backlog=%d)\n", s, backlog));
sock = get_socket(s);
if(!sock) return -ENOTSOCK;
err = netconn_listen(sock->conn);
if(err!=ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_listen(%d) failed, err=%d\n", s, err));
return -1;
}
return 0;
}
s32 net_recvfrom(s32 s,void *mem,s32 len,u32 flags,struct sockaddr *from,socklen_t *fromlen)
{
struct netsocket *sock;
struct netbuf *buf;
u16 buflen,copylen;
struct ip_addr *addr;
u16 port;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_recvfrom(%d, %p, %d, 0x%x, ..)\n", s, mem, len, flags));
if(mem==NULL || len<=0) return -EINVAL;
sock = get_socket(s);
if(!sock) return -ENOTSOCK;
if(sock->lastdata)
buf = sock->lastdata;
else {
if(((flags&MSG_DONTWAIT) || (sock->flags&O_NONBLOCK)) && !sock->rcvevt) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_recvfrom(%d): returning EWOULDBLOCK\n", s));
return -EAGAIN;
}
buf = netconn_recv(sock->conn);
if(!buf) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_recvfrom(%d): buf == NULL!\n", s));
return 0;
}
}
buflen = netbuf_len(buf);
buflen -= sock->lastoffset;
if(buflen<=0)
return 0;
if(len>buflen)
copylen = buflen;
else
copylen = len;
netbuf_copypartial(buf,mem,copylen,sock->lastoffset);
if(from && fromlen) {
struct sockaddr_in sin;
addr = netbuf_fromaddr(buf);
port = netbuf_fromport(buf);
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = addr->addr;
if(*fromlen>sizeof(sin))
*fromlen = sizeof(sin);
memcpy(from,&sin,*fromlen);
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_recvfrom(%d): addr=", s));
ip_addr_debug_print(SOCKETS_DEBUG, addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, copylen));
}
if(netconn_type(sock->conn)==NETCONN_TCP && (buflen-copylen)>0) {
sock->lastdata = buf;
sock->lastoffset += copylen;
} else {
sock->lastdata = NULL;
sock->lastoffset = 0;
netbuf_delete(buf);
}
return copylen;
}
s32 net_read(s32 s,void *mem,s32 len)
{
return net_recvfrom(s,mem,len,0,NULL,NULL);
}
s32 net_recv(s32 s,void *mem,s32 len,u32 flags)
{
return net_recvfrom(s,mem,len,flags,NULL,NULL);
}
s32 net_sendto(s32 s,const void *data,s32 len,u32 flags,struct sockaddr *to,socklen_t tolen)
{
struct netsocket *sock;
struct ip_addr remote_addr, addr;
u16_t remote_port, port = 0;
s32 ret,connected;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_sendto(%d, data=%p, size=%d, flags=0x%x)\n", s, data, len, flags));
if(data==NULL || len<=0) return -EINVAL;
sock = get_socket(s);
if (!sock) return -ENOTSOCK;
/* get the peer if currently connected */
connected = (netconn_peer(sock->conn, &addr, &port) == ERR_OK);
remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr;
remote_port = ((struct sockaddr_in *)to)->sin_port;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_sendto(%d, data=%p, size=%d, flags=0x%x to=", s, data, len, flags));
ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", ntohs(remote_port)));
netconn_connect(sock->conn, &remote_addr, ntohs(remote_port));
ret = net_send(s, data, len, flags);
/* reset the remote address and port number
of the connection */
if (connected)
netconn_connect(sock->conn, &addr, port);
else
netconn_disconnect(sock->conn);
return ret;
}
s32 net_send(s32 s,const void *data,s32 len,u32 flags)
{
struct netsocket *sock;
struct netbuf *buf;
err_t err;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_send(%d, data=%p, size=%d, flags=0x%x)\n", s, data, len, flags));
if(data==NULL || len<=0) return -EINVAL;
sock = get_socket(s);
if(!sock) return -ENOTSOCK;
switch(netconn_type(sock->conn)) {
case NETCONN_RAW:
case NETCONN_UDP:
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
buf = netbuf_new();
if(!buf) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_send(%d) ENOBUFS\n", s));
return -ENOBUFS;
}
netbuf_ref(buf,data,len);
err = netconn_send(sock->conn,buf);
netbuf_delete(buf);
break;
case NETCONN_TCP:
err = netconn_write(sock->conn,data,len,NETCONN_COPY);
break;
default:
err = ERR_ARG;
break;
}
if(err!=ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_send(%d) err=%d\n", s, err));
return -1;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_send(%d) ok size=%d\n", s, len));
return len;
}
s32 net_write(s32 s,const void *data,s32 size)
{
return net_send(s,data,size,0);
}
s32 net_connect(s32 s,struct sockaddr *name,socklen_t namelen)
{
struct netsocket *sock;
err_t err;
sock = get_socket(s);
if(!sock) return -ENOTSOCK;
if(((struct sockaddr_in*)name)->sin_family==AF_UNSPEC) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_connect(%d, AF_UNSPEC)\n", s));
err = netconn_disconnect(sock->conn);
} else {
struct ip_addr remote_addr;
u16 remote_port;
remote_addr.addr = ((struct sockaddr_in*)name)->sin_addr.s_addr;
remote_port = ((struct sockaddr_in*)name)->sin_port;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_connect(%d, addr=", s));
ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\n", ntohs(remote_port)));
err = netconn_connect(sock->conn,&remote_addr,ntohs(remote_port));
}
if(err!=ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_connect(%d) failed, err=%d\n", s, err));
return -1;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_connect(%d) succeeded\n", s));
return -EISCONN;
}
s32 net_close(s32 s)
{
struct netsocket *sock;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_close(%d)\n", s));
LWP_SemWait(netsocket_sem);
sock = get_socket(s);
if(!sock) {
LWP_SemPost(netsocket_sem);
return -ENOTSOCK;
}
netconn_delete(sock->conn);
if(sock->lastdata) netbuf_delete(sock->lastdata);
sock->lastdata = NULL;
sock->lastoffset = 0;
sock->conn = NULL;
LWP_SemPost(netsocket_sem);
return 0;
}
static s32 net_selscan(s32 maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset)
{
s32 i,nready = 0;
fd_set lreadset,lwriteset,lexceptset;
struct netsocket *sock;
FD_ZERO(&lreadset);
FD_ZERO(&lwriteset);
FD_ZERO(&lexceptset);
for(i=0;i<maxfdp1;i++) {
if(FD_ISSET(i,readset)) {
sock = get_socket(i);
if(sock && (sock->lastdata || sock->rcvevt)) {
FD_SET(i,&lreadset);
nready++;
}
}
if(FD_ISSET(i,writeset)) {
sock = get_socket(i);
if(sock && sock->sendevt) {
FD_SET(i,&lwriteset);
nready++;
}
}
}
*readset = lreadset;
*writeset = lwriteset;
FD_ZERO(exceptset);
return nready;
}
s32 net_select(s32 maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout)
{
s32 i,nready;
fd_set lreadset,lwriteset,lexceptset;
struct timespec tb,*p_tb;
struct netselect_cb sel_cb;
struct netselect_cb *psel_cb;
sel_cb.next = NULL;
sel_cb.readset = readset;
sel_cb.writeset = writeset;
sel_cb.exceptset = exceptset;
sel_cb.signaled = 0;
LWP_SemWait(sockselect_sem);
if(readset)
lreadset = *readset;
else
FD_ZERO(&lreadset);
if(writeset)
lwriteset = *writeset;
else
FD_ZERO(&lwriteset);
if(exceptset)
lexceptset = *exceptset;
else
FD_ZERO(&lexceptset);
nready = net_selscan(maxfdp1,&lreadset,&lwriteset,&lexceptset);
if(!nready) {
if(timeout && timeout->tv_sec==0 && timeout->tv_usec==0) {
LWP_SemPost(sockselect_sem);
if(readset)
FD_ZERO(readset);
if(writeset)
FD_ZERO(writeset);
if(exceptset)
FD_ZERO(exceptset);
return 0;
}
LWP_MutexInit(&sel_cb.cond_lck,FALSE);
LWP_CondInit(&sel_cb.cond);
sel_cb.next = selectcb_list;
selectcb_list = &sel_cb;
LWP_SemPost(sockselect_sem);
if(timeout==NULL)
p_tb = NULL;
else {
tb.tv_sec = timeout->tv_sec;
tb.tv_nsec = (timeout->tv_usec+500)*TB_NSPERUS;
p_tb = &tb;
}
LWP_MutexLock(sel_cb.cond_lck);
i = LWP_CondTimedWait(sel_cb.cond,sel_cb.cond_lck,p_tb);
LWP_MutexUnlock(sel_cb.cond_lck);
LWP_SemWait(sockselect_sem);
if(selectcb_list==&sel_cb)
selectcb_list = sel_cb.next;
else {
for(psel_cb = selectcb_list;psel_cb;psel_cb = psel_cb->next) {
if(psel_cb->next==&sel_cb) {
psel_cb->next = sel_cb.next;
break;
}
}
}
LWP_CondDestroy(sel_cb.cond);
LWP_MutexDestroy(sel_cb.cond_lck);
LWP_SemPost(sockselect_sem);
if(i==ETIMEDOUT) {
if(readset)
FD_ZERO(readset);
if(writeset)
FD_ZERO(writeset);
if(exceptset)
FD_ZERO(exceptset);
return 0;
}
if(readset)
lreadset = *readset;
else
FD_ZERO(&lreadset);
if(writeset)
lwriteset = *writeset;
else
FD_ZERO(&lwriteset);
if(exceptset)
lexceptset = *exceptset;
else
FD_ZERO(&lexceptset);
nready = net_selscan(maxfdp1,&lreadset,&lwriteset,&lexceptset);
} else
LWP_SemPost(sockselect_sem);
if(readset)
*readset = lreadset;
if(writeset)
*writeset = lwriteset;
if(exceptset)
*exceptset = lexceptset;
return nready;
}
s32 net_getsockopt(s32 s, u32 level, u32 optname, void *optval, socklen_t *optlen)
{
/* TODO/FIXME: Implement getsockopt */
memset(optval, 0, *optlen);
return 0;
}
s32 net_setsockopt(s32 s,u32 level,u32 optname,const void *optval,socklen_t optlen)
{
s32 err = 0;
struct netsocket *sock;
sock = get_socket(s);
if(sock==NULL) return -ENOTSOCK;
if(optval==NULL) return -EINVAL;
switch(level) {
case SOL_SOCKET:
{
switch(optname) {
case SO_BROADCAST:
case SO_KEEPALIVE:
case SO_REUSEADDR:
case SO_REUSEPORT:
if(optlen<sizeof(u32)) err = EINVAL;
break;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", s, optname));
err = ENOPROTOOPT;
}
}
break;
case IPPROTO_IP:
{
switch(optname) {
case IP_TTL:
case IP_TOS:
if(optlen<sizeof(u32)) err = EINVAL;
break;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", s, optname));
err = ENOPROTOOPT;
}
}
break;
case IPPROTO_TCP:
{
if(optlen<sizeof(u32)) {
err = EINVAL;
break;
}
if(sock->conn->type!=NETCONN_TCP) return 0;
switch(optname) {
case TCP_NODELAY:
case TCP_KEEPALIVE:
break;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname));
err = ENOPROTOOPT;
}
}
break;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", s, level, optname));
err = ENOPROTOOPT;
}
if(err!=0) return -1;
switch(level) {
case SOL_SOCKET:
{
switch(optname) {
case SO_BROADCAST:
case SO_KEEPALIVE:
case SO_REUSEADDR:
case SO_REUSEPORT:
if(*(u32*)optval)
sock->conn->pcb.tcp->so_options |= optname;
else
sock->conn->pcb.tcp->so_options &= ~optname;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", s, optname, (*(u32*)optval?"on":"off")));
break;
}
}
break;
case IPPROTO_IP:
{
switch(optname) {
case IP_TTL:
sock->conn->pcb.tcp->ttl = (u8)(*(u32*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %u\n", s, sock->conn->pcb.tcp->ttl));
break;
case IP_TOS:
sock->conn->pcb.tcp->tos = (u8)(*(u32*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %u\n", s, sock->conn->pcb.tcp->tos));
break;
}
}
break;
case IPPROTO_TCP:
{
switch(optname) {
case TCP_NODELAY:
if(*(u32*)optval)
sock->conn->pcb.tcp->flags |= TF_NODELAY;
else
sock->conn->pcb.tcp->flags &= ~TF_NODELAY;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", s, (*(u32*)optval)?"on":"off") );
break;
case TCP_KEEPALIVE:
sock->conn->pcb.tcp->keepalive = (u32)(*(u32*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %u\n", s, sock->conn->pcb.tcp->keepalive));
break;
}
}
}
return err?-1:0;
}
s32 net_ioctl(s32 s, u32 cmd, void *argp)
{
struct netsocket *sock = get_socket(s);
if(!sock) return -ENOTSOCK;
switch (cmd) {
case FIONREAD:
if(!argp) return -EINVAL;
*((u16_t*)argp) = sock->conn->recvavail;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_ioctl(%d, FIONREAD, %p) = %u\n", s, argp, *((u16*)argp)));
return 0;
case FIONBIO:
if(argp && *(u32*)argp)
sock->flags |= O_NONBLOCK;
else
sock->flags &= ~O_NONBLOCK;
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_ioctl(%d, FIONBIO, %d)\n", s, !!(sock->flags&O_NONBLOCK)));
return 0;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("net_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
return -EINVAL;
}
}