diff --git a/kernel/Makefile b/kernel/Makefile index f7280022..70009bff 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,4 +1,4 @@ -C_source = main.c tasks.c processor.c syscall.c tests.c +C_source = main.c tasks.c processor.c syscall.c tests.c echo.c ping.c OBJS += $(patsubst %.c, %.o, $(filter %.c, $(C_source))) diff --git a/kernel/echo.c b/kernel/echo.c new file mode 100644 index 00000000..6cab1dc6 --- /dev/null +++ b/kernel/echo.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of and a contribution to the lwIP TCP/IP stack. + * + * Credits go to Adam Dunkels (and the current maintainers) of this software. + * + * Christiaan Simons rewrote this file to get a more stable echo example. + */ + +/** + * @file + * TCP echo server example using raw API. + * + * Echos all bytes sent by connecting client, + * and passively closes when client is done. + * + */ + + +#include +#include +#if defined(CONFIG_LWIP) +#include +#include +#include + +static struct tcp_pcb *echo_pcb; + +enum echo_states +{ + ES_NONE = 0, + ES_ACCEPTED, + ES_RECEIVED, + ES_CLOSING +}; + +struct echo_state +{ + u8_t state; + u8_t retries; + struct tcp_pcb *pcb; + /* pbuf (chain) to recycle */ + struct pbuf *p; +}; + +err_t echo_accept(void *arg, struct tcp_pcb *newpcb, err_t err); +err_t echo_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); +void echo_error(void *arg, err_t err); +err_t echo_poll(void *arg, struct tcp_pcb *tpcb); +err_t echo_sent(void *arg, struct tcp_pcb *tpcb, u16_t len); +void echo_send(struct tcp_pcb *tpcb, struct echo_state *es); +void echo_close(struct tcp_pcb *tpcb, struct echo_state *es); + +void +echo_init(void) +{ + echo_pcb = tcp_new(); + if (echo_pcb != NULL) + { + err_t err; + + err = tcp_bind(echo_pcb, IP_ADDR_ANY, 7); + if (err == ERR_OK) + { + echo_pcb = tcp_listen(echo_pcb); + tcp_accept(echo_pcb, echo_accept); + } + else + { + /* abort? output diagnostic? */ + kputs("echo: tcp_bind failed!\n"); + } + } + else + { + /* abort? output diagnostic? */ + kputs("echo: tcp_new failed!\n"); + } +} + + +err_t +echo_accept(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + err_t ret_err; + struct echo_state *es; + + /* commonly observed practive to call tcp_setprio(), why? */ + tcp_setprio(newpcb, TCP_PRIO_MIN); + + es = mem_malloc(sizeof(struct echo_state)); + if (es != NULL) + { + es->state = ES_ACCEPTED; + es->pcb = newpcb; + es->retries = 0; + es->p = NULL; + /* pass newly allocated es to our callbacks */ + tcp_arg(newpcb, es); + tcp_recv(newpcb, echo_recv); + tcp_err(newpcb, echo_error); + tcp_poll(newpcb, echo_poll, 0); + ret_err = ERR_OK; + } + else + { + ret_err = ERR_MEM; + } + return ret_err; +} + +err_t +echo_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + struct echo_state *es; + err_t ret_err; + + LWIP_ASSERT("arg != NULL",arg != NULL); + es = arg; + if (p == NULL) + { + /* remote host closed connection */ + es->state = ES_CLOSING; + if(es->p == NULL) + { + /* we're done sending, close it */ + echo_close(tpcb, es); + } + else + { + /* we're not done yet */ + tcp_sent(tpcb, echo_sent); + echo_send(tpcb, es); + } + ret_err = ERR_OK; + } + else if(err != ERR_OK) + { + /* cleanup, for unkown reason */ + if (p != NULL) + { + es->p = NULL; + pbuf_free(p); + } + ret_err = err; + } + else if(es->state == ES_ACCEPTED) + { + /* first data chunk in p->payload */ + es->state = ES_RECEIVED; + /* store reference to incoming pbuf (chain) */ + es->p = p; + /* install send completion notifier */ + tcp_sent(tpcb, echo_sent); + echo_send(tpcb, es); + ret_err = ERR_OK; + } + else if (es->state == ES_RECEIVED) + { + /* read some more data */ + if(es->p == NULL) + { + es->p = p; + tcp_sent(tpcb, echo_sent); + echo_send(tpcb, es); + } + else + { + struct pbuf *ptr; + + /* chain pbufs to the end of what we recv'ed previously */ + ptr = es->p; + pbuf_chain(ptr,p); + } + ret_err = ERR_OK; + } + else if(es->state == ES_CLOSING) + { + /* odd case, remote side closing twice, trash data */ + tcp_recved(tpcb, p->tot_len); + es->p = NULL; + pbuf_free(p); + ret_err = ERR_OK; + } + else + { + /* unkown es->state, trash data */ + tcp_recved(tpcb, p->tot_len); + es->p = NULL; + pbuf_free(p); + ret_err = ERR_OK; + } + return ret_err; +} + +void +echo_error(void *arg, err_t err) +{ + struct echo_state *es; + + es = arg; + if (es != NULL) + { + mem_free(es); + } +} + +err_t +echo_poll(void *arg, struct tcp_pcb *tpcb) +{ + err_t ret_err; + struct echo_state *es; + + es = arg; + if (es != NULL) + { + if (es->p != NULL) + { + /* there is a remaining pbuf (chain) */ + tcp_sent(tpcb, echo_sent); + echo_send(tpcb, es); + } + else + { + /* no remaining pbuf (chain) */ + if(es->state == ES_CLOSING) + { + echo_close(tpcb, es); + } + } + ret_err = ERR_OK; + } + else + { + /* nothing to be done */ + tcp_abort(tpcb); + ret_err = ERR_ABRT; + } + return ret_err; +} + +err_t +echo_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + struct echo_state *es; + + es = arg; + es->retries = 0; + + if(es->p != NULL) + { + /* still got pbufs to send */ + tcp_sent(tpcb, echo_sent); + echo_send(tpcb, es); + } + else + { + /* no more pbufs to send */ + if(es->state == ES_CLOSING) + { + echo_close(tpcb, es); + } + } + return ERR_OK; +} + +void +echo_send(struct tcp_pcb *tpcb, struct echo_state *es) +{ + struct pbuf *ptr; + err_t wr_err = ERR_OK; + + while ((wr_err == ERR_OK) && + (es->p != NULL) && + (es->p->len <= tcp_sndbuf(tpcb))) + { + ptr = es->p; + + /* enqueue data for transmission */ + wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1); + if (wr_err == ERR_OK) + { + u16_t plen; + u8_t freed; + + plen = ptr->len; + /* continue with next pbuf in chain (if any) */ + es->p = ptr->next; + if(es->p != NULL) + { + /* new reference! */ + pbuf_ref(es->p); + } + /* chop first pbuf from chain */ + do + { + /* try hard to free pbuf */ + freed = pbuf_free(ptr); + } + while(freed == 0); + /* we can read more data now */ + tcp_recved(tpcb, plen); + } + else if(wr_err == ERR_MEM) + { + /* we are low on memory, try later / harder, defer to poll */ + es->p = ptr; + } + else + { + /* other problem ?? */ + } + } +} + +void +echo_close(struct tcp_pcb *tpcb, struct echo_state *es) +{ + tcp_arg(tpcb, NULL); + tcp_sent(tpcb, NULL); + tcp_recv(tpcb, NULL); + tcp_err(tpcb, NULL); + tcp_poll(tpcb, NULL, 0); + + if (es != NULL) + { + mem_free(es); + } + tcp_close(tpcb); +} + +#endif diff --git a/kernel/main.c b/kernel/main.c index 880899e7..e9367aeb 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #endif #ifdef CONFIG_LWIP +#include #include #include #include @@ -40,9 +42,7 @@ #include #include #endif -#ifdef CONFIG_ROCKCREEK -#include -#endif +#include extern int test_init(void); @@ -89,58 +89,69 @@ static void list_root(void) { list_fs(fs_root, 1); } -#if defined(CONFIG_LWIP) && defined(CONFIG_ROCKCREEK) -err_t sccpcif_init(struct netif* netif); +#if defined(CONFIG_LWIP) && defined(CONFIG_PCI) +void echo_init(void); +void ping_init(void); -int STDCALL scc_pc_task(void* arg) +int STDCALL rtl8139if_task(void* arg) { struct netif netif; struct ip_addr ipaddr; struct ip_addr netmask; struct ip_addr gw; + uint32_t mscnt = 0; - sleep(10); - kputs("LWIP task is started\n"); + kputs("Network task is started\n"); /* Set network address variables */ - IP4_ADDR(&gw, 192,168,1,254); - IP4_ADDR(&ipaddr, 192,168,1,scc_info.pid+1); - IP4_ADDR(&netmask, 255,255,255,0); + //IP4_ADDR(&gw, 192,168,1,254); + //IP4_ADDR(&ipaddr, 192,168,1,100); + //IP4_ADDR(&netmask, 255,255,255,0); + /* Clear network address because we use DHCP to get an ip address */ + IP4_ADDR(&gw, 0,0,0,0); + IP4_ADDR(&ipaddr, 0,0,0,0); + IP4_ADDR(&netmask, 0,0,0,0); - stats_init(); /* Clears the structure where runtime statistics are gathered */ - sys_init(); - mem_init(); /* Initializes the dynamic memory heap defined by MEM_SIZE. */ - memp_init(); /* Initializes the memory pools defined by MEMP_NUM_x */ - pbuf_init(); /* Initializes the pbuf memory pool defined by PBUF_POOL_SIZE. */ - etharp_init(); /* Initializes the ARP table and queue. */ - ip_init(); - udp_init(); /* Clears the UDP PCB list */ - tcp_init(); /* Clears the TCP PCB list and clears some internal TCP timers. */ + /* Clear the IP address, Subnet Mask, and Gateway */ + ipaddr.addr = 0; + netmask.addr = 0; + gw.addr = 0; /* Bring up the network interface */ - if (!netif_add(&netif, &ipaddr, &netmask, &gw, NULL, sccpcif_init, ip_input)) { + if (!netif_add(&netif, &ipaddr, &netmask, &gw, NULL, rtl8139if_init, ethernet_input)) { kputs("Unable to add network interface\n"); - return -1; + return -ENXIO; } netif_set_default(&netif); if (netif_is_up(&netif)) { kputs("Network interface is not up\n"); - return -1; + return -EIO; } + kprintf("Starting DHCPCD...\n"); + dhcp_start(&netif); + + // start echo server + echo_init(); + ping_init(); + while(1) { + rtl8139if_poll(&netif); } + dhcp_release(&netif); + dhcp_stop(&netif); + return 0; } #endif int main(void) { -#ifdef CONFIG_LWIP - tid_t pc_id; +#if defined(CONFIG_LWIP) && defined(CONFIG_PCI) + tid_t rtl8139_id; #endif kprintf("This is MetalSVM %s Build %u, %u, %u\n", METALSVM_VERSION, &__BUILD_VERSION, &__BUILD_DATE, &__BUILD_TIME); @@ -161,21 +172,21 @@ int main(void) kprintf("Kernel starts at %p and ends at %p\n", &kernel_start, &kernel_end); system_calibration(); +#ifdef CONFIG_LWIP + lwip_init(); +#endif kprintf("Processor frequency: %u MHz\n", get_cpu_frequency()); kprintf("Total memory: %u MBytes\n", atomic_int32_read(&total_pages)/((1024*1024)/PAGE_SIZE)); kprintf("Current allocated memory: %u KBytes\n", atomic_int32_read(&total_allocated_pages)*(PAGE_SIZE/1024)); kprintf("Current available memory: %u MBytes\n", atomic_int32_read(&total_available_pages)/((1024*1024)/PAGE_SIZE)); -#ifdef CONFIG_PCI - print_pci_adapters(); -#endif sleep(5); list_root(); - test_init(); + //test_init(); -#ifdef CONFIG_LWIP - create_kernel_task(&pc_id, scc_pc_task, NULL); +#if defined(CONFIG_LWIP) && defined(CONFIG_PCI) + create_kernel_task(&rtl8139_id, rtl8139if_task, NULL); #endif per_core(current_task)->status = TASK_IDLE; diff --git a/kernel/ping.c b/kernel/ping.c new file mode 100644 index 00000000..180b4420 --- /dev/null +++ b/kernel/ping.c @@ -0,0 +1,323 @@ +/** + * @file + * Ping sender module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +/** + * This is an example of a "ping" sender (with raw API and socket API). + * It can be used as a start point to maintain opened a network connection, or + * like a network "watchdog" for your device. + * + */ + +#include +#include +#include +#include + +#if LWIP_RAW && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * PING_DEBUG: Enable debugging for PING. + */ +#ifndef PING_DEBUG +#define PING_DEBUG LWIP_DBG_ON +#endif + +/** ping target - should be a "struct ip_addr" */ +#ifndef PING_TARGET +#define PING_TARGET (netif_default?netif_default->gw:ip_addr_any) +#endif + +/** ping receive timeout - in milliseconds */ +#ifndef PING_RCV_TIMEO +#define PING_RCV_TIMEO 1000 +#endif + +/** ping delay - in milliseconds */ +#ifndef PING_DELAY +#define PING_DELAY 1000 +#endif + +/** ping identifier - must fit on a u16_t */ +#ifndef PING_ID +#define PING_ID 0xAFAF +#endif + +/** ping additional data size to include in the packet */ +#ifndef PING_DATA_SIZE +#define PING_DATA_SIZE 32 +#endif + +/** ping result action - no default action */ +#ifndef PING_RESULT +#define PING_RESULT(ping_ok) +#endif + +/* ping variables */ +static u16_t ping_seq_num; +static u32_t ping_time; + +#if NO_SYS +/* port-defined functions used for timer execution */ +//void sys_msleep(u32_t ms); + +static u32_t sys_now() +{ + static uint64_t start = 0; + uint64_t end; + uint64_t tmp; + + if (start == 0) + start = rdtsc(); + + end = rdtsc(); + + tmp = (end > start) ? end - start : start - end; + + return (u32_t) ((tmp * 1000) / get_cpu_frequency()); +} +#endif /* NO_SYS */ + +/** Prepare a echo ICMP request */ +static void +ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len) +{ + int i; + + ICMPH_TYPE_SET(iecho,ICMP_ECHO); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + iecho->id = PING_ID; + iecho->seqno = htons(++ping_seq_num); + iecho->chksum = inet_chksum(iecho, len); + + /* fill the additional data buffer with some data */ + for(i = 0; i < PING_DATA_SIZE; i++) { + ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = i; + } +} + +#if LWIP_SOCKET + +/* Ping using the socket ip */ +static err_t +ping_send(int s, struct ip_addr *addr) +{ + int err; + struct icmp_echo_hdr *iecho; + struct sockaddr_in to; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + + if (!(iecho = mem_malloc(ping_size))) { + return ERR_MEM; + } + + ping_prepare_echo(iecho, ping_size); + + to.sin_len = sizeof(to); + to.sin_family = AF_INET; + to.sin_addr.s_addr = addr->addr; + + err = lwip_sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to)); + + mem_free(iecho); + + return (err ? ERR_OK : ERR_VAL); +} + +static void +ping_recv(int s) +{ + char buf[64]; + int fromlen, len; + struct sockaddr_in from; + struct ip_hdr *iphdr; + struct icmp_echo_hdr *iecho; + + while((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) { + if (len >= (sizeof(struct ip_hdr)+sizeof(struct icmp_echo_hdr))) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv ")); + ip_addr_debug_print(PING_DEBUG, (struct ip_addr *)&(from.sin_addr)); + LWIP_DEBUGF( PING_DEBUG, (" %lu ms\n", (sys_now()-ping_time))); + + iphdr = (struct ip_hdr *)buf; + iecho = (struct icmp_echo_hdr *)(buf+(IPH_HL(iphdr) * 4)); + if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) { + /* do some ping result processing */ + PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER)); + return; + } else { + LWIP_DEBUGF( PING_DEBUG, ("ping: drop\n")); + } + } + } + + if (len == 0) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv - %lu ms - timeout\n", (sys_now()-ping_time))); + } + + /* do some ping result processing */ + PING_RESULT(0); +} + +static void +ping_thread(void *arg) +{ + int s; + int timeout = PING_RCV_TIMEO; + struct ip_addr ping_target; + + LWIP_UNUSED_ARG(arg); + + if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) { + return; + } + + lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + + while (1) { + ping_target = PING_TARGET; + + if (ping_send(s, &ping_target) == ERR_OK) { + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, &ping_target); + LWIP_DEBUGF( PING_DEBUG, ("\n")); + + ping_time = sys_now(); + ping_recv(s); + } else { + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, &ping_target); + LWIP_DEBUGF( PING_DEBUG, (" - error\n")); + } + sys_msleep(PING_DELAY); + } +} + +#else /* LWIP_SOCKET */ + +/* Ping using the raw ip */ +static u8_t +ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *addr) +{ + struct icmp_echo_hdr *iecho; + + if (pbuf_header( p, -PBUF_IP_HLEN)==0) { + iecho = p->payload; + + if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, (" %lu ms\n", (sys_now()-ping_time))); + + /* do some ping result processing */ + PING_RESULT(1); + } + } + + return 1; /* eat the event */ +} + +static void +ping_send(struct raw_pcb *raw, struct ip_addr *addr) +{ + struct pbuf *p; + struct icmp_echo_hdr *iecho; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + + if (!(p = pbuf_alloc(PBUF_IP, ping_size, PBUF_RAM))) { + return; + } + if ((p->len == p->tot_len) && (p->next == NULL)) { + iecho = p->payload; + + ping_prepare_echo(iecho, ping_size); + + raw_sendto(raw, p, addr); + ping_time = sys_now(); + } + pbuf_free(p); +} + +static void +ping_timeout(void *arg) +{ + struct raw_pcb *pcb = (struct raw_pcb*)arg; + struct ip_addr ping_target = PING_TARGET; + + LWIP_ASSERT("ping_timeout: no pcb given!", pcb != NULL); + + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, &ping_target); + LWIP_DEBUGF( PING_DEBUG, ("\n")); + + ping_send(pcb, &ping_target); + + sys_timeout(PING_DELAY, ping_timeout, pcb); +} + +static void +ping_raw_init(void) +{ + struct raw_pcb *pcb; + + if (!(pcb = raw_new(IP_PROTO_ICMP))) { + return; + } + + raw_recv(pcb, ping_recv, NULL); + raw_bind(pcb, IP_ADDR_ANY); + sys_timeout(PING_DELAY, ping_timeout, pcb); +} + +#endif /* LWIP_SOCKET */ + +void +ping_init(void) +{ +#if LWIP_SOCKET + sys_thread_new("ping_thread", ping_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); +#else /* LWIP_SOCKET */ + ping_raw_init(); +#endif /* LWIP_SOCKET */ +} + +#endif /* LWIP_RAW && LWIP_ICMP */