2011年5月24日火曜日

Android Open Accessory Development Kit

Android Open Accessory Development Kit

Android 3.1 プラットフォーム (そして、バックポートされた Andorid 2.3.4) で Android Open Accessory がサポートされるようになりました。これにより外部の USB ハードウェア(Android USB accessory)は、"accessory" モードの Android-powered デバイスとやりとりできるようになりました。Android-powered デバイスが accessory モードのとき、接続された機器は USB ホストとして振る舞い(バスに電力を供給しデバイスを列挙する)、Android-powered デバイスは USB デバイスとして振る舞います。Andorid USB accessory は Android-powered device に取り付けるために特別にデザインされており、accessory モードをサポートした Android-powered デバイスを検出するためのシンプルなプロトコル(Android accessory protocol)が取り付けられています。また、accessory は電力をチャージするために 5V で 500mA を提供しなければなりません。すでにリリースされている多くの Android-powered デバイスは USB デバイスとして振舞うことだけが可能で、外部の USB デバイスと接続を開始することはできません。Android Open Accessory はこの制約を克服し、accessory が接続を開始できるようにすることで、さまざまな種類の Android-powered デバイスとやりとりできる accessory を作成することができます。

注意 : accessory モードは最終的にはデバイスのハードウェアに依存し、全てのデバイスが accessory モードをサポートするわけではありません。アプリケーションの Android マニフェストで <uses-feature> エレメントを使うことで、accessory をサポートするデバイスかどうかフィルターすることができます。より詳しい情報は、USB Accessory Developer Guide を見てください。

次のリストは現在 Andorid Open Accessory と互換性のある開発ボードを扱っている代理店です。

  • RT Corp は Android ADK ボードデザインに基づいた Arduino-compatible board を提供しています。
  • Microchip は A PIC based USB microcontroller board を提供しています。
  • DIY Drones は RC (radio controlled) と UAV (unmanned aerial vehicle) と連動した Arduino-compatible board を提供しています。
  • Modem Device は ADK firmware をサポートした Arduino-compatible board を提供しています。

我々はより多くのハードウェア代理店が多種多様なキットを作成すると予測しています。ですから、将来の開発のためにも、このまま注目していてください。

---

ADK Components


Android Open Accessory Development Kit (ADK) は Arduino open source electronics prototyping platform, accessory のハードウェアデザインファイル, accessory のファームウェアを実装したコード, accessory とやりとりする Android アプリケーションに基づいた Android USB accessory の実装を提供します。ハードウェアデザインファイルとファームウェアコードは ADK package download に含まれています。

ADK に含まれている主なハードウェアとソフトウェアコンポーネントは:

  • Arduino Mega2560 と Circuits@Home USB Host Shield design (現在 ADK board と呼ばれる) に基づいた USB micro-controller board。これは後で Android USB accessory として実装されます。ADK board は、"shields" と呼ばれるアタッチメントで使うことができる入力ピンと出力ピンを提供します。カスタムファームウェアは C++ で書かれており、ボードの機能とアタッチメントシールドと Android-powered デバイス間のやりとりを定義するためにインストールされています。ボード用のハードウェアデザインファイルは hardware/ ディレクトリに配置されています。

  • ADK board 上に乗っている Android Demo Shield(ADK shield) はボードの入力と出力のポイントを実装しています。これらの実装には、ジョイスティック、LED 出力、温度と光度のセンサーが含まれています。あなたは、自分用のシールド買ったり作ったり、ADK に独自の配線をして機能をカスタマイズすることができます。シールド用のハードウェアデザインファイルは hardware/ に配置されています。

  • Arduino USB Host Shield ライブラリを基にしたライブラリは USB Host として振舞うための USB micro-controller board のロジックを提供します。これにより、ボードは USB デバイスとのトランザクションを開始することができます。このライブラリ全体の使い方の説明はこのドキュメントのスコープ外です。必要に応じて、このライブラリとの重要なやりとりを取り上げます。より詳しい情報は、firmware/arduino_libs/USB_Host_Shield ディレクトリ内の Arduino USB Host Shield library のソースコードを見てください。

  • firmware/demokit/demokit.pde の Arduino sketch にはADK board 上で動く firmware が C++ で書かれています。sketch は Android-powered デバイスとやりとりするために Android accessory protocol library を呼びます。また、ADK board とシールドからのデータを Android アプリケーションに送り、Android アプリケーションから受け取ったデータを ADK board とシールドにアウトプットします。

  • Android accessory protocol library は firmware/arduino_libs/AndroidAccessory ディレクトリに配置されています。このライブラリは、どのようにバスを列挙し、accessory mode をサポートする Android-powered デバイスに接続し、デバイスとの接続をセットアップするかを定義しています。

  • ADK board の機能をサポートする他の 3rd party ライブラリ:



