通常はリーダライタはそれぞれドライバがあってSDKが提供されたりしてそのリーダ用のプログラムを作成するわけなんですが、PC/SCに対応したリーダライタでPC/SCを利用してプログラミングしてしまえば、他のPC/SC対応リーダに変更してもそのまま使えちゃうわけですね。(まぁ、そんなにうまくはいかないんですが)
こういう共通インターフェース仕様っていうのはぶっ飛び系(なんで?こんだけのことするのに何でこんなにコードがいるわけ?など)が多いわけですが、このPC/SCも結構不思議ちゃんな部分があります。
実際に見てみましょう。ACSさんのACR122リーダを使用します。
PC/SCの実体というかAPIの呼び出し先はwinscard.dllというシステムファイルです。Windowsに含まれてますので、特に何かインストールしなくても普通に使えます。
で、だいたい次のようなAPIの呼び出しをして使います。
SCardEstablishContext - まあ、これから使うよー的な。
↓
SCardListReaders - これでWindowsが認識しているPC/SCに対応したリーダの一覧を取得します。
↓
SCardGetStatusChange - リーダの状態の変化を察知します。カードが置かれたとかがわかります。
↓
SCardConnect - カードが置かれてるようなら、これでカードと接続(おおげさな)します。
↓
SCardTransmit - カードに対してコマンドを送信します。
↓
SCardDisconnect - カードとの接続を切断(またおおげさな)します。
↓
SCardReleaseContext - 終わったよー的な。
至って普通に見えますね。重要なのは SCardTransmit で、これでAPDU(Application Protocol Data Unit)という形式のバイナリデータを送ることでカードを操作します。たとえば「お前だれー」というときは
FF CA 00 00 00 (16進数)
といったバイナリ列を送ればカードのIDを返してきます。APDUは定義されたものもあればメーカ独自のものもあります。
先ほどのIDの取得やタグの読み書きなどは定義されていますが、ACR122リーダではブザーを鳴らしたりLEDを点灯したりみたいなこともAPDUを送ることで可能です。
FF 00 40 50 04 05 05 03 01
なんて送ると、赤のLEDが3回点滅してそれに合わせてピーピーピーと鳴ります。便利ですねー。これを使えばカードをかざしてお前のカード認証されてねーよみたいなときに警告としてピーピー言うこともできそうです。
ところが不都合が1つあります。先ほどのAPDUを送信するAPIなんですが、SCardTransmitなんです。そうです。S「Card」Transmitです。カードなんです。カードに送るんです。
まあ、カードに送るって言っても一旦はリーダに送るんだからいいんじゃね?とも思いますが、実際SCardTransmitの引数はこうなっています。
SCardTransmit(カードハンドル, 送信パラメータ, 送信データ, 送信データの長さ, 受信パラメータ, 受信用バッファ, 受信用バッファの長さ)
いきなりカードハンドルというのがあります。これは SCardConnect の時に取得できるカードの識別子みたいなもんです。これがないと送信してくれません。0とかにしてもダメです。もちろん送信するときにはカードとリーダが通信できている状態にある必要があります。
要するに、カードが置いてないときにAPDUコマンドは送信できないのです。例えそのAPDUがリーダのブザーを鳴らすとかカードに全く関係のないコマンドであったとしてもです。えー!なんでー?謎。
(注:ACR122リーダではこれを回避?するため DirectTransmit というものがPC/SCとは別に用意されているようです)
※2014/07/04 追記 カードが無くてもコマンド送信は可能でした。お詫びいたします。詳細は新しいエントリー「PC/SCに潜む謎 その2」を参照ください。