2022年2月16日水曜日

【Android USB oscilloscope】(16) AOA --- Android~Linux hostとのUSB BULK通信 (化物)

余っているAndroidスマホをオシロにしよう!

スマホでこんなUSB接続をしたい.
     Linux(host) ----> スマホ(device)

スマホをPCに接続するとdialogが出て、ストレージ接続しますか? カメラ接続しますか? 充電だけですか? などと用途を訊かれるのはお馴染みだろう.こちらに書いた.

ところが、スマホをオシロにしたいわたしは、ストレージでもカメラでもない「別の何かのAndroidデバイス」に化ける必要がある.

化物Androidになる方法がちょっと特殊だ.Android open Accessory(AOA) libraryを使ってhostと接続する仕組みがgoogleから提供されている.
AOAがLinux hostと繋がるためには、一度USB接続して、一旦切断して別のVID/PIDでAndroidを再起動させ、二度目のUSB接続で化物Androidになるのである.

前回、AOAの化物接続まではできた.

以上が前回までの経緯だった.

今回のnew itemは2つ
  1)前回参考にしたpc-usb.cを書き換えてhost側で走らせる
  2)スマホで走らせるkotlinアプリ
これらでUSB bulk転送を実験する.

動かし方
1)スマホをPC(Linux)に挿す
2)hostプログラムを実行する  (スマホ画面にアクセス許可ダイアログが出るかも)
3)スマホアプリを起動する
4)スマホアプリのボタンを押すと表示が変わる

【hostのプログラム】
書き換えたものをこちらに置いとく.(2022.4.29 少し修正しました)
コンパイルはLinuxでこんなおまじないで通ると思う.
 gcc -lusb-1.0 pc-usb.c -o pc-usb `pkg-config libusb-1.0 --libs --cflags`
なお、あらかじめlibusbをインストしておく必要があるのを注意のほどよろしく.
 apt-get install libusb-1.0-0-dev

sourceの最初の方に、userが書き換えるべきパラメータがある.

bangflataoatestの文字は、化物Androidの識別に使うためのおまじないである.Android Studio上のaccessory_filter.xmlに同じ文字を記入しておく必要がある.
#define MANUFACTURER "bangflat"
#define MODEL "aoatest"

EP番号はスマホのdescriptorから動的に決定するのが作法だけど、ここでは決め打ちにしてる.あらかじめこちらでdescriptorを調査した.スマホによって83だったり01だったりするのでスマホに合わせて変更する必要があります.
#define IN 0x81    // end point in
#define OUT 0x02   // end point out

現在使っているスマホのADB debug mode時のVID/PID.事前調査で知った情報.1度目接続ではこの値である.スマホが変わればPIDは違うと思う.
#define VID 0x18D1 // VID
#define PID 0x4EE7 // PID 1st phase

化物Android接続ではVID/PIDが変わる.AOA仕様のPIDは2D01または2D00と決まっている(と思って差し支えないと言っても過言ではないと思う)
#define ACCESSORY_VID 0x18D1  // VID 2nd phase
#define ACCESSORY_PID 0x2D01  // PID 2nd phase
#define ACCESSORY_PID_ALT 0x2D00  // PID 2nd phase alternative

userが書き換えるべきパラメータは以上.

次にmain()のキモの部分を見てゆく.

最初に、化物Android接続を試みる.成功すればBULK通信へjumpする.
handle = libusb_open_device_with_vid_pid(NULL, ACCESSORY_VIDACCESSORY_PID);

NULLだったら化物Androidではないので、通常の(1度目の)VID/PIDで接続を試みる.
if(handle == NULL)
   handle = libusb_open_device_with_vid_pid(NULL, VID, PID);

1度目の接続に成功したら、化物Android接続への移行を試みる.
if( setupAccessory() < 0)  { エラー処理は略 }

化物Androidに成れたら、、、BULK通信の開始だ!
while(1){
 sprintf(buffer,"bulk transfer test %d----------",N++);
 if(libusb_bulk_transfer(handle, OUT, buffer, strlen(buffer),\ 
        &transferred, 0) < 0) {}
}
やってる事は、文字列をチロチロとBULK送信するloopでしかない.