---

Getting Started with the ADK


以下のセクションでは、PC 上 に Arduino software をインストールし、Arduino software を使って ADK board に ファームウェアを書き込み、ADK board 用の Android アプリケーションと一緒に動かす方法を説明します。始める前に、開発環境をセットアップするため、次のアイテムをダウンロードしてください。

  • Arduino Software : ライブラリおよびコーディングと ADK board にファームウェアをインストールするための IDE が含まれる
  • CapSence library : 人間の静電容量を感知するためのライブラリが含まれる。これは Android シールドに配置されている静電容量ボタンの為に必要
  • The ADK package : ADK board 用のファームウェアと ADK board とシールド用の hardware design files が含まれる



Installing the Arduino software and necessary libraries

Arduino software をインストールするには:

  • 1. Arduino website で説明されているように Arduino Software をダウンロードしてインストールします。
    注意: もし、Mac 上で使うなら、インストール手順ではそう言っていなくても、Arduino package に含まれている FTDI USB Serial Driver をインストールしてください。

  • 2. ADK package をダウンロードし、任意の場所に展開してください。app, firmware, hardware ディレクトリが含まれているはずです。

  • 3. CapSence をダウンロードし、任意の場所に展開します。

  • 4. 必要なライブラリをインストールします。

    On Windows:

    • firmware/arduino_libs/AndroidAccessoryfirmware/arduino_libs/USB_Host_Shield ディレクトリ (ディレクトリごと、含まれるファイルだけではなく) を <arduino_installation_root>/libraries/ ディレクトリにコピーする
    • <arduino_installation_root>/libraries/ ディレクトリに CapSense ディレクトリを作る
    • ダウンロードし展開した CapSence から CapSense.cppCapSense.h を上記の CapSense ディレクトリにコピーする


    On Mac:

    • Finder で Arduino アプリケーションを右クリックし、Show Package Contents を選択する
    • firmware/arduino_libs/AndroidAccessoryfirmware/arduino_libs/USB_Host_Shield ディレクトリ (ディレクトリごと、含まれるファイルだけではなく) を Arduino アプリケーション内の Contents/Resources/Java/libraries ディレクトリにコピーする
    • Contents/Resources/Java/libraries ディレクトリに CapSense ディレクトリを作る
    • ダウンロードし展開した CapSence から CapSense.cppCapSense.h を上記の CapSense ディレクトリにコピーする


    On Linux (Ubuntu):

    • firmware/arduino_libs/AndroidAccessoryfirmware/arduino_libs/USB_Host_Shield ディレクトリ (ディレクトリごと、含まれるファイルだけではなく) を <arduino_installation_root>/libraries/ ディレクトリにコピーする
    • <arduino_installation_root>/libraries/ ディレクトリに CapSense ディレクトリを作る
    • ダウンロードし展開した CapSence から CapSense.cppCapSense.h を上記の CapSense ディレクトリにコピーする
    • shell prompt で sudo apt-get install avr-libc と入力して、avr-libc library をインストールする



AndroidAccessory, USB_Host_Shield, CapSense の3つの新しいディレクトリが Arduino libraries ディレクトリに出来ているはずです。


Installing the firmware to the ADK board

ADK board にファームウェアをインストールするには:

  • 1. ADK board を micro-USB port を使ってあなたのコンピュータに接続する。これにより、双方向通信ができるようになり、さらに ADK board に電力が供給される。

  • 2. Arduino を起動する

  • 3. Tools > Board > Arduino Mega 2560 をクリックして ADK board のタイプを指定する

  • 4. 適切な USB port を選択する:

    • On Windows : Tools > Serial Port > COM# をクリックして通信ポートを指定する。COM ポート番号はあなたのコンピュータによって変わる。COM1 は通常はシリアルポート接続用に予約されている。COM2 もしくは COM3 になることが多い。

    • On Mac : Tools > Serial Port > dev/tty.usbserial-### をクリックし通信ポートを指定する。

    • On Linux(Ubuntu) : Tools > Serial Port > dev/ttyUSB# をクリックして通信ポートを指定する。

  • 5. File > Open をクリックして、ファームウェアコード (sketch) を開き、firmware/demokit/demokit.pde を選択する。

  • 6. Sketch > Compile/Verify をクリックして、sketch にエラーがないことを確かめる。

  • 7. File > Upload to I/O Board を選択する。board が Android-powered デバイスと通信する準備が整ったら、Arduino は Done uploading を出力する。



