1 /*
2 * COPYRIGHT (C) 2006-2018, RT-Thread Development Team
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
25 * OF SUCH DAMAGE.
26 *
27 * Change Logs:
28 * Date Author Notes
29 * 2012-12-8 Bernard add file header
30 * export bsd socket symbol for RT-Thread Application Module
31 * 2017-11-15 Bernard add lock for init_done callback.
32 */
33
34 #include <rtthread.h>
35
36 #include "lwip/sys.h"
37 #include "lwip/opt.h"
38 #include "lwip/stats.h"
39 #include "lwip/err.h"
40 #include "arch/sys_arch.h"
41 #include "lwip/debug.h"
42 #include "lwip/netif.h"
43 #include "lwip/tcpip.h"
44 #include "netif/ethernetif.h"
45 #include "lwip/sio.h"
46 #include <lwip/init.h>
47 #include "lwip/inet.h"
48
49 #include <string.h>
50
51 /*
52 * Initialize the network interface device
53 *
54 * @return the operation status, ERR_OK on OK, ERR_IF on error
55 */
netif_device_init(struct netif * netif)56 static err_t netif_device_init(struct netif *netif)
57 {
58 struct eth_device *ethif;
59
60 ethif = (struct eth_device *)netif->state;
61 if (ethif != RT_NULL)
62 {
63 rt_device_t device;
64
65 /* get device object */
66 device = (rt_device_t) ethif;
67 if (rt_device_init(device) != RT_EOK)
68 {
69 return ERR_IF;
70 }
71
72 /* copy device flags to netif flags */
73 netif->flags = ethif->flags;
74
75 return ERR_OK;
76 }
77
78 return ERR_IF;
79 }
80 /*
81 * Initialize the ethernetif layer and set network interface device up
82 */
tcpip_init_done_callback(void * arg)83 static void tcpip_init_done_callback(void *arg)
84 {
85 rt_device_t device;
86 struct eth_device *ethif;
87 struct ip_addr ipaddr, netmask, gw;
88 struct rt_list_node* node;
89 struct rt_object* object;
90 struct rt_object_information *information;
91
92 LWIP_ASSERT("invalid arg.\n",arg);
93
94 IP4_ADDR(&gw, 0,0,0,0);
95 IP4_ADDR(&ipaddr, 0,0,0,0);
96 IP4_ADDR(&netmask, 0,0,0,0);
97
98 /* enter critical */
99 rt_enter_critical();
100
101 /* for each network interfaces */
102 information = rt_object_get_information(RT_Object_Class_Device);
103 RT_ASSERT(information != RT_NULL);
104 for (node = information->object_list.next;
105 node != &(information->object_list);
106 node = node->next)
107 {
108 object = rt_list_entry(node, struct rt_object, list);
109 device = (rt_device_t)object;
110 if (device->type == RT_Device_Class_NetIf)
111 {
112 ethif = (struct eth_device *)device;
113
114 /* leave critical */
115 rt_exit_critical();
116 LOCK_TCPIP_CORE();
117
118 netif_add(ethif->netif, &ipaddr, &netmask, &gw,
119 ethif, netif_device_init, tcpip_input);
120
121 if (netif_default == RT_NULL)
122 netif_set_default(ethif->netif);
123
124 #if LWIP_DHCP
125 if (ethif->flags & NETIF_FLAG_DHCP)
126 {
127 /* if this interface uses DHCP, start the DHCP client */
128 dhcp_start(ethif->netif);
129 }
130 else
131 #endif
132 {
133 /* set interface up */
134 netif_set_up(ethif->netif);
135 }
136
137 if (!(ethif->flags & ETHIF_LINK_PHYUP))
138 {
139 netif_set_link_up(ethif->netif);
140 }
141
142 UNLOCK_TCPIP_CORE();
143 /* enter critical */
144 rt_enter_critical();
145 }
146 }
147
148 /* leave critical */
149 rt_exit_critical();
150 rt_sem_release((rt_sem_t)arg);
151 }
152
153 /**
154 * LwIP system initialization
155 */
lwip_system_init(void)156 int lwip_system_init(void)
157 {
158 rt_err_t rc;
159 struct rt_semaphore done_sem;
160
161 /* set default netif to NULL */
162 netif_default = RT_NULL;
163
164 rc = rt_sem_init(&done_sem, "done", 0, RT_IPC_FLAG_FIFO);
165
166 if (rc != RT_EOK)
167 {
168 LWIP_ASSERT("Failed to create semaphore", 0);
169
170 return -1;
171 }
172
173 tcpip_init(tcpip_init_done_callback, (void *)&done_sem);
174
175 /* waiting for initialization done */
176 if (rt_sem_take(&done_sem, RT_WAITING_FOREVER) != RT_EOK)
177 {
178 rt_sem_detach(&done_sem);
179
180 return -1;
181 }
182 rt_sem_detach(&done_sem);
183
184 /* set default ip address */
185 #if !LWIP_DHCP
186 if (netif_default != RT_NULL)
187 {
188 struct ip_addr ipaddr, netmask, gw;
189
190 ipaddr.addr = inet_addr(RT_LWIP_IPADDR);
191 gw.addr = inet_addr(RT_LWIP_GWADDR);
192 netmask.addr = inet_addr(RT_LWIP_MSKADDR);
193
194 netifapi_netif_set_addr(netif_default, &ipaddr, &netmask, &gw);
195 }
196 #endif
197 rt_kprintf("lwIP-%d.%d.%d initialized!\n", LWIP_VERSION_MAJOR, LWIP_VERSION_MINOR, LWIP_VERSION_REVISION);
198
199 return 0;
200 }
201 INIT_COMPONENT_EXPORT(lwip_system_init);
202
sys_init(void)203 void sys_init(void)
204 {
205 /* nothing on RT-Thread porting */
206 }
207
lwip_sys_init(void)208 void lwip_sys_init(void)
209 {
210 lwip_system_init();
211 }
212
213 /*
214 * Create a new semaphore
215 *
216 * @return the operation status, ERR_OK on OK; others on error
217 */
sys_sem_new(sys_sem_t * sem,u8_t count)218 err_t sys_sem_new(sys_sem_t *sem, u8_t count)
219 {
220 static unsigned short counter = 0;
221 char tname[RT_NAME_MAX];
222 sys_sem_t tmpsem;
223
224 RT_DEBUG_NOT_IN_INTERRUPT;
225
226 rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_SEM_NAME, counter);
227 counter ++;
228
229 tmpsem = rt_sem_create(tname, count, RT_IPC_FLAG_FIFO);
230 if (tmpsem == RT_NULL)
231 return ERR_MEM;
232 else
233 {
234 *sem = tmpsem;
235
236 return ERR_OK;
237 }
238 }
239
240 /*
241 * Deallocate a semaphore
242 */
sys_sem_free(sys_sem_t * sem)243 void sys_sem_free(sys_sem_t *sem)
244 {
245 RT_DEBUG_NOT_IN_INTERRUPT;
246 rt_sem_delete(*sem);
247 }
248
249 /*
250 * Signal a semaphore
251 */
sys_sem_signal(sys_sem_t * sem)252 void sys_sem_signal(sys_sem_t *sem)
253 {
254 rt_sem_release(*sem);
255 }
256
257 /*
258 * Block the thread while waiting for the semaphore to be signaled
259 *
260 * @return If the timeout argument is non-zero, it will return the number of milliseconds
261 * spent waiting for the semaphore to be signaled; If the semaphore isn't signaled
262 * within the specified time, it will return SYS_ARCH_TIMEOUT; If the thread doesn't
263 * wait for the semaphore, it will return zero
264 */
sys_arch_sem_wait(sys_sem_t * sem,u32_t timeout)265 u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
266 {
267 rt_err_t ret;
268 s32_t t;
269 u32_t tick;
270
271 RT_DEBUG_NOT_IN_INTERRUPT;
272
273 /* get the begin tick */
274 tick = rt_tick_get();
275 if (timeout == 0)
276 t = RT_WAITING_FOREVER;
277 else
278 {
279 /* convert msecond to os tick */
280 if (timeout < (1000/RT_TICK_PER_SECOND))
281 t = 1;
282 else
283 t = timeout / (1000/RT_TICK_PER_SECOND);
284 }
285
286 ret = rt_sem_take(*sem, t);
287
288 if (ret == -RT_ETIMEOUT)
289 return SYS_ARCH_TIMEOUT;
290 else
291 {
292 if (ret == RT_EOK)
293 ret = 1;
294 }
295
296 /* get elapse msecond */
297 tick = rt_tick_get() - tick;
298
299 /* convert tick to msecond */
300 tick = tick * (1000 / RT_TICK_PER_SECOND);
301 if (tick == 0)
302 tick = 1;
303
304 return tick;
305 }
306
307 #ifndef sys_sem_valid
308 /** Check if a semaphore is valid/allocated:
309 * return 1 for valid, 0 for invalid
310 */
sys_sem_valid(sys_sem_t * sem)311 int sys_sem_valid(sys_sem_t *sem)
312 {
313 return (int)(*sem);
314 }
315 #endif
316
317 #ifndef sys_sem_set_invalid
318 /** Set a semaphore invalid so that sys_sem_valid returns 0
319 */
sys_sem_set_invalid(sys_sem_t * sem)320 void sys_sem_set_invalid(sys_sem_t *sem)
321 {
322 *sem = RT_NULL;
323 }
324 #endif
325
326 /* ====================== Mutex ====================== */
327
328 /** Create a new mutex
329 * @param mutex pointer to the mutex to create
330 * @return a new mutex
331 */
sys_mutex_new(sys_mutex_t * mutex)332 err_t sys_mutex_new(sys_mutex_t *mutex)
333 {
334 static unsigned short counter = 0;
335 char tname[RT_NAME_MAX];
336 sys_mutex_t tmpmutex;
337
338 RT_DEBUG_NOT_IN_INTERRUPT;
339
340 rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_MUTEX_NAME, counter);
341 counter ++;
342
343 tmpmutex = rt_mutex_create(tname, RT_IPC_FLAG_FIFO);
344 if (tmpmutex == RT_NULL)
345 return ERR_MEM;
346 else
347 {
348 *mutex = tmpmutex;
349
350 return ERR_OK;
351 }
352 }
353
354 /** Lock a mutex
355 * @param mutex the mutex to lock
356 */
sys_mutex_lock(sys_mutex_t * mutex)357 void sys_mutex_lock(sys_mutex_t *mutex)
358 {
359 RT_DEBUG_NOT_IN_INTERRUPT;
360 rt_mutex_take(*mutex, RT_WAITING_FOREVER);
361
362 return;
363 }
364
365 /** Unlock a mutex
366 * @param mutex the mutex to unlock
367 */
sys_mutex_unlock(sys_mutex_t * mutex)368 void sys_mutex_unlock(sys_mutex_t *mutex)
369 {
370 rt_mutex_release(*mutex);
371 }
372
373 /** Delete a semaphore
374 * @param mutex the mutex to delete
375 */
sys_mutex_free(sys_mutex_t * mutex)376 void sys_mutex_free(sys_mutex_t *mutex)
377 {
378 RT_DEBUG_NOT_IN_INTERRUPT;
379
380 rt_mutex_delete(*mutex);
381 }
382
383 #ifndef sys_mutex_valid
384 /** Check if a mutex is valid/allocated:
385 * return 1 for valid, 0 for invalid
386 */
sys_mutex_valid(sys_mutex_t * mutex)387 int sys_mutex_valid(sys_mutex_t *mutex)
388 {
389 return (int)(*mutex);
390 }
391 #endif
392
393 #ifndef sys_mutex_set_invalid
394 /** Set a mutex invalid so that sys_mutex_valid returns 0
395 */
sys_mutex_set_invalid(sys_mutex_t * mutex)396 void sys_mutex_set_invalid(sys_mutex_t *mutex)
397 {
398 *mutex = RT_NULL;
399 }
400 #endif
401
402 /* ====================== Mailbox ====================== */
403
404 /*
405 * Create an empty mailbox for maximum "size" elements
406 *
407 * @return the operation status, ERR_OK on OK; others on error
408 */
sys_mbox_new(sys_mbox_t * mbox,int size)409 err_t sys_mbox_new(sys_mbox_t *mbox, int size)
410 {
411 static unsigned short counter = 0;
412 char tname[RT_NAME_MAX];
413 sys_mbox_t tmpmbox;
414
415 RT_DEBUG_NOT_IN_INTERRUPT;
416
417 rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_MBOX_NAME, counter);
418 counter ++;
419
420 tmpmbox = rt_mb_create(tname, size, RT_IPC_FLAG_FIFO);
421 if (tmpmbox != RT_NULL)
422 {
423 *mbox = tmpmbox;
424
425 return ERR_OK;
426 }
427
428 return ERR_MEM;
429 }
430
431 /*
432 * Deallocate a mailbox
433 */
sys_mbox_free(sys_mbox_t * mbox)434 void sys_mbox_free(sys_mbox_t *mbox)
435 {
436 RT_DEBUG_NOT_IN_INTERRUPT;
437
438 rt_mb_delete(*mbox);
439
440 return;
441 }
442
443 /** Post a message to an mbox - may not fail
444 * -> blocks if full, only used from tasks not from ISR
445 * @param mbox mbox to posts the message
446 * @param msg message to post (ATTENTION: can be NULL)
447 */
sys_mbox_post(sys_mbox_t * mbox,void * msg)448 void sys_mbox_post(sys_mbox_t *mbox, void *msg)
449 {
450 RT_DEBUG_NOT_IN_INTERRUPT;
451
452 rt_mb_send_wait(*mbox, (rt_uint32_t)msg, RT_WAITING_FOREVER);
453
454 return;
455 }
456
457 /*
458 * Try to post the "msg" to the mailbox
459 *
460 * @return return ERR_OK if the "msg" is posted, ERR_MEM if the mailbox is full
461 */
sys_mbox_trypost(sys_mbox_t * mbox,void * msg)462 err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
463 {
464 if (rt_mb_send(*mbox, (rt_uint32_t)msg) == RT_EOK)
465 return ERR_OK;
466
467 return ERR_MEM;
468 }
469
470 /** Wait for a new message to arrive in the mbox
471 * @param mbox mbox to get a message from
472 * @param msg pointer where the message is stored
473 * @param timeout maximum time (in milliseconds) to wait for a message
474 * @return time (in milliseconds) waited for a message, may be 0 if not waited
475 or SYS_ARCH_TIMEOUT on timeout
476 * The returned time has to be accurate to prevent timer jitter!
477 */
sys_arch_mbox_fetch(sys_mbox_t * mbox,void ** msg,u32_t timeout)478 u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
479 {
480 rt_err_t ret;
481 s32_t t;
482 u32_t tick;
483
484 RT_DEBUG_NOT_IN_INTERRUPT;
485
486 /* get the begin tick */
487 tick = rt_tick_get();
488
489 if(timeout == 0)
490 t = RT_WAITING_FOREVER;
491 else
492 {
493 /* convirt msecond to os tick */
494 if (timeout < (1000/RT_TICK_PER_SECOND))
495 t = 1;
496 else
497 t = timeout / (1000/RT_TICK_PER_SECOND);
498 }
499
500 ret = rt_mb_recv(*mbox, (rt_ubase_t *)msg, t);
501 if(ret == -RT_ETIMEOUT)
502 {
503 return SYS_ARCH_TIMEOUT;
504 }
505 else
506 {
507 LWIP_ASSERT("rt_mb_recv returned with error!", ret == RT_EOK);
508 }
509
510 /* get elapse msecond */
511 tick = rt_tick_get() - tick;
512
513 /* convert tick to msecond */
514 tick = tick * (1000 / RT_TICK_PER_SECOND);
515 if (tick == 0)
516 tick = 1;
517
518 return tick;
519 }
520
521 /** Wait for a new message to arrive in the mbox
522 * @param mbox mbox to get a message from
523 * @param msg pointer where the message is stored
524 * @param timeout maximum time (in milliseconds) to wait for a message
525 * @return 0 (milliseconds) if a message has been received
526 * or SYS_MBOX_EMPTY if the mailbox is empty
527 */
sys_arch_mbox_tryfetch(sys_mbox_t * mbox,void ** msg)528 u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
529 {
530 int ret;
531
532 ret = rt_mb_recv(*mbox, (rt_ubase_t *)msg, 0);
533 if(ret == -RT_ETIMEOUT)
534 {
535 return SYS_ARCH_TIMEOUT;
536 }
537 else
538 {
539 if (ret == RT_EOK)
540 ret = 1;
541 }
542
543 return ret;
544 }
545
546 #ifndef sys_mbox_valid
547 /** Check if an mbox is valid/allocated:
548 * return 1 for valid, 0 for invalid
549 */
sys_mbox_valid(sys_mbox_t * mbox)550 int sys_mbox_valid(sys_mbox_t *mbox)
551 {
552 return (int)(*mbox);
553 }
554 #endif
555
556 #ifndef sys_mbox_set_invalid
557 /** Set an mbox invalid so that sys_mbox_valid returns 0
558 */
sys_mbox_set_invalid(sys_mbox_t * mbox)559 void sys_mbox_set_invalid(sys_mbox_t *mbox)
560 {
561 *mbox = RT_NULL;
562 }
563 #endif
564
565 /* ====================== System ====================== */
566
567 /*
568 * Start a new thread named "name" with priority "prio" that will begin
569 * its execution in the function "thread()". The "arg" argument will be
570 * passed as an argument to the thread() function
571 */
sys_thread_new(const char * name,lwip_thread_fn thread,void * arg,int stacksize,int prio)572 sys_thread_t sys_thread_new(const char *name,
573 lwip_thread_fn thread,
574 void *arg,
575 int stacksize,
576 int prio)
577 {
578 rt_thread_t t;
579
580 RT_DEBUG_NOT_IN_INTERRUPT;
581
582 /* create thread */
583 t = rt_thread_create(name, thread, arg, stacksize, prio, 20);
584 RT_ASSERT(t != RT_NULL);
585
586 /* startup thread */
587 rt_thread_startup(t);
588
589 return t;
590 }
591
sys_arch_protect(void)592 sys_prot_t sys_arch_protect(void)
593 {
594 rt_base_t level;
595
596 /* disable interrupt */
597 level = rt_hw_interrupt_disable();
598
599 return level;
600 }
601
sys_arch_unprotect(sys_prot_t pval)602 void sys_arch_unprotect(sys_prot_t pval)
603 {
604 /* enable interrupt */
605 rt_hw_interrupt_enable(pval);
606
607 return;
608 }
609
sys_arch_assert(const char * file,int line)610 void sys_arch_assert(const char *file, int line)
611 {
612 rt_kprintf("\nAssertion: %d in %s, thread %s\n",
613 line, file, rt_thread_self()->name);
614 RT_ASSERT(0);
615 }
616
sys_jiffies(void)617 u32_t sys_jiffies(void)
618 {
619 return rt_tick_get();
620 }
621
sys_now(void)622 u32_t sys_now(void)
623 {
624 return rt_tick_get() * (1000 / RT_TICK_PER_SECOND);
625 }
626
627 #ifdef RT_LWIP_PPP
sio_read(sio_fd_t fd,u8_t * buf,u32_t size)628 u32_t sio_read(sio_fd_t fd, u8_t *buf, u32_t size)
629 {
630 u32_t len;
631
632 RT_ASSERT(fd != RT_NULL);
633
634 len = rt_device_read((rt_device_t)fd, 0, buf, size);
635 if (len <= 0)
636 return 0;
637
638 return len;
639 }
640
sio_write(sio_fd_t fd,u8_t * buf,u32_t size)641 u32_t sio_write(sio_fd_t fd, u8_t *buf, u32_t size)
642 {
643 RT_ASSERT(fd != RT_NULL);
644
645 return rt_device_write((rt_device_t)fd, 0, buf, size);
646 }
647
sio_read_abort(sio_fd_t fd)648 void sio_read_abort(sio_fd_t fd)
649 {
650 rt_kprintf("read_abort\n");
651 }
652
ppp_trace(int level,const char * format,...)653 void ppp_trace(int level, const char *format, ...)
654 {
655 va_list args;
656 rt_size_t length;
657 static char rt_log_buf[RT_CONSOLEBUF_SIZE];
658
659 va_start(args, format);
660 length = rt_vsprintf(rt_log_buf, format, args);
661 rt_device_write((rt_device_t)rt_console_get_device(), 0, rt_log_buf, length);
662 va_end(args);
663 }
664 #endif
665
666 /*
667 * export bsd socket symbol for RT-Thread Application Module
668 */
669 #if LWIP_SOCKET
670 #include <lwip/sockets.h>
671 RTM_EXPORT(lwip_accept);
672 RTM_EXPORT(lwip_bind);
673 RTM_EXPORT(lwip_shutdown);
674 RTM_EXPORT(lwip_getpeername);
675 RTM_EXPORT(lwip_getsockname);
676 RTM_EXPORT(lwip_getsockopt);
677 RTM_EXPORT(lwip_setsockopt);
678 RTM_EXPORT(lwip_close);
679 RTM_EXPORT(lwip_connect);
680 RTM_EXPORT(lwip_listen);
681 RTM_EXPORT(lwip_recv);
682 RTM_EXPORT(lwip_read);
683 RTM_EXPORT(lwip_recvfrom);
684 RTM_EXPORT(lwip_send);
685 RTM_EXPORT(lwip_sendto);
686 RTM_EXPORT(lwip_socket);
687 RTM_EXPORT(lwip_write);
688 RTM_EXPORT(lwip_select);
689 RTM_EXPORT(lwip_ioctl);
690 RTM_EXPORT(lwip_fcntl);
691
692 RTM_EXPORT(lwip_htons);
693 RTM_EXPORT(lwip_ntohs);
694 RTM_EXPORT(lwip_htonl);
695 RTM_EXPORT(lwip_ntohl);
696
697 RTM_EXPORT(ipaddr_aton);
698 RTM_EXPORT(ipaddr_ntoa);
699
700 #if LWIP_DNS
701 #include <lwip/netdb.h>
702 RTM_EXPORT(lwip_gethostbyname);
703 RTM_EXPORT(lwip_gethostbyname_r);
704 RTM_EXPORT(lwip_freeaddrinfo);
705 RTM_EXPORT(lwip_getaddrinfo);
706 #endif
707
708 #endif
709
710 #if LWIP_DHCP
711 #include <lwip/dhcp.h>
712 RTM_EXPORT(dhcp_start);
713 RTM_EXPORT(dhcp_renew);
714 RTM_EXPORT(dhcp_stop);
715 #endif
716
717 #if LWIP_NETIF_API
718 #include <lwip/netifapi.h>
719 RTM_EXPORT(netifapi_netif_set_addr);
720 #endif
721
722 #if LWIP_NETIF_LINK_CALLBACK
723 RTM_EXPORT(netif_set_link_callback);
724 #endif
725
726 #if LWIP_NETIF_STATUS_CALLBACK
727 RTM_EXPORT(netif_set_status_callback);
728 #endif
729
730 RTM_EXPORT(netif_find);
731 RTM_EXPORT(netif_set_addr);
732 RTM_EXPORT(netif_set_ipaddr);
733 RTM_EXPORT(netif_set_gw);
734 RTM_EXPORT(netif_set_netmask);
735