main()はこれだけで終了.

なのだが、1度目接続→化物移行させるsetupAccessory()が混迷している.見ていこう.

スマホがAOAをサポートしているかどうかを、USBのcontrol transferで確認する.
libusb_control_transfer( handle, 0xC0, 51, 0, 0, buf, 2, 0)
control transferはEP0を使って行われ、主にUSB制御に使われる.
C0,51,0,0,2,0 という謎の数列は、AOA独自のコマンドだと思われる.AOAをサポートしているなら2byteで返信せよ、とでも言いたいのだろう.
buf[]に入った返信が非ゼロならAOAサポートしているとの判断をする.

さらにAOA独自の謎コマンドが続く.
bangflat,aoatestのような文字列を照合することによって、hostプログラムと、化物Androidのペアリングを検証している.
6行あるのは6種類の情報でもって化物チェックするためだが、実際にやってみると2つしか照合してなく見える.なので4つは”  ”を送ってるだけで済ませた.
libusb_control_transfer(handle,0x40,52,0,0,MANUFACTURER,\
                               strlen(MANUFACTURER),0)
libusb_control_transfer(handle,0x40,52,0,1,MODEL,strlen(MODEL),0)
libusb_control_transfer(handle,0x40,52,0,2," ",1,0)
libusb_control_transfer(handle,0x40,52,0,3," ",1,0)
libusb_control_transfer(handle,0x40,52,0,4," ",1,0)
libusb_control_transfer(handle,0x40,52,0,5," ",1,0)

とりあえず6種の照合データを送信して、結果は知らないままで、スマホに次の命令を送る.命令:化物になって再起動せよ!
libusb_control_transfer(handle,0x40,53,0,0,NULL,0,0)

化物Androidが再接続してくる事を期して、hostは一旦USB接続を切断する.
libusb_close(handle); // close 1st phase handler

化物AndroidのためのVID/PIDで接続を試みる.5回やる.
handle = libusb_open_device_with_vid_pid(NULL, ACCESSORY_VID, ACCESSORY_PID);

化物Androidが繋がったら、Interface0をセレクト.
libusb_claim_interface(handle, 0)
  
以上で化物Androidとの通信ができるようになった.BULK通信も可能なはずだ.


【スマホのアプリ】
readボタンを押す度にBULK受信し、受信内容が表示されるというもの.
project folder詰め合わせ(Kotlin)をこちらに置いとく.使っているAndroid Studioのversionはこれ.
元ネタはこれで、Kotlinに移植して少し弄ったもの.あんまり読んでないので理解してない.     AndroidとPCのUSB通信のサンプル

MainActivity.ktのonCreate()で、ボタンが押されたらUSBをrecieve()して表示するだけ.
IDreadBTN.setOnClickListener {
    try {
        val rmsg: String = usbDriver!!.receive()
        IDreadTXT.setText(rmsg)
    }
}
これのキモの部分はusbDriver.receive()ですが、定義はこうなってる.usbInが気になる.
fun receive(): String {
    val buff = ByteArray(1024)
    val len = usbIn!!.read(buff)
    return String(buff)
}
usbInの正体はこうなってる.つまりOS直結部分ではusbInはただのfile streamになっていて、usbIn.readすればシーケンシャルに受信dataがポロポロと吐き出されるみたいだ.ならばこれを呼び出すのは誰なのか?
fun openAccessory(accessory: UsbAccessory?) {
    fileDescriptor = usbManager.openAccessory(accessory)
    val fd = fileDescriptor!!.fileDescriptor
    usbIn = FileInputStream(fd)
}
openAccessory()を呼び出すのは、元を辿ればonReceive()だ.つまり、USBから受信した時、Intentの中にどのUsbAccessoryなのかの情報が含まれており、それをopenAccessory()に与えることでusbInに紐づける仕組み.
fun onReceive(context: Context, intent: Intent) {
    open(intent)
}
fun open(intent: Intent) {
    val accessory = intent...... as UsbAccessory?
    driver.openAccessory(accessory)
}