Running the DemoKit Android application

DemoKit Android アプリケーションはあなたの Android-powered デバイス上で動き、ADK board と通信します。ADK board は board 上の LED ライトを光らせろ、というようなコマンドを受信したり、ジョイスティックの動きや温度データなどを board から送ったりします。

アプリケーションを Eclipse にインストールして実行するには:

  • 1. accesory モードをサポートする 2.3.4 デバイス用の Open Accessory library を含む Google APIs API Level 10 add-on library をインストールする。
    このライブラリは accessory mode をサポートする Android 3.1 以上のデバイスと前方互換性があります。もし、Android 3.1 以上のデバイスのみ対象にするなら、API Level 12 だけで十分です。どちらの API レベルを使うか決めるためのより詳しい情報は USB Accessory ドキュメントを見てください。

  • 2. File > New > Project... をクリックし、次に Android > Android Project を選択します。

  • 3. Project name: に DemoKit と入力します。

  • 4. Create project from existing source を選択し、Browse をクリックし、app ディレクトリを選択して Finish をクリックします。

  • 5. Build Target に Google APIs(Platform 2.3.3, API Level 10) を選択します。

    注意: add-on が 2.3.3 とラベルされていても、最新の Google API add-on library for API level 10 には、2.3.4 デバイスに対する USB Open Accessory API サポートが追加されています。

  • 6. Finish をクリックします。

  • 7. アプリケーションをあなたのデバイスにインストールします。

  • 8. ADK board (USB-A) をあなたの Android-powered デバイス (micro-USB) に接続します。accessory への電源ケーブルが刺さっているか、もしくは accessory の micro-USB ポートが電力供給のためにあなたのコンピュータに接続されている(この場合は ADK board をモニターすることもできます。)ことを確認してください。接続したら、DemoKit アプリケーションが accessory に接続してもいいか尋ねるプロンプトが出るので、許可を選択します。もしプロンプトが出なかったら、accessory を接続もしくは再接続してください。


これで、LED カラーやサーボ(サーボが接続されている場合)のスライダーを動かしたり、リレーボタンを押したりして、ADK board とやりとりすることができます。ADK シールドでは、ボタンを押したり、ジョイスティックを動かして、それらの出力をアプリケーションで見ることができます。


Monitoring the ADK board

ADK ファームウェアは、あなたが自身の accessory を作成したいときに見るべきいくつかのファイルで構成されています。firmware/arduino_libs/AndroidAccessory ディレクトリ内のファイルは最も重要なファイルで、accessory モードをサポートした Android-powered デバイスを検出し接続するためのロジックが入っています。arduino_libraries_directory/AndroidAccessory ディレクトリ内のコードと firmware/demokit/demokit.pde sketch に自由にデバッグステートメント(Arduino の Serial.print() ステートメント) を追加して再アップロードし、どのようにファームウェアが動くか見ることができます。

Tools > Serial Monitor をクリックし、baud を 115200 に設定して Arduino Serial Monitor でデバッグステートメントを見ることができます。
次の、どのように accessory が Android-powered デバイスと通信するかのセクションでは、あなたが自分の accessory でするべきことについて説明します。


---

Implementing the Android Accessory Protocol


Android USB accessory には、Android-powered デバイスを検出して通信をセットアップする方法が定義された Android Accessory Protocol が取り付けられている必要があります。一般的には、accessory は次のステップを行います。

  • 1. 接続されたデバイスを検出するまで待つ
  • 2. デバイスが accessory モードをサポートしているかどうか確かめる
  • 3. 必要であれば accessory mode でデバイスを開始するよう試みる
  • 4. デバイスが Android accessory protocol をサポートしていたら、通信を確立する


次のセクションでは、これらのステップをどのように実装するかより詳しく見ていきます。

Wait for and detect connected devices

あなたの accessory に Android-powered device が接続されたことを定期的にチェックするロジックを組み込むようにしてください。デバイスが接続されたら、デバイスが accessory モードをサポートしているかどうか確かめるようにしてください。


Determine the device's accessory mode support

Android-powered デバイスが接続されたら、次の3つの状態のどれかになるでしょう。

  • a. 取り付けられたデバイスは Android accessory モードをサポートし、すでに accessory モードになっている
  • b. 取り付けられたデバイスは Android accessory モードをサポートしているが、accessory モードにはなっていない
  • c. 取り付けられたデバイスは Android accessory モードをサポートしていない


