ZephyrのTCP/IP実装 メインループを読む

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](https://gerrit.zephyrproject.org/r/gitweb?p=zephyr.git;a=blob;f=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が正なら、次のループに移動する。

```c: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](https://gerrit.zephyrproject.org/r/gitweb?p=zephyr.git;a=blob;f=net/ip/net_core.c) にあります。
* 受信パケットはネットワークデバイスのドライバで受信キューに登録されます。
* nano_fifo_get()で受信キューを読み出す。
* tcpip_input()で、process_post_synch()でuIPのループに渡されて、TCP/IPの処理を行う。
* tcpip_input()の戻り値が0なら、bufを解放する。

```c: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](https://gerrit.zephyrproject.org/r/gitweb?p=zephyr.git;a=blob;f=net/ip/contiki/ip/tcpip.c) にあります。

```c:tcpip.c
PROCESS_THREAD(tcpip_process, ev, data, buf, user_data)
{
  PROCESS_BEGIN();

```

* 初期化

```c: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

```c:tcpip.c
  while(1) {
    PROCESS_YIELD();
    eventhandler(ev, data, buf);
  }
  
  PROCESS_END();
}
```

# 参考
* https://www.zephyrproject.org/