それがすべて始まった方法
したがって、私たちの素晴らしいBercut-MMTデバイス(PXA320およびGNU / Linuxベース)には、パルス光反射率計であるOTDRモジュール(STM32およびNutOSベース)があります。 このバンドルは次のように機能します。ユーザーがUIのさまざまな要素の画面をクリックすると、デバイスで小さな魔法が発生し、ユーザーの希望は「duration 300」タイプのコマンドに変換され、測定モジュールに送られます。 具体的には、このコマンドは測定時間を300秒に設定します。 モジュールはUSB経由でデバイスに接続され、CDC-ACMはUSB経由でコマンドを送信するために発生します。
簡単に説明すると、CDC-ACMを使用すると、USB経由でシリアルポートをエミュレートできます。 したがって、最上位レベルでは、システムの測定モジュールは/ dev / ttyACM0として利用可能です。 CDC-ACMは、コマンドをモジュールに送信したり、モジュールの現在の設定/ステータスを読み取るために使用されます。 USBバルクインターフェイスを使用してトレースを送信しました。これは、バイナリデータストリームのように、モジュールからデバイスへのトレースデータの転送という1つのことだけに専念していました。 ある時点で、痕跡が完全には届いていないことに気付きました。 そこで、USBがデータを失う可能性があることを発見しました。
概略的には、次のようになりました。
b5-cardifacedは、D-Busコマンドを受け入れ、CDC-ACMインターフェイス経由でカードに送信するデーモンです。 実行結果はD-BUS経由で返送されます。
usbgatherはlibusbに基づいて動作する小さなプログラムであり、USB Bulkを介してモジュールからトレースをスクープし、stdoutに提供するという事実に関与しています。
松葉杖と自転車
私たちは座って考えました-不完全なリフレクトグラムをスキップできるようにするには、トレース全体が来たかどうかを理解する必要があります。 さまざまなトリッキーな見出し、チェックサムなどを思いつき始めました。 その後、TCPを発明していることに気付きました。 そして、USB Bulkの代わりに、CDC-EEMの上にTCP / IPを取得するという強い意思決定が行われました。 CDC-EEMはなぜですか? CDC-EEMを使用すると、ネットワークトラフィックを送信するためのトランスポートとしてUSBを最も簡単に使用できるためです。 デバイス自体では、カーネルでCDC-ECMがサポートされており、オペレーティングシステムモジュールとしてNutOSを使用しており、NutOSはCDC-EEMおよびTCP / IPスタックをサポートしていました。
3か月の修正
トラブルの前兆はないようです。 上げられたCDC-EEM、設定されたIPアドレス。 ピン? pingがあります! やったー USB Bulkからデータを転送するメカニズムを、TCPソケットを介したデータ転送に変更しました。 幸福がまもなくやって来ましたが、テスト中に突然、ネットワークはその困難な生活、曲がった手、利害関係のあるネットワークインターフェイスの送信キューについて叫びました。 このようなもの:
[ 118.289339] ------------[ cut here ]------------ [ 118.293978] WARNING: at net/sched/sch_generic.c:258 dev_watchdog+0x184/0x298() [ 118.301163] NETDEV WATCHDOG: usb2 (cdc_eem): transmit queue 0 timed out [ 118.307726] Modules linked in: cdc_eem usbnet cdc_acm wm97xx_ts ucb1400_ts ipv6 cards button pmmct [ 118.318671] [<c002f750>] (unwind_backtrace+0x0/0xec) from [<c003e5a4>] (warn_slowpath_common+0x4c) [ 118.328017] [<c003e5a4>] (warn_slowpath_common+0x4c/0x7c) from [<c003e668>] (warn_slowpath_fmt+0x) [ 118.337536] [<c003e668>] (warn_slowpath_fmt+0x30/0x40) from [<c02ab738>] (dev_watchdog+0x184/0x29) [ 118.346552] [<c02ab738>] (dev_watchdog+0x184/0x298) from [<c0049938>] (run_timer_softirq+0x18c/0x) [ 118.355731] [<c0049938>] (run_timer_softirq+0x18c/0x26c) from [<c0043f78>] (__do_softirq+0x84/0x1) [ 118.364819] [<c0043f78>] (__do_softirq+0x84/0x114) from [<c004404c>] (irq_exit+0x44/0x64) [ 118.372959] [<c004404c>] (irq_exit+0x44/0x64) from [<c0029074>] (asm_do_IRQ+0x74/0x94) [ 118.380843] [<c0029074>] (asm_do_IRQ+0x74/0x94) from [<c0029b04>] (__irq_svc+0x44/0xcc) [ 118.388792] Exception stack(0xc0425f78 to 0xc0425fc0) [ 118.393819] 5f60: 00000001 c606b300 [ 118.401958] 5f80: 00000000 60000013 c0424000 c0428334 c0454bac c0428328 a0022438 69056827 [ 118.410100] 5fa0: a0022368 00000000 c0425f98 c0425fc0 c002b04c c002b058 60000013 ffffffff [ 118.418232] [<c0029b04>] (__irq_svc+0x44/0xcc) from [<c002b058>] (default_idle+0x34/0x40) [ 118.426367] [<c002b058>] (default_idle+0x34/0x40) from [<c002b5cc>] (cpu_idle+0x54/0xb0) [ 118.434425] [<c002b5cc>] (cpu_idle+0x54/0xb0) from [<c00089f0>] (start_kernel+0x28c/0x2f8) [ 118.442653] [<c00089f0>] (start_kernel+0x28c/0x2f8) from [<a0008034>] (0xa0008034) [ 118.450180] ---[ end trace d7e298087ff4c373 ]---
簡潔に言えば、文字通り簡単に言えば-各ネットワークインターフェイスには、最後の各データ送信からの時間を記録するタイマーがあり、特定の間隔を超えた場合、このメッセージが表示されます。 その後、すべてはドライバーに依存します-一部は正常に動作し、一部は動作しません。
悪の根
上記のすべては、不明なリンクcmdに関するdmesgのメッセージによって悪化しました。 さらにデバッグを追加し、USBホストへのエコー要求応答を受信したことがわかりましたが、送信しませんでした。
何も機能しない場合は、ドキュメントを読む時間です。 そのため、CDC-EEMにドックを入手しましたが、どこからでもではなく、usb.orgから直接入手しました。 最初のEEMパケットは、データの束であるだけでなく、パケットのタイプ(制御またはデータ)とデータの長さを含むEEMヘッダーでもあることがわかります。 はい、CDC-EEMには独自のエコー要求/エコー応答があります。
しかし、もう少し詳しく説明しなければ、私たちの幸せは完全ではありません。サービスパケットを受信すると、モジュールがハングします。 しっかり。 CDC-ACMと一緒に。
USBモジュールでは、送信が64バイトのパケットで送信されるように構成されました。 したがって、64個のN個のパケットで1つのイーサネットパケットがbeat打され、USB経由で送信されました。 このように:
非常に長い間状況を調査した結果、これが何であるかという結論に達しました。EEMパッケージの一部を失います(そう、USBは配信を保証しません)。 しかし、ヘッダーから長さを読み取り、それに依存しています。 したがって、次のパケットからN個の失われたバイトを減算し、次のデータを新しいEEMパケットの始まりとして認識し、最初の2バイトをヘッダーとして解釈します。 そして、何かがあるかもしれません。 これはサービスパッケージであることを示す最大1ビットに設定されています。 非常に悪いケースでは、EEMヘッダーとして解釈されると、巨大なエコー応答を提供するデータをキャッチします。 RAMをはるかに超えています。 そのため、NutOSでusbnetを実装するには深刻な改善が必要であることに気付きました。
より良いチェックと異なるチェック
NutOSでusbnetを選択するプロセスで、現在のバージョンではサービスパケットを受信する準備がまったく整っていないことがわかりました。 すべての言葉から。 新しいオプションを作成しました。これにより、サービスパッケージを正しく処理できるようになりました。つまり、標準でエコーに応答する必要があるため、パッケージのタイプを調べました。 長さをチェックしました-MTUより大きい場合-明らかにゴミをキャッチしました。 また、エンドポイントを介してデータ転送を開始する関数に異常が見つかりました。ゼロエンドポイントが現在ビジーであるかどうかをチェックし、ビジーである場合はそれだけでした。 この関数の呼び出し元は、データ転送が開始されたと常に信じていましたが、しばしばそうではないことが判明しました。 その結果、データを両方向で失いました。
TCPソケットとの戦争がありました-データが送信されないことがあり、その理由はわかりませんでした。 NutOS開発者を導いた理由はわかりませんが、不明な状況でintが返された多くの関数は-1を返しました。 それらの一部はソケット情報に実際のエラーコードを書き留めましたが、一部は書きませんでした。 したがって、ネットワークカードからデータを送信する機能のように、NutTcpDeviceWrite?()のような機能を一番下に、リターンコードを下からドラッグする必要がありました。 その後、プラグが発生した場所を確認できました。
その後、あらゆる種類の終了とソケットのタイムアウトの調整が行われ、モジュールとデバイス自体のARPテーブルに静的エントリが追加されました。ネットワークにはデバイスが2つしかありません:デバイスとモジュール、ARPテーブルの古いエントリには意味がありません。
まとめ
マップには小さなTCPサーバーがあり、カードからデバイスにデータを転送します。 測定を開始する前に、カードでTCPサーバーが起動し、カードは着信接続を待機し始めます。 クライアントがTCPサーバーに接続すると、カードは測定を開始し、TCPサーバー経由でデバイスに結果を送信します。
ここで概略的に、モジュールを使用したデバイスの操作は次のようになります。
俳優:
b5-cardifaced-前と同じ-D-Busからカードにコマンドを送信し、結果をD-Busに送り返します。
nc-実際にはnetcatで、ソケットからトレースデータを読み取り、さらに処理するためにstdoutに送信します。
これらすべての冒険の後、ネットワーク反射率計が完成しました。 ただし、ネットワーキングは100%完全ではありません-制御はCDC-ACMを介して行われ、モジュールからのデータ収集はCDC-EEMを介したTCP / IPを介して行われます。 まだわずかなデータ損失がありますが、出力でTCP / IPを使用しているため、常に完全なトレースを取得します。 USB全般、特にCDC-EEMについて多くのことを学びましたが、以前よりもUSBが少し好きになりました。
負荷テストでは、STM32F103に基づくモジュールがTCP / IP over CDC-EEMを介して220キロバイトのデータを毎秒ポンピングできる一方で、このモジュールは現時点で有用な作業を行っており、USBはDMAなしで動作することが示されました。