最初の接続の間に、accessory は接続されたデバイスの USB device descriptor の vendor ID と product ID をチェックするようにしてください。デバイスがすでに accessory モード (case A) であれば、vendor ID は Google's ID (0x18D1) と、product ID は 0x2D00 もしくは 0x2D01 と一致するはずです。そうであれば、accessory は自身の通信プロトコルでバルク転送エンドポイントを通して デバイスとの通信を確立することができます。ここでは、デバイスを accessory モードで開始する必要はありません。

注意: 0x2D00 は accessory モードをサポートする Android-powered デバイス用に予約されています。0x2D01 は ADB (Android Debug Bridge) protocol をサポートするデバイス用に予約されています。これは、ADB 用に2つのバルクエンドポイントを持つ2番目のエンドポイントを公開します。もしコンピュータで accessory のシミュレーションを行うなら、これらのエンドポイント使って accessory アプリケーションをデバッグすることができます。一般的には、デバイスの ADB へのパススルーが実装されている accessory 以外ではこのインタフェースは使用しないでください。

もし、vendor ID と product ID が一致しなかったら、状態 b と状態 c を区別する方法はありません。よって、accessory はデバイスがサポートされているかどうか確かめるために accessory モードでデバイスを開始するよう試みます。


Attempt to start the device in accessory mode

vendor ID と product ID が accessory mode の Android-powered デバイスと一致しない場合、accessory はデバイスが accessory モードをサポートしているがその状態ではないのか、それとも accessory モードをサポートしていないのか識別することができません。なぜなら、accessory モードをサポートしているがその状態ではないデバイスは、Android Openn Accessory 用の特別なものではなくメーカーの vendor ID と product ID を最初に報告するからです。いずれの場合においても、accessory はデバイスが accessory モードをサポートしているかどうか確かめるために、accessory mode でデバイスを開始するよう試みるべきです。次のステップはそれをどのように行うかを説明しています:

  • 1. 51 control request ("Get Protocol") を送ってデバイスが Android accessory protocol をサポートしているか確かめます。protocol がサポートされている場合、0 以外の値が返ってきます。返ってくる値はデバイスがサポートする protocol のバージョンを表します(現在は、version 1 だけ存在しています)。このリクエストは次のような特徴を持つエンドポイント0での control request です。

    requestType: USB_DIR_IN | USB_TYPE_VENDOR
    request: 51
    value: 0
    index: 0
    data: protocol version number (16 bits little endian sent from the device to the accessory)


  • 2. デバイスが適切なプロトコルバージョンを返したら、デバイスに識別文字列情報を送ります。この情報によって、デバイスはこの accessory に対する適切なアプリケーションを見つけることができ、また、もし適切なアプリケーションが存在しない場合にユーザーに URL を表示することができます。これらのリクエストは次のような特徴を持つエンドポイント0 (各 string ID に対して) での control request です。

    requestType: USB_DIR_OUT | USB_TYPE_VENDOR
    request: 52
    value: 0
    index: string ID
    data zero terminated UTF8 string sent from accessory to device

    次の string ID がサポートされています。各文字列に対して最大サイズは 256 bytes です。(\0 を使って 0 で終わる必要があります)

    manufacturer name: 0
    model name: 1
    description: 2
    version: 3
    URI: 4
    serial number: 5


  • 3. 識別文字列を送ったら、デバイスに accessory mode でスタートするようにリクエストします。このリクエストは次のような特徴を持つエンドポイント0での control request です。

    requestType: USB_DIR_OUT | USB_TYPE_VENDOR
    request: 53
    value: 0
    index: 0
    data: none



最後の contorl request を送った後、接続された USB デバイスが自身を accessory モードでバスに再紹介したら、accessory は接続されたデバイスを再列挙できるようになります。ここで determining the device's accessory mode support のアルゴリズムまで戻って、再び vendor ID と product ID をチェックします。デバイスが正しく accessory モードに移ったならば、vendor ID と product ID は最初のメーカーの値とは異なり、Google の vendor ID と product ID に一致するようになります。これで、accessory はデバイスとの通信を確立できるようになります。

これらのステップで失敗するということは、このデバイスは Android accessory モードをサポートしていないということであり、accessory は次のデバイスが接続されるまで待つようにしてください。


Establish communication with the device

