FeliCaコマンドの発行
後回しにしたfelicalib_nfc_thru関数について説明します。
「SDK for NFC」の「NFCアクセスライブラリ」は、その名のとおりNFCに対応しています。つまり「FeliCa Standard」や「FeliCa Lite-S」だけでなく、「Type A (MIFARE)」や「Type B」もサポートしています。
しかし、ポートのオープンやアクセス権の取得などの関数は共通化されているものの、いざ実際にカードの読み書きを行おうとすると、カードの規格ごとに異なるコマンド(バイナリデータ)を自分で組み立て、felicalib_nfc_thru関数の引数としてドライバに渡さなければなりません。
FeliCa Lite-Sのコマンドの詳細は、「FeliCa Lite-Sユーザーズマニュアル」に記述があります[6]。
コマンドには「Polling」「Write Without Encryption」「Read Without Encryption」があり、これらを使ってカードの読み書きを行うことができます。今回はS_PAD3ブロックを読み書きしてみましょう。
Pollingコマンド
Pollingは、カードを捕捉するためのコマンドです。
先に呼び出したfelicalib_nfc_poll_and_start_dev_accessと同等であり、省略しても動作するようですが、念のため発行しておきます。
Pollingコマンドの構成は、次のとおりです。
| パラメータ名 | サイズ | データ | 備考 |
|---|---|---|---|
| コマンドコード | 1 | 0x00 | - |
| システムコード | 2 | - | システムコードの指定 |
| リクエストコード | 1 | - | リクエストデータの指定 |
| タイムスロット | 1 | - | 応答可能な最大スロット数の指定 |
- 「コマンドコード」は、「0x00」固定です。
- 「システムコード」には、FeliCa Lite-S用のシステムコード「0x88B4」をビッグエンディアンで指定します。つまり、「0x88」「0xB4」になります。
- 「リクエストコード」には、特に情報を要求しない場合、「0x00」を指定します。
- 「タイムスロット」には、複数枚のカードを考慮しない場合、「0x00」を指定します。
「コマンドコード」の前1バイトには、自分自身を含むデータ長をセットする必要があります。
こうして組み立てたコマンドを、felicalib_nfc_thru関数を通して発行します。ベタ書きすると、次のようになります。
unsigned char pCom[] =
{
0x06, // データ長
0x00, // コマンドコード
0x88, 0xB4, // システムコード
0x00, // リクエストコード
0x00 // タイムスロット
};
unsigned char pRes[512];
unsigned short usComLen = 6;
unsigned short usResLen = sizeof(pRes);
bSuccess = felicalib_nfc_thru(pCom, usComLen, pRes, &usResLen);
if (! bSuccess)
{
// エラー処理...
}
コマンドの発行に成功すると、処理結果がレスポンスとして第3引数に格納されます。
| パラメータ名 | サイズ | データ | 備考 |
|---|---|---|---|
| レスポンスコード | 1 | 0x01 | - |
| IDm | 8 | - | 対象システムのIDm |
| PMm | 8 | - | - |
| リクエストデータ | 0 or 2 | - | リクエストコードで情報を要求した場合のみ |
「レスポンスコード」の前に、自分自身を含むデータ長が1バイト付加されるので注意してください。今回はリクエストデータを要求しなかったため、データ長は1+1+8+8=18になります。
3バイト目から始まる「IDm」の領域には、捕捉したカードのIDmが格納されているはずです。
Write Without Encryptionコマンド
カードへの書き込みには、「Write Without Encryption」コマンドを使います。
| パラメータ名 | サイズ | データ | 備考 |
|---|---|---|---|
| コマンドコード | 1 | 0x08 | - |
| IDm | 8 | - | - |
| サービス数 | 1 | m | m=1 |
| サービスコードリスト | 2m | - | リトルエンディアン |
| ブロック数 | 1 | n | n=1または2 |
| ブロックリスト | N | - | 2n≦N≦3n |
| ブロックデータ | 16n | - | - |
- 「コマンドコード」は、「0x08」固定です。
- 「IDm」には、先ほどPollingコマンドで取得した、カードのIDmを指定します。
- 「サービス数」には、「0x01」を指定します。
- 「サービスコードリスト」には、「0x0009」をリトルエンディアンで指定します。つまり、「0x09」「0x00」となります。サービスコード0x0009は、読み書き可能なブロックへのアクセスを意味します。
- 「ブロック数」には、1つのブロックにのみアクセスする場合、「0x01」を指定します。
- 「ブロックリスト」には、アクセスしたいブロックを指定します。S_PAD3ブロック、つまりブロック番号0x03にアクセスする場合は、「0x00」「0x03」「0x00」を指定してください。真ん中の「0x03」が、ブロック0x03を意味します。
- 「ブロックデータ」には、カードに書き込むデータ羅列します。1ブロック分のデータを書き込む場合は、16バイトのデータを羅列することになります。
なお、「コマンドコード」の前1バイトに、自分自身を含むデータ長をセットする必要があります。
以上の書き込みコマンドをベタ書きすると、次のようになります。
unsigned char pCom[] = {
0x21, // データ長
0x08, // コマンドコード
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IDm
0x01, // サービス数
0x09, 0x00, // サービスコード
0x01, // ブロック数
0x00, 0x03, 0x00, // ブロック
'c', 'o', 'd', 'e', 'z', 'i', 'n', 'e', // 任意のデータ
'.', 'j', 'p', 0x00, 0x00, 0x00, 0x00, 0x00 // 任意のデータ
};
unsigned char pRes[512];
unsigned short usComLen = 0x21;
unsigned short usResLen = sizeof(pRes);
// 配列 unsigned char pIDm[8] に、
// 目的のシステムの IDm が格納されているものとする
memcpy_s(&pCom[2], sizeof(pCom) - 2, pIDm, 8);
bSuccess = felicalib_nfc_thru(pCom, usComLen, pRes, &usResLen);
if (! bSuccess)
{
// エラー処理...
}
書き込みは必ず16バイト単位で行われます。そのため、1バイトだけ書き変えたい場合も、当該ブロックのデータ16バイト分を読み取り、目的の1バイトを変更した後、16バイト分まとめて書き戻さなければなりません。
コマンドの発行に成功すると、実行結果がレスポンスとして第3引数に格納されます。
| パラメータ名 | サイズ | データ | 備考 |
|---|---|---|---|
| レスポンスコード | 1 | 0x09 | - |
| IDm | 8 | - | - |
| ステータスフラグ1 | 1 | - | 書き込み成功時は0x00 |
| ステータスフラグ2 | 1 | - | - |
「レスポンスコード」の前に、自分自身を含むデータ長が1バイト付加されます。このコマンドの場合、データ長は12になります。
「ステータスフラグ1」には、書き込みの成否が格納されます。成功時は「0x00」、失敗時はそれ以外の値になります。