判ってしまえばそんなもんかだけど、こんなのスクラッチから書けるわけがないわー

ーーーー
ともあれこれで、hostから任意の波形データをスマホに送信するルートは確保できた.

次は波形を描画させてみるかね?

15へ            17へ

かしこ

25 件のコメント:

  1. すごいです〜。
    こんなの自分には絶対乗り越えられない。ネットで検索して2年以内の記事がないと先ず諦めちゃいます。USBもナゾが多すぎて他の経路に逃げちゃいます。
    androidでUSB通信についてここまで丁寧に書かれている記事は他に無いかも。

    返信削除
    返信
    1. 近頃はヒマさえあればこれやってます.

      AOAは古いです.いつまで持つのかAOA.

      USB関連情報ってギルドの内輪には情報が溜まってるけど、書籍になるほどのニーズは無く、同志諸君がnetに散逸させた情報しかないみたいです.

      sourceをgitに上げる勇気はないです.

      削除
    2. 昔USBが普及し始めたころにICEとPCの接続も少しづつ専用I/FからUSBに移行し始めました。そのUSBドライバのインストールが曲者で、担当者ごとにうまくいったりいかなかったり混乱しました。自分のPCではPC自体がハングして起動しなくなりました。京都マイコンというICEの会社に電話して対処を聞いたのですが、USBドライバが自社製ではなくイスラエル製とかで対応に2週間はかかるとか言われました。USBはドライバの中でもかなり特殊で、カーネルっぽいところに直接組み込まないと動かないんですよと真面目そうなエンジニアの人から説明されました。当時国内にはそのドライバかける人いないっぽかったです。
      そのあたりからUSBってなんて怖いんだとPTSD気味に刷り込まれました。平坂氏のUSBでオーディオデータを送る記事も読んでいて、まじかーとつぶやいていました。

      削除
    3. AOAのducumentはもうnetのセカイに存在しないんです.オロオロ....

      USBオーディオで転送レートをfeedback制御する仕組みはカーネル近傍でやってる臭いがします.packetを500byteにするか499byteにするかみたいな事をhostがやってる.

      USBオーディオの投稿INDEXはチロチロと読みに来てる人々が世界中からいるようです.人数は多くないけどw

      削除
    4. hostからバイナリのsin wave dataを送信して、スマホで受信してるんだけど、その500byteほどのバイナリをbyteにバラしてintに変換して画面に文字で表示するのが何故かできないという.JAVAのめんどくささよ.

      matlabみたいなベクトル数値処理言語だと、scalingとか内積とか一発で計算できるんで大好きなんだけど、JAVAってそうゆうの全然できない.あぐー

      削除
    5. GoogleとしてはAndoroidをスマホ以外の用途に使ってほしくないんですかねー。
      もう少し力入れてほしいかも。

      削除
    6. 確かにJAVAって小回り効かないイメージが。
      PHPでサイトのロジックを書いていた人にはゴシゴシ書いていた人には福音だったでしょうけど、組み込みには向かないような気が。読みにくいし。

      削除
    7. いろいろとやってみて失敗してやる気なくして撤退をたくさんやってる気がします.

      Android Oneはいかにも売れそうにない.
      Android Goって途上国向けなんですかね?流行りそうにない.
      chromeベースのweb serviceもいろいろと出ては消えてる.

      削除
    8. AOAって古くはAudio IFも在ったのだけど、途中でAudio機能は削除されました.

      削除
  2. >AOAがLinux hostと繋がるためには、一度USB接続して、一旦切断して別のVID/PIDでAndroidを再起動させ、二度目のUSB接続で化物Androidになるのである.
    某スマホ用「RF band tweak soft」(RF系チップセットを直に弄って、海外スマホを「日本国内専用バンド」も、使えるようにしてしまう謎ソフト)が、まさにこういう動きをしていて、今思えばこれが「AOA仕様」だったのですね。
    ※確か、Android Debug Port(内部的には、シリアルポート扱い(COMなんとかが割り当てられる)だったような)が、一瞬出現してその後に「本物のポート」が、現れるように見えた。
    最初、「壊れたのか?」(動的に VID/PID を変えるなんて知らなかったから)と思ってました。
    (USBは、破壊モードよっては VID/PID が変わったりするので。)

    ※ちなみに私は、一応この本
    https://www.amazon.co.jp/USB%E3%83%8F%E3%83%BC%E3%83%89-%E3%82%BD%E3%83%95%E3%83%88%E9%96%8B%E7%99%BA%E3%81%AE%E3%81%99%E3%81%B9%E3%81%A6%E2%80%95USB%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9%E3%81%8B%E3%82%89Windows-Linux%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90%E3%81%AE%E4%BD%9C%E6%88%90%E3%81%BE%E3%81%A7-TECHI-Vol-8/dp/4789833194
    USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで

    を持ってるんですが、古すぎて、最近の動向には付いていけませんね・・・
    (この本は、USB2.0が出たか出ないか位の頃で止まってます。最近の USB3以上は、もはやUSBですらない(事実上 PCI Express/Thunder Bolt の親戚になってる)ので、もうワケわかめです。)

    返信削除
    返信
    1. その本はわたしも持ってます.古いっ.内容も古いっ.記事のICも古い.

      >一瞬出現してその後に「本物のポート」が、現れるように見えた

      会社だとUSBプロトコルアナライザとかちゃんとした機材を持ってるでしょうから、切り替わりが見えてしまうのではないでしょうか.うらやま~

      USB3も面白そうなので少し調べたけど、コントローラICが高価なのでプリント基板作って遊ぶにはキツい感じでした.

      EZ-USBのライタ接続時に化物切り替えをするので2度目の経験でした.

      削除
    2. >USBプロトコルアナライザ
      仕事でUSB関連はやったことがないので、私も無縁です。
      ↑のは、Windows のデバイスマネージャ上で、ホントに
      「目の前で」表示が変わっていきます。まるで魔法のようにw

      削除
    3. それは何が起きてるのかを解明するためにビデオで撮影してコマ送りで確認したくなります.
      履歴はsystem logに残ってるらしいですけどwinのlogって見づらくてなぁ.

      削除
  3. >USB関連情報ってギルドの内輪には情報が溜まってるけど
    個人的には、何故か「Windows系」のUSB関連情報よりも「Linux系」のほうが、USB関連情報が充実してるような気がします。関連コマンドも豊富だし。
    ※さすがは「OpenSourceの王者」たる Linuxです。デバイスドライバのソースが見れるのはLinux位です。windowsは、特殊デバイスのドライバソースなんて殆ど見られないので、ハードルが高い。
    例えば、「素性不明」のUSBデバイスの VID/PID が知りたいときは、Linuxだと、とりあえずどこかに刺して、
    lsusb
    とやれば、一発で分かります。
    ・Windowsでも調べられなくないが、ちょっと面倒。(というか場合にっては、「勝手にドライバがインストール」されて、取り返しがつかなくなることがあるし・・・)

    返信削除
    返信
    1. lsusb -s 1:8 -v
      だったか、descriptorを見るのによくお世話になってます.

      USBなんとかクラスの公開情報に則った実装ならいいんですけど、user独自実装だと手が付けられないですね.AOAの元のdocumentをgoogleが削除しちゃったみたいでプアな情報しかなく、将来に禍根ってやつです.

      削除
  4. >JAVAのめんどくささよ.
    Javaはそもそも、言語的には「最小限」のことしか定義されていないので、何か「まとまった」ことをやらせようとすると、
    ・ロジックをゴリゴリ書く
    ・どこかから、使えそうな Class / Library を拾ってくる。あるいは自分で作る
    しかありません。
    ※そういう意味では、C/C++ と同じで「高級アセンブラ」でしかない。
    元々は「組込み」を主眼に置いていたので、逆にこうなったのでしょうかね?

    言語自体の「拡張仕様」も定義されてないので、
    ・matlabみたいなベクトル数値処理
    とかも「Java」である限りは、
    ・どこかから、使えそうな Class / Library を拾ってくる。あるいは自分で作る
    しかありません。
    (言語自体に「ベクトル」という概念がない。構造体かクラスにするしかない。)
    なので、
    「scalingとか内積とか一発で計算」
    したければ、「Javaに似て非なる、新しい言語」を、自分で作るしかありません。
    (なので、Kotlinもそうですが、そういう言語が乱立している。そういう意味では、C#も「拡張Java」とも言えなくはない。文法がかなり近いし。)

    返信削除
    返信
    1. ListやArrayがいろいろあるので、きっと便利なんだろうと思ったのがぬか喜びでした.ベクトル演算パッケージをimportしたいです.探してないけど.あるのかどうか知らないけど.

      >元々は「組込み」を主眼に置いて

      あぁなるほど、それはありそうですね.組み込み用途にベクトル演算なんか真っ先に除外したくなりますもんね.計算重いし電気喰うし.

      AIだの機械学習だのでモバイルやIoTで行列演算が欲しがられるようになったのってつい最近のことですからなぁ.

      削除
  5. >GoogleとしてはAndoroidをスマホ以外の用途に使ってほしくない
    前に自分も書いたような気がしますが、現状を見ると、
    「そうとしか思えない」
    ですね。
    ・しかも最近Googleは、Andoroidが「あまりにも肥大化してきて」収拾がつかなくなりつつあるので、これを捨てて「別言語を作成中」と、言うウワサもあります。

    返信削除
    返信
    1. >別言語を作成中

      書籍のライターさんが儲かってよござんす

      削除
  6. >PHPでサイトのロジックを書いていた人にはゴシゴシ書いていた人には福音
    実はそんなことはなくて、むしろ、PHPとかのが全然書きやすいです。
    ・Javaは、手続き(おまじない)が多すぎて、なかなか、「肝心のロジック本体」が、書けない感じがします。言語仕様として、必ず「Class」でなければいけないので、いきなり、
    println("hoge.");
    とか、書けません。実はPHPは、この辺アバウトなので、いきなり、
    echo "Hoge."
    とか、書けたりします。あと、最近のPHPは、「組み込みライブラリ」が豊富なので、事前定義なしに色んなことが結構出来ちゃいます。
    (その代わり、メモリとか食うようになったが。)

    返信削除
    返信
    1. >手続き(おまじない)が多すぎて

      それはVHDLがめんどくさすぎてやる気がしないけど、verilogはイキナリ書けるのと似てる.

      あと、PHPってそうゆう雑誌を思い出しちゃういつもいつもー

      削除
    2. >それはVHDLがめんどくさすぎてやる気がしないけど、verilogはイキナリ書けるのと似てる.
      へー、そういう違いがあるんだ。知りませんでした。
      ※そろそろ、何かHDL書けるようになりたいな、と思ってもう数十年経ってしまいました。今年こそは・・・始めたいな。

      削除
    3. FPGAはいまちょっと値上がりしてますけど、90年代には想像もつかないくらいなんでも入ってしまうほど巨大化して値段も安くなって、お手軽になりました.

      VHDLやverilogで記述できてsimulationもできるけど、その論理は回路合成できません、という地雷みたいなのがあるのでそこが気持ち悪いかもです.

      あと、code上でインスタンスすると現実に回路が焼かれて勝手に動き始めます.ある意味で究極の並列処理ともいえるかも.

      削除
  7. VHDLとVerilogのどちらが好きかと聞かれたら、自分は「VHDLです」と答えます。

    確かにすべての論理を書く必要があるVHDLに比べると、必要な分だけ書いたらOK!というVerilogは楽なような気がします。
    でも、書いていない部分の動きはどうなるかは運しだい。
    Verilogはデバッグで苦しむ、VHDLはすべてのパターンを考えるのが大変で苦しむような気がします。

    最近はツールは無料、ボードは評価ボードが格安で入手できるので、Lチカだったらすぐにお試しできます。
    良い時代になりました。

    いつやるか?今でしょう!(笑)

    返信削除
    返信
    1. 産みの苦しみVHDL、育てる苦しみverilog

      HDLは新しいのがあまり出てきませんね.systemCはまだ生きてるんでしょうか?

      削除