Android-poweredデバイスが accessory モードとして検出されたら、accessory はデバイスとの通信に使用するバルクエンドポイントを取得するためにインタフェースとエンドポイントのディスクリプタを問い合せます。0x2D00 の product ID を持つ Android-powered デバイスは、入力と出力の2つの通信用バルクエンドポイントがあるインタフェースを1つ持ちます。0x2D01 の product ID を持つデバイスは、入力と出力の2つの通信用バルクエンドポイントがあるインタフェースを2つ持ちます。1つ目のインタフェースは標準の通信用、2つ目のインタフェースは ADB 通信用のインタフェースです。あるインタフェースで通信するためにすることは、最初のバルク入力・出力エンドポイントを見つけて、デバイスの設定を SET_CONFIGURATION (0x09) デバイスリクエストで 1 に設定することです。そうすれば、エンドポイントを使って通信できるようになります。

---

How the ADK board implements the Android Accessory protocol


次のセクションでは ADK board にインストールする firmware コードについて説明します。ファームウェアはどのように Android Accessory protocol を実装するかの実例を示しています。たとえあなたが ADK board と shild を持っていなくても、どのようにハードウェアがデバイスを検出し、accessory mode でやりとりするかを読むことは、自分の accessory 上にコードをポートしたい場合にとても役に立ちます。

ファームウェアの重要なピースは firmware/demokit/demokit.pde sketch にあります。これは Android-powered デバイスで動いている DemoKit アプリケーションにデータを送受信するコードです。Android-powered デバイスを検出して通信をセットアップするコードは firmware/arduino_libs/AndroidAccessory/AndroidAccessory.hfirmware/arduino_libs/AndroidAccessory/AndroidAccessory.cpp ファイルに含まれています。このコードには、あなたが自分の accessory のファームウェアに実装する助けになるほとんどのロジックが含まれています。次のセクションを通して読めるように、これら3つのファイルをテキストエディターで開いておくと便利でしょう。

次のセクションでは、Android Accessory Protocol の実装で説明したアルゴリズムの、コンテキスト内のファームウェアコードについて説明します。


Wait for and detect connected devices

ファームウェアコード (demokit.pde) 内の loop() 関数はファームウェアが実行されている間繰り返し呼び出されます。この loop() 関数内で AndroidAccessory::isConnected() を呼ぶことでデバイスが接続されているか定期的にチェックしています。接続されているデバイスがある場合、継続的に board とアプリケーションへの/からの入力と出力のストリームをアップデートしています。なにも接続されていない場合は、デバイスが接続されているか継続的にチェックします。:

  1. ...  
  2.   
  3. AndroidAccessory acc("Google, Inc.",  
  4.                      "DemoKit",  
  5.                      "DemoKit Arduino Board",  
  6.                      "1.0",  
  7.                      "http://www.android.com",  
  8.                      "0000000012345678");  
  9.   
  10. ...  
  11. void loop()  
  12. {  
  13. ...  
  14.     if (acc.isConnected()) {  
  15.         //communicate with Android application  
  16.     }  
  17.     else{  
  18.         //set the accessory to its default state  
  19.     }  
  20. ...  
  21. }  



Determine the connected device's accessory mode support

デバイスが ADK board に接続されたとき、そのデバイスがすでに accessory mode になっている場合と、accessory mode をサポートしてるがその状態ではない場合と、accessory mode をサポートしていない場合があります。AndroidAccessory::isConnected() メソッドはこれらのケースをチェックし、loop() 関数から呼ばれたらチェック結果を返します。この関数では最初に、すでに接続されているがまだ処理されていないデバイスかどうかチェックしています。そうでない場合は、接続されているデバイスのデバイスディスクリプタを取得し、それを使って AndroidAccessory::isAccessoryDevice() を呼んでデバイスがすでに accessory モードかどうか確かめます。このメソッドはデバイスディスクリプタの vendor ID と product ID をチェックします。デバイスが accessory モードであれば、vendor ID は 0x18D1, product ID は 0x2D00 もしくは 0x2D01 になります。デバイスが accessory モードの場合は、ADK board はデバイスとの通信を確立することができます。そうでなければ、board は accessory mode でデバイスを開始するように試みます。

  1. bool AndroidAccessory::isConnected(void)  
  2. {  
  3.     USB_DEVICE_DESCRIPTOR *devDesc = (USB_DEVICE_DESCRIPTOR *) descBuff;  
  4.     byte err;  
  5.   
  6.     max.Task();  
  7.     usb.Task();  
  8.   
  9.     if (!connected &&  
  10.         usb.getUsbTaskState() >= USB_STATE_CONFIGURING &&  
  11.         usb.getUsbTaskState() != USB_STATE_RUNNING) {  
  12.         Serial.print("\nDevice addressed... ");  
  13.         Serial.print("Requesting device descriptor.");  
  14.   
  15.         err = usb.getDevDescr(1, 0, 0x12, (char *) devDesc);  
  16.         if (err) {  
  17.             Serial.print("\nDevice descriptor cannot be retrieved. Program Halted\n");  
  18.             while(1);  
  19.         }  
  20.   
  21.         if (isAccessoryDevice(devDesc)) {  
  22.             Serial.print("found android accessory device\n");  
  23.   
  24.             connected = configureAndroid();  
  25.         } else {  
  26.             Serial.print("found possible device. switching to serial mode\n");  
  27.             switchDevice(1);  
  28.         }  
  29.     } else if (usb.getUsbTaskState() == USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) {  
  30.         connected = false;  
  31.     }  
  32.   
  33.     return connected;  
  34. }  



