Palm Programmer's Laboratory
Palm OS Programmer's Companion Volume II/6-3
6-3 Bluetooth 仮想シリアルドライバ
Bluetooth システムは仮想シリアルドライバによってシリアルポートプロファイルを実装しています。このドライバの特徴は以下の通りです。
- Bluetoothスタックのためにバックグラウンドスレッドを開きます。
- 一度に一つのアクティブなシリアルチャネル( Point-to-point 接続)をサポートします。
- 明確にクライアントかサーバとして開かれます。
- クライアントとして、以下のPalm OSコンポーネントによって利用されます。
- PPP
- HotSync
- 電話通信
- もしサーバとして開かれたなら、リモートクライアントの問い合わせのためにサービスのリスト( UUID )を広報します。
- もしクライアントとして開かれたなら、開いたものが渡した情報に基づいて、必要な帯域と RFCOMM 接続を生成します。
RFCOMM ベースの仮想シリアルポートは、物理シリアルポートよりも対称的ではありません。伝統的なシリアルポートにおいて、基盤となる転送を確立する必要はありません。しかし、Bluetooth シリアルポートを確立するとき、3 つの異なるスタックレベル( ACL、L2CAP、RFCOMM )においてクライアントとサーバデバイスの役割があります。SDP の登録および問い合わせの責任と同様です。
シリアルポートを開く
Bluetooth シリアルポートを開くために、カスタム情報ブロックにおける Bluetooth 特有のパラメータを渡して、新しい SrmExtOpen 関数を使うことができます。まず最初に、他の種類のポートはブロックを他の目的に使用するので、開いているポートが本当に Bluetooth シリアルポートなのかを検証します。
特定のレガシーなアプリケーションの利点のために、ドライバは古い SrmOpen 関数で開かれたものもサポートします。この場合、ドライバはクライアントの役割を想定し、ユーザとの相互作用によってデバイス探索を行ない、シリアルポートサービスクラスを広報するリモートチャネルを探します。
SrmExtOpen で RFCOMM 仮想シリアルポートを開くために、BtVdOpenParams 構造体を生成する必要があります。この構造体は以下のように宣言されます。
typedef struct { BtVdRole role; union { BtVdOpenParamsClient client; BtVdOpenParamsServer server; } u; Boolean authenticate; Boolean encrypt; } BtVdOpenParams;
この構造体をどのように移行するかは、シリアルポートをクライアントとして開いているのかサーバとして開いているのかに依存します。サーバが入ってくる帯域と RFCOMM 接続を待っている間に、クライアントは帯域と RFCOMM 接続を初期化します。
もしクライアントとして振る舞うなら、「ポートをサーバとして開く?」で説明するように、構造体の role メンバを btVdClient にセットし、client メンバの結合を埋めます。
クライアントとして振る舞うのかサーバとして振る舞うのかに関係なく、もしリンク認証を必要とするなら authenticate を true にセットします。同様に、もしリンク暗号化を必要とするなら encrypt を true にセットします。リンク暗号化はリンク認証を必要とします。
クライアントとしてポートを開く
クライアントの役割を演じるとき、リモートBluetoothデバイスのアドレスとリモートデバイス上の接続対象サービスを指定します。これは、BtVdOpenParams 構造体内の結合の client メンバを埋めることによって行なわれます。このメンバは BtVdOpenParamsClient 構造体として以下のように宣言されます。
typedef struct { BtLibDeviceAddressType remoteDevAddr; BtVdClientMethod method; union { BtLibRfCommServerIdType channelId; BtVdUuidList uuidList; } u; } BtVdOpenParamsClient;
もし接続しようとする Bluetooth デバイスのアドレスが分かっているなら、remoteDevAddr にアドレスを提供します。分からないなら、アドレスの値を全てゼロにします。これは Bluetoothデバイス探索を初期化し、ハンドヘルドユーザが接続するデバイスを選択することができるようになります。
RFCOMM を使ってリモートサービスに接続するとき、2つの基本的な選択肢があります。
- SDP を使って 1 つ以上の UUID を探す
- 特定の RFCOMM チャネル ID に接続する
通常は、1 つ以上のサービスクラス UUID の提供された一覧によって、接続するサービスを指定します。単純に構造体の method メンバを btVdUseUuidList にセットし、構造体の結合の uuidList メンバを使って UUID の一覧を提供します。これは一連の SDP クエリを引き起こします。SDP クエリは指定された各サービスクラスを探します。リモートデバイスで最初に見つかったサービスクラスが使用されます。もしそれがアプリケーションに合ったものなら、空のサービスクラス UUID 一覧(項目数がゼロ)を指定します。これにより、デフォルトの Palm 特有サービスクラスUUID(953D4FBC-8DA3-11D5-AA62-0030657C543C)にするための SDP クエリが引き起こされます。
SDP クエリが成功した結果が、リモートデバイスに接続するための RFCOMM サーバチャネルです。テストを容易にするために、SDP クエリバイパスしてリモート RFCOMM サーバチャネルIDを直接指定することができます。単純に構造体の method メンバをbtVdUseChannelId にセットし、構造体の結合の channelId メンバをサーバチャネル ID にセットします。
RFCOMM 接続が確立するか接続が確立できないことが決定するかするまで、SrmExtOpen ブロックをコールします。ドライバは進捗ダイアログを表示し、接続試行をキャンセルする機会を与えます。
RFCOMM 接続が上手く確立したときだけ、SrmExtOpen はゼロを戻します。
サーバとしてポートを開く
クライアントとしてシリアルポートを開くプロセスに比べて、サーバとしてポートを開くのはかなりシンプルです。サーバとして振る舞うとき、必要なのは広報したいサービスの UUID を指定するだけです。オプションとして、ユーザが読める名前をサービスにつけることもできます。
BtVdOpenParams 構造体の結合の server メンバを埋めることによって、サービス UUIDとユーザが読める名前を指定します。このメンバは BtVdOpenParamsServer 構造体で、以下のように宣言します。
typedef struct { BtLibSdpUuidType uuid; const Char *name; } BtVdOpenParamsServer;
便利なように、デフォルトの Palm 特有サービスクラス UUID が広報された場合にはヌルの UUID(全てのバイナリがゼロ)を指定することができます。 (953D4FBC-8DA3-11D5-AA62-0030657C543C)
SrmExtOpenのコールは入ってくる RFCOMM 接続を待つことなくすぐに戻ります。入ってくるデータを待つために、SrmReceive 、SrmReceiveWait 、または SrmReceiveCheck を定期的にコールします。
例
以下のコードは、クライアントとして振る舞う、RFCOMM仮想シリアルポートを生成しリモートデバイスの既知の RFCOMM チャネルに接続する SrmExtOpen のコールについての解説を抜粋したものです。
Err err; SrmOpenConfigType config; BtVdOpenParams btParams; UInt16 btPortId; config.function = 0; // must be zero config.drvrDataP = (MemPtr)&btParams; config.drvrDataSize = sizeof(BtVdOpenParams); btParams.role = btVdClient; // we are the client side btParams.u.client.remoteDevAddr.address[0] = ...; // remote device addr byte 1 ... btParams.u.client.remoteDevAddr.address[5] = ...; // remote device addr byte 6 btParams.u.client.method = btVdUseChannelId; btParams.u.client.u.channelId = 0x53; err = SrmExtOpen( sysFileCVirtRfComm, // type of port == RFCOMM &config, // port configuration params sizeof(config), // size of port config params &btPortId // receives the id of this virtual serial port instance );
このコード抜粋はそのままではコンパイルできないことに注意して下さい。リモート Bluetooth デバイスアドレスが適切に指定されていません。
Palm から Palm への通信
ほとんどのアプリケーションはクライアントとしてだけ振る舞います。しかし、Palm から Palm への場合、両方ともクライアントとサーバとして振る舞い必要があります。この場合、そのサービスを他のリモートデバイスに広報するために、仮想シリアルドライバは最初にサーバとして設定されるべきです。この時点で、両方のデバイスがサーバとして振る舞い、そのサービスを広報します。ユーザが開始したアクションによって一方のデバイスの仮想シリアルドライバがクライアントとして再度開くとき、リモートデバイスと広報されたサービス(事前に定義されて UUID で同意されている)を探索し、両デバイス間でチャネルが開かれます。
Palm OS がどのようにBluetooth仮想シリアルドライバを使用するか
Palm OSにおいて、HotSync・PPP・電話マネージャは全てBluetoothを使用することができます。しかしそれらはクライアントとして振る舞うことしかできません。
これらのクライアントのために、ユーザは接続パネルでデバイス探索と(もし適切であれば)デバイスペアリングを行ないます。これらのクライアントは、リモートデバイスのアドレス・リンクキー・リモートデバイスを探すためのサービスクラスを決定するために、接続パネルを調べることができます。例えば、リモートデバイスが電話かモデムであることを示す接続プロファイルを PPP が使用しているなら、それはダイヤルアップネットワークサービスクラス UUID を探します。しかしもしリモートデバイスが PCまたは LAN アクセスポイントであることをプロファイルが示すなら、LAN アクセス使用 PPP サービスクラス UUID を探します。