
インターネット・プロトコル・スイート
インターネット/トランスポート層として、リンク層上にTCP/IPを実装します。今回の実装では、uIP*1を利用しました。
uIP
uIPは、Swedish Institute of Computer ScienceのAdam Dunkels氏が開発したオープンソースのTCP/IPスタックです。商用、非商用どちらでもフリーで利用できます。uIPはC言語で記述された、8bit CPUでも実装可能な小コードサイズとRAM使用サイズを特徴としたTCP/IPスタックであり、豊富なアプリケーションサンプル(Webサーバ、Webクライアント、SMTPクライアント、Telnetサーバ等)が付属しています。
uIPは以下のディレクトリで構成されています。
uip-1.0
uip/
apps/
webserver/
doc/
lib/
unix/
uipディレクトリにTCP/IPスタックコードが、apps/webserverディレクトリにWebサーバのアプリケーションサンプルが格納されています。今回は、主にこの2つのディレクトリのデータを利用して、TCP/IP及びHTTPの実装を行います。
uIPの移植
ターゲットとするシステムに組み込む為に、uIPコンフィグレーション・ファイルの設定とタイマー機能の実装を行います。
コンフィグレーションファイルの設定
uip-conf.hを書き換えて、各パラメータの設定を行います。主に、uIPで使用されるデータタイプの定義と、バッファサイズの指定を行います。
#ifndef __UIP_CONF_H__ #define __UIP_CONF_H__ // データタイプの定義 typedef unsigned char u8_t; typedef unsigned short u16_t; typedef unsigned short uip_stats_t; // #define UIP_CONF_MAX_CONNECTIONS 40 // 最大Connection数 #define UIP_CONF_MAX_LISTENPORTS 40 // 最大 Listening TCPポート数 #define UIP_CONF_BUFFER_SIZE 840 // パケットバッファサイズ #define UIP_CONF_BYTE_ORDER LITTLE_ENDIAN #define UIP_CONF_UDP 0 #define UIP_CONF_UDP_CHECKSUMS 1 #define UIP_CONF_STATISTICS 1 // http用ヘッダファイル #include "webserver.h" #endif // __UIP_CONF_H__
タイマーの実装
タイマーは、TCPの状態遷移をTIME_WAITからCLOSEDに遷移させる為や、パケットの再送タイミングとして使用されます。
今回は、SH-4Aの32bitタイマユニット(TMU2)を利用してタイマーを実装しています。以下の設定では、TMU2に対して1秒ごとにアンダーフローインタラプトが発生するように指定を行っています。(clock_init())
(clock-arch.h)
#ifndef __CLOCK_ARCH_H__ #define __CLOCK_ARCH_H__ typedef unsigned int clock_time_t; #define CLOCK_CONF_SECOND 1000 int get_timer_count_ether(int ms_count); #endif // __CLOCK_ARCH_H__
(clock-arch.c)
#include "iodefine.h"
#include "clock-arch.h"
clock_time_t
clock_time(void) {
// return TMU2 current counter
unsigned int x = TMU2.TCNT;
return x;
}
int get_timer_count_ether(int ms_count) {
return (int)((float)ms_count/0.030720); // 30ns x 1024 (PCK/1024)
}
void clock_init(void) {
// TMU2 is used for Networking
// Configured to count ARP timeout
// pck = 33MHz = 30ns, pck/1024 = 130KHz = 30us
// set interrupt priority for TMU0(bit28:24),TMU1(bit20:16), TMU2(bit12:8)
INTC.INT2PRI0.LONG = 0x0c0c0c00; // bit 28-24 (0,1 = mask)
INTC.INT2MSKCR.LONG = 1; // TMU0-2
TMU2.TCOR = get_timer_count_ether( 1000); // 1Sec
TMU2.TCNT = get_timer_count_ether( 1000);
TMU2.TCR.WORD = 0x0024; // timer control UNF=0,UNIE=1(int),CKEG=00 TPSC=100
}
uIPの組み込み
TCP/IP処理のメインルーチンは、Ethernet Frameの受信インタラプトをトリガとして処理を開始します。Ethernet Frameを受信すると、typeフィールドの値を確認し、データがIP(Internet Protocol)の場合とARP(Address Resolution Protocol)の場合で処理を分岐します。
FrameデータがARPパケットの場合、uip_arp_arpin()をコールして応答用のARPパケットを生成します。応答用ARPには送信元のMACアドレスがセットされます。ARPパケットの生成後、ether_write_frame()をコールしてEthernet Frameを送信します。
FrameデータがIPの場合、次の順に処理を行います。
- uip_arp_ipin()をコールして、送信元のIPアドレスとMACアドレスの対応を確認し、内部ARPテーブルのアップデートを行います。
- uip_input()をコールしてTCPパケットの処理を行います。現在のTCPの遷移状態に従って、処理を行うイベントを判別し、UIP_APP_CALL()をコールします。HTTPは、この関数内で処理されます。
- UIP_APP_CALL()の結果、送信用のIPパケットが存在すれば、uip_arp_out()をコールしてEthernet Headerの作成を行います。
- ether_write_frame()をコールして、Ethernet Frameを送信します。

uIP動作フロー
実際のコードは以下のようになります。コード中のuip_lenとuip_bufは、uIPで定義されているグローバル変数です。uIPとリンク層は、uip_lenとuip_bufを用いてデータの受け渡しを行います。uip_bufは送受信用のデータを格納する為に使用され、uip_lenは、送受信用データの長さを格納する為に使用されます。
(実装コード:SH4インタラプトハンドラ INTR IRQ2)
void INT_IRL_LEVEL9() {
// INTR IRQ2
struct st_intsrc int_src;
int i;
int_src.LONG = ETHER_INT_SOURCE.LONG;
if (int_src.TXB) {
ETHER_INT_SOURCE.LONG = 1; // Clear Int
}
if (int_src.RXB) {
// Ethernet Frameの受信
ETHER_INT_SOURCE.LONG = 4; // インタラプトのクリア
uip_len = ether_read_frame(uip_buf); // Frameデータと長さを獲得
if(uip_len > 0) {
if(BUF->type == htons(UIP_ETHTYPE_IP)) {
// IP Packet
uip_arp_ipin(); // ARPテーブルアップデート
uip_input(); // TCPパケット処理、(UIP_APP_CALL()呼び出し)
if(uip_len > 0) {
uip_arp_out(); // Ethernet Headerの作成
ether_write_frame(uip_len, uip_buf); // Ethernet Frameの送信
}
} else if(BUF->type == htons(UIP_ETHTYPE_ARP)) {
// ARP
uip_arp_arpin(); // 応答用ARPパケットの生成
if(uip_len > 0) {
ether_write_frame(uip_len,uip_buf); // ARPパケットの送信
}
}
}
}
}