2012/06/30

[llcp]シーケンス番号

コメントをもらいつつ、LLCPの実装が進んでいる。
今週は黙々とやっていたのだが、ああだこうだやりつつI PDUのところまでできたと思う。


I PDU (Informationの略)には、シーケンス番号がある。
ドキュメントでは、N(S)とN(R)と書いてある。
LLCの説明と同じなら、NはNowのNらしい。
これを取り扱うために、4つの状態変数を持つことになる(Connection-orientedの場合)。

  • V(S) : 送信状態変数
  • V(SA) : 送信ACK状態変数
  • V(R) : 受信状態変数
    V(RA) : 受信ACK状態変数

V(SA)は、最後に受信したN(R)を覚えておく。
V(RA)は、最後に送信したN(R)を覚えておく。

V(S)とV(R)の範囲は0~15。
V(S)は次に送信するI PDUのN(S)になる。I PDUを送信したらインクリメントする。
V(R)は次に受信するI PDUのN(S)の期待値で、V(R)==N(S)だったらインクリメントする。

 

自分をA、相手をBとし、自分が最初のI PDUを送信したとすると、こうなるはず。

A(VS=0, VR=0) → I(NS=0, RS=0) → B(VS=0, VR=0)
  A(VS=1, VR=0) : Iを送信したので、VSインクリメント
  B(VS=0, VR=1) : VR==NSだったので、VRインクリメント
A(VS=1, VR=0) ← I(NS=0, RS=1) ← B(VS=0, VR=1)
  A(VS=1, VR=1) : VR==NSだったので、VRインクリメント
  B(VS=1, VR=1) : Iを送信したので、VSインクリメント
A(VS=1, VR=1) → I(NS=1, RS=1) → B(VS=1, VR=1)
  A(VS=2, VR=1) : Iを送信したので、VSインクリメント
  B(VS=1, VR=2) : VR==NSだったので、VRインクリメント
A(VS=2, VR=1) ← I(NS=1, RS=2) ← B(VS=1, VR=2)
  A(VS=2, VR=2) : VR==NSだったので、VRインクリメント
  B(VS=2, VR=2) : Iを送信したので、VSインクリメント

これの繰り返し。15の次は0に戻る、というくらいか。

 

V(SA)とV(RA)はどこに出てくるのだろうか?
たぶん、FRMR PDUというエラー関係のPDU引数で使うように見える。
V(SA)は、自分がI PDUを送信するかどうかの判断でも使っているみたい。
まあ・・・後回しにしよう。


ずっとやっているけど、実装量はそんなに多くないのだ。
ログを出すための実装がけっこうある。
よくわからずにやっているためか、無駄な実装が多い気がする。
InitiatorとTargetで別の実装にしてしまったけど、ここを抽象化できれば一本化できるんじゃなかろうかね。

なんで別にしているかというと、Initiator系の場合は1コマンドで送信→受信だけど、Target系の場合は受信で1コマンド、送信で1コマンドとなっているため。
この見方は、R/Wから応答が返ってくるまでを1シーケンスとして実装しているからであって、普通に「応答が来るまで別のことをする」とやれば、抽象化とかなんとかいわずにできるのだ。

なんで応答が返ってくるまで1シーケンスにしてるかというと、InitiatorとしてR/Wを使っている分にはそっちの方が扱いやすいからだ。
送信と応答を別にするとなると、R/W制御を別のコンテキストにして、スレッドなり割込なりで回すようにせんといかん。
スレッドにするためにはOSの力を使わんといかんし、割込にするとPCでの実装がやりづらい。
抽象化するなら、この「非同期化」の部分だな。

メッセージループでぐるぐるとメッセージが来るまで待つようにしておけば、PCでもマイコンでも同じような実装ができそうだけど、ぐるぐる待つと電気喰うから割込が来るまで寝るようにして・・・とか考えるとめんどくさい。

でもまあ、そういうのをちゃんと設計できないと、ソフト専門とはいえ組込み屋さんとしてはいかんなぁ。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。

注: コメントを投稿できるのは、このブログのメンバーだけです。