2015年12月20日日曜日

Ether ShieldのW5100をSTM8Sから動かしてみる (5)ping

上司が言うんですよ、「pingが通らなかったらnetwork機器とは云えないぞ」.確かにそうだと合点して、pingに応答するようにしました.

ところがW5100でpingに応答するにはちょっとかったるいんです.w5100のUDPやTCPはautomaticに動く感が強いのですが、ping応答はそうではなく自力でごにょごにょせにゃいかんみたい.

なぜかというと、UDPやTCPと同列に語られる(と思う)ICMPというのがあって、pingはICMPを活用して行われる.ゆえにW5100でICMPを動くようにする必要がある.さしずめTCP(ICMP)を土台としてHTTP(ping)が動くような比喩をできるのではないかと思う.
しかし、W5100にはICMPモードというのは存在せず、RAWIPモードを使ってICMPを実装する必要がある.RAWIPモードでは、送受信データの受け渡しのみをW5100が請け負いますが、それだけしかやりませんよというモードです.

pingを使ったことのある人はご存知でしょうが、①マシンAからマシンBにpingを打つと、②マシンBがマシンAに受領の旨返信する、という2段階のpacketの送受でpingが通ったなどと称します.
ゆえに、①のpacketを少し加工して②のpacketとして返信する必要があるのですが、W5100はICMPをautomaticで返信してくれず、①→②の加工をユーザーが自分でやる必要があるぞと、そんな状況だったわけだった.

------------
W5100のRAWIPモードはどこまで面倒を見てくれるんだろう?

↓Ethernet Cableへ降りてゆくにつれて色々なヘッダが追加されてゆき、ICMPフレームは最終的にこんなになっているそうです.W5100のRAWIPモードはデータの送受だけやりますと上で書きましたが、それはどの部分なのかというと、下図の青で囲った部分だけです.他の部分はバックグラウンドで捨てられて見えません.
↓①W5100の受信バッファに積まれるデータの具体例(=青囲い)はこんなです.
送信元IP+データ数で6BYTEが先頭に付加されていて、これはW5100がサービスで付けてくれたデータです.+6以降の40H BYTEが上図の青囲い部分です.タイプ+コード+チェックサムがあります.その後続はユーザーデータで、ping commandが好きに挿入したデータですからdon't careでOK.タイプ=08だとping要求の意味があります.
↓詳細の説明は後でしますが、②ping返信データはこうなります.受信データから変えたところは、送信元IPとデータ数を捨てて、タイプとチェックサムを変更しています.タイプ=0だとping返信の意味があります.これをW5100の送信バッファに積んで「送信命令」をW5100に与えるとping返信が成立します.

問題はチェックサムのところでした.IPのチェックサム計算をするのは未体験ゾーンなため.
1の補数のチェックサムの手順は以下のようになります.IPに登場する1の補数のチェックサムは皆同じやり方みたいです.
 1)チェックサム部分をとりあえず0000にしておく
 2)データを、16bitごとに区切る.この例では32ワードのデータに区切れる
3)32ワードを全部足します.ここで、桁あふれを保存するために32bit加算する
000505fa
4)上位16bitと下位6bitに分けて32bit加算する
000005FF
5)4と同じことをもう一度やる.ここでは結果は同じになる.2度やる理由はよくわからないが、IPだと高々1500BYTEなので2度で済むということなのかもしれない.

6)それの下位16bitを残す
05FF
7)bit反転すると、FA00が出てきました.おしまい
FA00

というわけで、言葉で書くとping受信→ping返信の手続きはこれだけで動くようです.

---------
コピってチェックサムを入れ替えるって簡単そうですが、W5100の制御コードはずいぶんと長いものになりました.

sizeRX = getSn_RX_RSR();  ping受信データBYTE数
if(sizeRX==0) goto EX;   ping受信データ=0ならスルー

sizeRX = sizeRX & bufMUSK;  ping受信データBYTE数
topRX = bufRXtop + (getSn_RX_RD()&bufMUSK); 受信バッファ先頭アドレス

ip[0] = w51RD(bufRXarign(topRX  ));  ping発信元マシンのIPアドレス
ip[1] = w51RD(bufRXarign(topRX+1));
ip[2] = w51RD(bufRXarign(topRX+2));
ip[3] = w51RD(bufRXarign(topRX+3));

     中略)

以下はping返信の手続き

r16 = getSn_TX_FSR();   送信バッファ空きBYTE
r8 = getSn_SR();    動作モード
if(tim3_reset_seq!=0 || r16<sizeRX || r8!=SOCK_IPRAW) goto UPDATERXBUF;      送信バッファ空きとか諸々をチェック

topTX = bufTXtop + (getSn_TX_WR() & bufMUSK); 送信バッファ先頭アドレス

