USBスピーカのディスクリプタが読めたので、その情報に基づいて音量設定とPCM出力インタフェースの活性化を行います。本来であれば、ディスクリプタをきちんと解析/解釈すれば、どんなUSBスピーカにも対応することが可能なのですが、試験に使えるスピーカがいくつもあるわけではないし、購入したSONYのスピーカに対応することしか考えません。読み出したディスクリプタからわかったインタフェース番号やエンドポイント番号を半ば決め打ちでコード書いちゃっています。
音量やミュートの制御をおこなうには、Audio Control Interfaceを使いますが、そのインタフェース番号は0番であることが、ディスクリプタからわかっています。そこで、まずはこのインタフェースから現在の音量/ミュート設定を読み出してやります。
> host
Initializing Host Stack
Host Initialized
Connect a Audio device
Port = 1
I 04 00 00 00 01 01 00 00
AI 24 01 00 01 28 00 01 01
AI 24 02 01 01 01 00 02 03 00 00 00
AI 24 03 06 01 03 00 09 00
AI 24 06 09 01 01 01 02 02 00
I 04 01 00 00 01 02 00 00
I 04 01 01 01 01 02 00 00
AI 24 01 01 01 01 00
AI 24 02 01 02 02 10 02 80 bb 00 44 ac 00
E 05 01 09 c8 00 01 00 00
AE 25 01 01 01 01 00
I 04 02 00 01 03 00 00 00
AD 21 00 01 00 01 22 32 00
E 05 83 03 04 00 20
IF #0, alt 0 is audio control.
IF #1, alt 0 is audio streaming.
IF #1, alt 1 is audio streaming.
-- Endpoint 1-OUT (200) is type ISOCHRONOUS
IF #2, alt 0 is HID.
-- Endpoint 3-IN (4) is type INTERRUPT
Audio device connected
> host muteget 0
0
> host volget 1
CUR: 65472 (c0 ff) MIN: 58272 (a0 e3) MAX: 65520 (f0 ff)
> host volget 2
CUR: 65472 (c0 ff) MIN: 58272 (a0 e3) MAX: 65520 (f0 ff)
>
引数の0, 1, 2はチャネル番号を表し、マスター、左、右に対応します。ミュートはオフ状態ですので、改めて設定する必要もないようです。音量はCUR, MIN, MAXの3つの値を取得して表示しています。CURが現在の設定値ですが、上記のように電源が入った直後では最大値に近い値に設定されています。ちょっと、どうかしらと思う動作仕様です。そこで、次のように設定して音量を下げてやります。マスターを操作すると左右の両方に影響を与えるのですが、音量設定ではマスターがサポートされていないようなので、左右別々に設定してやります。
> host volset 1 59000
> host volset 2 59000
> host volget 1
CUR: 58944 (40 e6) MIN: 58272 (a0 e3) MAX: 65520 (f0 ff)
> host volget 2
CUR: 58944 (40 e6) MIN: 58272 (a0 e3) MAX: 65520 (f0 ff)
>
実際に設定できた値が指定した値と異なっていますが、これはスピーカ側で設定できる値の解像度に制約があり、丸められた値しか設定できないためです。
現在のオーディオ出力はalt #0 になっており、PCM出力のエンドポイントと関連づけられていません。そこで、alt #1に変更してやります。
> host ifget 1
0
> host ifset 1 1
> host ifget 1
1
>
これでスピーカ側ではPCM出力動作が開始されているハズ。あとはアイソクロナス転送でPCMデータを送りつけてやれば、スピーカが鳴ってくれるでしょう。
上記の設定変更は、全てUSBのEP0を使うコントロール転送で制御しています。コントロール転送での書き込みや読み出しの基本ルーチンはUSB Host Liteで用意されているので、これを使っているのですが、当初正しく書き出し読み出しができなくて悩みました。音量を設定変更したつもりでも、読み出してみるとちっとも値が変わっていなかったのです。
USB_INT32S Host_CtrlRecv(USB_INT08U bm_request_type,
USB_INT08U b_request,
USB_INT16U w_value,
USB_INT16U w_index,
USB_INT16U w_length,
volatile USB_INT08U *buffer)
USB_INT32S Host_CtrlSend(USB_INT08U bm_request_type,
USB_INT08U b_request,
USB_INT16U w_value,
USB_INT16U w_index,
USB_INT16U w_length,
volatile USB_INT08U *buffer)
使っているのは上記ふたつの関数で、引数bufferで読み書きするデータを保持するバッファを指定します。ところが、USB Host Liteのコードを調べれてみると、これらふたつの関数の実装では
引数bufferは全く参照されておらず(!!)、決め打ちのバッファを使うようになっていたのです。こんなのアリ?! サンプル以外の用途には簡単には流用できないようにワザと罠をしかけてあったんでしょうか。
USB Host Liteを使ってみようとお考えの方は、ご注意ください。ちなみに、インターフェース6月号記事のサンプルもこういうコードになっていますが、筆者の方の落ち度ではなく、もともとNXPが配布しているUSB Liteのコードからして、こうなっています。