Attempt to start the device in accessory mode

デバイスがまだ accessory モードになっていない場合、ADK board はデバイスが accessory モードをサポートしているかどうか確かめなければなりません。そのために、control request 51 を送ってデバイスがサポートする USB accessory protocol のバージョンをチェックします(AndroidAccessory::getProtocol()を参照)。現在ではプロトコルのバージョンは 1 だけですが、将来的には 0 以上の値になります。適切なプロトコルバージョンが返ってきたら、board は control request 52 を送って(各文字列に対して AndroidAccessory:sendString())自身の識別情報を送信し、control request 53 でデバイスを accessory mode で開始するよう試みます。この処理は AndroidAccessory::switchDevice() メソッドで行われます。

  1. #define ACCESSORY_GET_PROTOCOL    51  
  2. #define ACCESSORY_SEND_STRING     52  
  3. #define ACCESSORY_START           53  
  4. ...  
  5.   
  6. bool AndroidAccessory::switchDevice(byte addr)  
  7. {  
  8.     int protocol = getProtocol(addr);  
  9.     if (protocol == 1) {  
  10.         Serial.print("device supports protocol 1\n");  
  11.     } else {  
  12.         Serial.print("could not read device protocol version\n");  
  13.         return false;  
  14.     }  
  15.   
  16.     sendString(addr, ACCESSORY_STRING_MANUFACTURER, manufacturer);  
  17.     sendString(addr, ACCESSORY_STRING_MODEL, model);  
  18.     sendString(addr, ACCESSORY_STRING_DESCRIPTION, description);  
  19.     sendString(addr, ACCESSORY_STRING_VERSION, version);  
  20.     sendString(addr, ACCESSORY_STRING_URI, uri);  
  21.     sendString(addr, ACCESSORY_STRING_SERIAL, serial);  
  22.   
  23.     usb.ctrlReq(addr, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,  
  24.                 ACCESSORY_START, 0, 0, 0, 0, NULL);  
  25.     return true;  
  26. }  
  27.   
  28. void AndroidAccessory::getProtocol(byte addr)  
  29. {  
  30.     uint16_t protocol = -1;  
  31.  usb.ctrlReq(addr, 0,  
  32.              USB_SETUP_HOST_TO_DEVICE |  
  33.     USB_SETUP_TYPE_VENDOR |  
  34.     USB_SETUP_RECIPIENT_DEVICE,  
  35.     ACCESSORY_GET_PROTOCOL, 0, 0, 0, 2, (char *)&protocol);  
  36. }  
  37.   
  38. void AndroidAccessory::sendString(byte addr, int index, const char *str)  
  39. {  
  40.     usb.ctrlReq(addr, 0,  
  41.              USB_SETUP_HOST_TO_DEVICE |  
  42.     USB_SETUP_TYPE_VENDOR |  
  43.     USB_SETUP_RECIPIENT_DEVICE,  
  44.     ACCESSORY_SEND_STRING, 0, 0, index,  
  45.     strlen(str) + 1, (char *)str);  
  46. }  


swtichDevice() メソッドが false を返したら、board は次の新しいデバイスが接続されるまで待ちます。このメソッドが成功したら、ADK board が bus を再列挙したとき、デバイスは自身を accessory モードで開始した状態として USB bus 上に表示します。デバイスが accessory モードであれば、accessory はデバイスとの通信を確立することができます。


Establish communication with the device

