ZephyrのTCP/IPプロトコルスタックのメインループを読んでみました。
ZephyrはLinux Foundationが開発しているRTOSです。
ソース
* https://gerrit.zephyrproject.org/r/gitweb?p=zephyr.git
ZephyrのTCP/IPプロトコルスタックはContiki OSのuIPを使っています。そのスタックをZephyrのスタイルに合わせるためにラッピングしています。
プロトコルスタックのループは3つあります。
* 送信パケットのループ (by Zephyr)
* 受信パケットのループ (by Zephyr)
* uIPのループ (by Contiki)
送信パケットのループ
- 送信パケットのループはfiberで実装しています。net/ip/net_core.c にあります。
- nano_fifo_get()で送信キューから送信パケットを取り出す。
- check_and_send_packet()で送信します。送信データはTCP, UDP, ARPの処理を行い、process_post_synch()でuIPのループに渡されます。
- check_and_send_packet() の戻り値rが0であるなら、process_run()でバッファの状態を監視し、処理が完了したらbufを解放する。
- check_and_send_packet() の戻り値rが負なら、bufを解放して次のループに移動する。
- check_and_send_packet() の戻り値rが正なら、次のループに移動する。
net_core.c
static void net_tx_fiber(void)
{
NET_DBG("Starting TX fiber (stack %d bytes)\n",
sizeof(tx_fiber_stack));
while (1) {
struct net_buf *buf;
int ret;
/* Get next packet from application - wait if necessary */
buf = nano_fifo_get(&netdev.tx_queue, TICKS_UNLIMITED);
NET_DBG("Sending (buf %p, len %u) to IP stack\n",
buf, buf->len);
/* What to do with the buffer:
* <0: error, release the buffer
* 0: message was discarded by uIP, release the buffer here
* >0: message was sent ok, buffer released already
*/
ret = check_and_send_packet(buf);
if (ret < 0) {
ip_buf_unref(buf);
goto wait_next;
} else if (ret > 0) {
goto wait_next;
}
NET_BUF_CHECK_IF_NOT_IN_USE(buf);
/* Check for any events that we might need to process */
do {
ret = process_run(buf);
} while (ret > 0);
ip_buf_unref(buf);
wait_next:
/* Check stack usage (no-op if not enabled) */
net_analyze_stack("TX fiber", tx_fiber_stack,
sizeof(tx_fiber_stack));
net_print_statistics();
}
}
受信パケットのループ
- 受信パケットのループはfiberで実装しています。net/ip/net_core.c にあります。
- 受信パケットはネットワークデバイスのドライバで受信キューに登録されます。
- nano_fifo_get()で受信キューを読み出す。
- tcpip_input()で、process_post_synch()でuIPのループに渡されて、TCP/IPの処理を行う。
- tcpip_input()の戻り値が0なら、bufを解放する。
net_core.c
static void net_rx_fiber(void)
{
struct net_buf *buf;
NET_DBG("Starting RX fiber (stack %d bytes)\n",
sizeof(rx_fiber_stack));
while (1) {
buf = nano_fifo_get(&netdev.rx_queue, TICKS_UNLIMITED);
/* Check stack usage (no-op if not enabled) */
net_analyze_stack("RX fiber", rx_fiber_stack,
sizeof(rx_fiber_stack));
NET_DBG("Received buf %p\n", buf);
if (!tcpip_input(buf)) {
ip_buf_unref(buf);
}
/* The buffer is on to its way to receiver at this
* point. We must not remove it here.
*/
net_print_statistics();
}
}
uIPのループ
- uIPのループは net/ip/contiki/ip/tcpip.c にあります。
tcpip.c
PROCESS_THREAD(tcpip_process, ev, data, buf, user_data)
{
PROCESS_BEGIN();
- 初期化
tcpip.c
#if UIP_TCP
{
static unsigned char i;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
s.listenports[i].port = 0;
}
s.p = PROCESS_CURRENT();
}
#endif
tcpip_event = process_alloc_event();
#if UIP_CONF_ICMP6
tcpip_icmp6_event = process_alloc_event();
#endif /* UIP_CONF_ICMP6 */
etimer_set(&periodic, CLOCK_SECOND / 2, &tcpip_process);
uip_init();
#ifdef UIP_FALLBACK_INTERFACE
UIP_FALLBACK_INTERFACE.init();
#endif
/* initialize RPL if configured for using RPL */
#if NETSTACK_CONF_WITH_IPV6 && UIP_CONF_IPV6_RPL
rpl_init();
#endif /* UIP_CONF_IPV6_RPL */
- 送信イベント、受信イベント、タイマイベントを処理する。
- イベント:PROCESS_EVENT_EXITED PROCESS_EVENT_TIMER TCP_POLL UDP_POLL PACKET_INPUT
tcpip.c
while(1) {
PROCESS_YIELD();
eventhandler(ev, data, buf);
}
PROCESS_END();
}