#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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;leftnext) { if(offset!=0 && offset>=p->len) offset -= p->len; else { for(i=offset;ilen;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;iNUM_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;ilastdata || 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_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(optlenconn->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; } }