デバイスが accessory モードで開始されていることが検出できたら、accessory は適切なバルクエンドポイントを見つけてデバイスとの通信をセットアップする必要があります。ADK board が accessory モードの Android-powered デバイスを見つけたら、AndroidAccessory::configureAndroid() 関数を呼びます。

  1. ...  
  2. if (isAccessoryDevice(devDesc)) {  
  3.             Serial.print("found android acessory device\n");  
  4.   
  5.             connected = configureAndroid();  
  6.         }  
  7. ...  


ここでは findEndpoints() 関数を呼びます

  1. ...  
  2. bool AndroidAccessory::configureAndroid(void)  
  3. {  
  4.     byte err;  
  5.     EP_RECORD inEp, outEp;  
  6.   
  7.     if (!findEndpoints(1, &inEp, &outEp))  
  8.         return false;  
  9. ...  


AndroidAccessory::findEndpoints() 関数は Android-powered デバイスの構成ディスクリプタを問い合わせて USB デバイスと通信するためのバルクデータエンドポイントを見つけます。こうするために、まずデバイスの構成ディスクリプタの最初の4バイト(必要なのは descBuff[2] と descBuff[3] のみ)を取得します。ここにはディスクリプタを取得することで返されたデータの全データ長が含まれています。また、このディスクリプタには全インタフェースとエンドポイントディスクリプタについての情報も含まれています。ディスクリプタが適切なサイズであれば、メソッドは構成ディスクリプタ全体を読み込み、ディスクリプタバッファーに格納します。なんらかの理由でディスクリプタに到達できない場合はエラーを返します。

  1. ...  
  2.   
  3. bool AndroidAccessory::findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outEp)  
  4. {  
  5.     int len;  
  6.     byte err;  
  7.     uint8_t *p;  
  8.   
  9.     err = usb.getConfDescr(addr, 0, 4, 0, (char *)descBuff);  
  10.     if (err) {  
  11.         Serial.print("Can't get config descriptor length\n");  
  12.         return false;  
  13.     }  
  14.   
  15.   
  16.     len = descBuff[2] | ((int)descBuff[3] << 8);  
  17.     if (len > sizeof(descBuff)) {  
  18.         Serial.print("config descriptor too large\n");  
  19.             /* might want to truncate here */  
  20.         return false;  
  21.     }  
  22.   
  23.     err = usb.getConfDescr(addr, 0, len, 0, (char *)descBuff);  
  24.     if (err) {  
  25.         Serial.print("Can't get config descriptor\n");  
  26.         return false;  
  27.     }  
  28.   
  29. ...  


一度ディスクリプタがメモリに格納されたら、ポインターはバッファの最初の位置に割当てられ、バッファ読み込みのインデックスとして使われます。ここには AndroidAccessory::findEndpoints() に渡される2つのエンドポイント用ポインタ(入力用と出力用)があり、それぞれのアドレスは 0 にセットされています。なぜなら、コードはまた適切なバルクエンドポイントを見つけていないからです。ループでは構成、インタフェースもしくはエンドポイントの各ディスクリプタを解析し、バッファを読み取ります。各ディスクリプタに対し、位置 0 には常にディスクリプタのサイズ(バイト単位)が格納されており、位置 1 には常にディスクリプタのタイプが格納されています。これらの2つの値を使ってループでは任意の構成、インタフェースディスクリプタをスキップし、次のディスクリプタを取得するために descLen 変数でバッファをインクリメントします。

 注意:accessory モードの Android-powered デバイスは潜在的に2つのインタフェースを持つことができます。1つはデバイスとのデフォルトの通信用、もう1つは ADB 通信用です。デフォルトの通信インタフェースは常に最初にインデックスされており、最初に見つける入力と出力のバルクエンドポイントはデフォルトの通信エンドポイントを返します。demokit.pde sketch ではそのように処理しています。自身のファームウェアを書く場合、あなたの accessory に対する適切なエンドポイントを見つけるロジックは異なる可能性があります。

最初の入力と出力のエンドポイントディスクリプタを見つけたら、これらのアドレスにエンドポイントポインタをセットします。findEndpoints() 関数は入力と出力両方のエンドポイントを見つけたら true を返します。この関数は、他のエンドポイント(ADB インタフェースなど)が見つかっても無視します。

  1. ...  
  2.     p = descBuff;  
  3.     inEp->epAddr = 0;  
  4.     outEp->epAddr = 0;  
  5.     while (p < (descBuff + len)){  
  6.         uint8_t descLen = p[0];  
  7.         uint8_t descType = p[1];  
  8.         USB_ENDPOINT_DESCRIPTOR *epDesc;  
  9.         EP_RECORD *ep;  
  10.   
  11.         switch (descType) {  
  12.         case USB_DESCRIPTOR_CONFIGURATION:  
  13.             Serial.print("config desc\n");  
  14.             break;  
  15.   
  16.         case USB_DESCRIPTOR_INTERFACE:  
  17.             Serial.print("interface desc\n");  
  18.             break;  
  19.   
  20.         case USB_DESCRIPTOR_ENDPOINT:  
  21.             epDesc = (USB_ENDPOINT_DESCRIPTOR *)p;  
  22.             if (!inEp->epAddr && (epDesc->bEndpointAddress & 0x80))  
  23.                 ep = inEp;  
  24.             else if (!outEp->epAddr)  
  25.                 ep = outEp;  
  26.             else  
  27.                 ep = NULL;  
  28.   
  29.             if (ep) {  
  30.                 ep->epAddr = epDesc->bEndpointAddress & 0x7f;  
  31.                 ep->Attr = epDesc->bmAttributes;  
  32.                 ep->MaxPktSize = epDesc->wMaxPacketSize;  
  33.                 ep->sndToggle = bmSNDTOG0;  
  34.                 ep->rcvToggle = bmRCVTOG0;  
  35.             }  
  36.             break;  
  37.   
  38.         default:  
  39.             Serial.print("unkown desc type ");  
  40.             Serial.println( descType, HEX);  
  41.             break;  
  42.         }  
  43.   
  44.         p += descLen;  
  45.     }  
  46.   
  47.     if (!(inEp->epAddr && outEp->epAddr))  
  48.         Serial.println("can't find accessory endpoints");  
  49.   
  50.     return inEp->epAddr && outEp->epAddr;  
  51. }  
  52.   
  53. ...  