ptrRX = 6;  受信pingデータを送信バッファにコピーする
ptrTX = 0;
do
  {
    adrsRX = bufRXarign(topRX+ptrRX);
    adrsTX = bufTXarign(topTX+ptrTX);
    if(ptrTX==0) w51WR(adrsTX,0);     タイプ=0
    else if(ptrTX==2) w51WR(adrsTX,0);  チェックサム=0
    else if(ptrTX==3) w51WR(adrsTX,0);  チェックサム=0
    else w51WR(adrsTX,w51RD(adrsRX));  他はそのままコピー
    ptrRX++;
    ptrTX++;
  }while(ptrRX<sizeRX);

chksum32=0;    送信pingのチェックサムを計算する
sizeTX=sizeRX-6; 送信データは受信データよりも6バイト少ない
ptrTX=0;
do{
  uint8 H,L;
  H=w51RD(bufTXarign(topTX+ptrTX  ));
  L=w51RD(bufTXarign(topTX+ptrTX+1));
  chksum32 += (uint16)(H<<8) + L;    32bit加算
  ptrTX+=2;
}while(ptrTX<sizeTX);
chksum32 = (chksum32>>16) + (chksum32 & 0xFFFF); 上位16+下位16
chksum32 = (chksum32>>16) + (chksum32 & 0xFFFF); 上位16+下位16
chksum32 = ~chksum32;            bit反転
chksum16 = chksum32 & 0xFFFF;   下位16bitのみでチェックサムを得る

送信バッファにチェックサムを記入する
w51WR( bufTXarign(topTX+2) , (uint8)(chksum16>>8) );
w51WR( bufTXarign(topTX+3) , (uint8)(chksum16&0xFF) );

setSn_DIPR(ip);  返信相手のIPをW5100にセットする

setSn_TX_WR( getSn_TX_WR() + sizeTX );  送信バッファポインタ更新
setSn_CR( Sn_CR_SEND );    送信コマンド
while( getSn_CR() != 0 ){}

UPDATERXBUF:
setSn_RX_RD( getSn_RX_RD() + sizeRX );  受信バッファポインタを更新
setSn_CR( Sn_CR_RECV );    受信コマンド
while( getSn_CR() != 0 ){}

EX:

------------
サンプルプログラムでは自機IPアドレス=192.168.1.100にしてあるので、
ping 192.168.1.100
Linuxあるいはwindowsマシンからpingを打つと、本機からの応答を観測できるはず.

例によってサンプルプログラムであなたが受けた損害がなんであれわたしは知りませんぞ.

さて、次はTCPの実装だ.

4へ   6へ

かしこ

=== STMのアフィリエイト始めました ===
STM32のwelcome-kitです
        
試用レポはいずれまた...

5 件のコメント:

  1. チェックサム懐かしいですね、パンチが1つ間違っただけで、1日棒に振るなんてのが何回か・・

    返信削除
    返信
    1. パンチカードは一度もお目にかかった経験がございません.
      せいぜいカセットテープぐらいです.

      削除
  2. winインストールisoを焼いてドジッタ話。
    ダウンロードしたisoをPower2goで焼き込み、空きが多いので他のファイルを足して追記しようとした、なんかの作業が終わって、
    ウエルにロードとかでなかったので失敗した。
    もう一枚入れて、isoと他のをドロップ書いてみると全部有ったが、起動してもインストールが始まらない。
    前のを入れてみるとsetupがあって起動、他のファイルは個別に動いてインストールできる。
    追記の概念が真逆だった怪。

    返信削除
  3. PING で思い出した。
    CMD
    >google.com [216.58.221.14]に ping を送信しています 32 バイトのデータ:
    216.58.221.14 からの応答: バイト数 =32 時間 =48ms TTL=56
    216.58.221.14 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
    ラウンド トリップの概算時間 (ミリ秒):
    最小 = 48ms、最大 = 50ms、平均 = 49ms

    >ping ntt
    ping 要求ではホスト ntt が見つかりませんでした。ホスト名を確認してもう一度実行してください。

    >ping 60.60.50.38
    60.60.50.38 に ping を送信しています 32 バイトのデータ:
    要求がタイムアウトしました。
    60.60.50.38 の ping 統計:
    パケット数: 送信 = 4、受信 = 0、損失 = 4 (100% の損失)

    >ping ntt.com
    ntt.com [210.226.39.112]に ping を送信しています 32 バイトのデータ:
    要求がタイムアウトしました。
    210.226.39.112 の ping 統計:
    パケット数: 送信 = 4、受信 = 0、損失 = 4 (100% の損失)

    返事があってデータが伝送できないとき
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
    等と表示されます。

    返信削除
    返信
    1. 外にping打ったことはないけれど、googleから48mSで戻ってくるとは早いですね

      削除