configureAndroid() 関数に戻りましょう。エンドポイントが見つかったら、通信用に適切にセットアップします。デバイスのコンフィグレーションを 1 にセットし、さらにデバイス状態を "running" にします。これはデバイスがあなたの USB accessory と通信するために正しくセットアップされたことを意味します。この状態に設定することで、AndroidAccessory::isConnected() 関数でデバイスの再検出や再設定が行われるのを防ぎます。

  1. bool AndroidAccessory::configureAndroid(void)  
  2. {  
  3.     byte err;  
  4.     EP_RECORD inEp, outEp;  
  5.   
  6.     if (!findEndpoints(1, &inEp, &outEp))  
  7.         return false;  
  8.   
  9.     memset(&epRecord, 0x0, sizeof(epRecord));  
  10.   
  11.     epRecord[inEp.epAddr] = inEp;  
  12.     if (outEp.epAddr != inEp.epAddr)  
  13.         epRecord[outEp.epAddr] = outEp;  
  14.   
  15.     in = inEp.epAddr;  
  16.     out = outEp.epAddr;  
  17.   
  18.     Serial.print("inEp: ");  
  19.     Serial.println(inEp.epAddr, HEX);  
  20.     Serial.print("outEp: ");  
  21.     Serial.println(outEp.epAddr, HEX);  
  22.   
  23.     epRecord[0] = *(usb.getDevTableEntry(0,0));  
  24.     usb.setDevTableEntry(1, epRecord);  
  25.   
  26.     err = usb.setConf( 1, 0, 1 );  
  27.     if (err) {  
  28.         Serial.print("Can't set config to 1\n");  
  29.         return false;  
  30.     }  
  31.   
  32.     usb.setUsbTaskState( USB_STATE_RUNNING );  
  33.   
  34.     return true;  
  35. }  


最後に、適切なエンドポイントに読み書きするメソッドが必要です。demokit.pdf sketch では Android-powered デバイスから読むか、ADK board から送られたかによって、これらのメソッドを呼んでいます。例えば、ADK shield 上でジョイスティックが動くとデータが書き込まれ、Android-powered デバイス上の DemoKit アプリケーション によって読み込まれます。DemoKit アプリケーション上のスライダーが動くと、demokit.pde sketch によって読み込まれ、LED ライトの色が変わったり明るくなるなど accessory の状態が変わります。

  1. int AndroidAccessory::read(void *buff, int len, unsigned int nakLimit) {  
  2.   return usb.newInTransfer(1, in, len, (char *)buff, nakLimit); }  
  3.   
  4. int AndroidAccessory::write(void *buff, int len) {  
  5.   usb.outTransfer(1, out, len, (char *)buff);  
  6.   return len; }  


ADK board がどのように読み書きしているかの情報は firmware/demokit/demokit.pde ファイルを見てください。

0 件のコメント:

コメントを投稿