2年くらい前だっただろうか、iPhoneが決済システムを搭載するという話になって、「おサイフケータイ搭載か?!」と話題を呼んだ。結局、未だにiPhoneはおサイフケータイに対応せず、NFCもロックされていて一般には使えないという状況である。
まあ、世の中、技術が揃っていれば実現可能とも言えるというのが、筆者の考えで、今まで、いろんなモノをゴニョゴニョしてきた。そこで、今回も、一発やってやろうという話なのだが、その前にちょっと補足を。
前回の投稿時、筆者は、豪州に住んでいた。あれから3年以上経ってしまって、ブログの体を成していない訳なのであるが、時代の流れがSNS中心になってしまっているので、なかなかブログに書くということも思いつきもしないのだ。お許し願いたい。とは言え、ここのところ、書きとどめておきたいと思うような話はあまりなかった。最悪の道を回避するだけでは、最高の道に進むことはできないのだと思い知った。できることなら4年ほど時を巻き戻したい。
さて、筆者の開発環境であるが、当時は、Macbook Pro 13in Early 2011 i7モデルだった。数々の故障を自分でなおしてきたが、一月ほど前についに内部のSATAポートが死んで起動しなくなってしまった。そこで、購入したのが、Macbook Pro 13in Mid 2012である。なぜ、4年前のモデルを購入するのか。それは、なんといっても「改造できるから」である。そんな、ちょっとオカシイ筆者の環境を貼り付けておく。
Model: MacBookPro9,2, BootROM MBP91.00D3.B0D, 2 processors, Intel Core i7, 2.9 GHz, 16 GB, SMC 2.2f44
Graphics: Intel HD Graphics 4000, Intel HD Graphics 4000, Built-In
Memory Module: BANK 0/DIMM0, 8 GB, DDR3, 1333 MHz, 0x029E
Memory Module: BANK 1/DIMM0, 8 GB, DDR3, 1333 MHz, 0x029E
AirPort: spairport_wireless_card_type_airport_extreme (0x14E4, 0xF5), Broadcom BCM43xx 1.0 (7.21.95.175.1a6)
Bluetooth: Version 4.4.5f3 17904, 3 services, 27 devices, 1 incoming serial ports
Network Service: Wi-Fi, AirPort, en1
Serial ATA Device: Crucial_CT256MX100SSD1, 256.06 GB
Serial ATA Device: Hitachi HTS547575A9E384, 750.16 GB
USB Device: USB 2.0 Bus
USB Device: Hub
USB Device: Hub
USB Device: Apple Internal Keyboard / Trackpad
USB Device: IR Receiver
USB Device: BRCM20702 Hub
USB Device: Bluetooth USB Host Controller
USB Device: USB 2.0 Bus
USB Device: Hub
USB Device: FaceTime HD Camera (Built-in)
USB Device: USB 3.0 Bus
USB Device: RC-S360/S
Thunderbolt Bus: MacBook Pro, Apple Inc., 25.1
話を戻そう。取り敢えず、筆者としては、NFC機能のないiPhoneに、おサイフケータイに近い、可能なかぎりのものを実装しようというのが狙いである。(なお、RCS390の存在は知っている。)この問題を単純化していくと、まずは、DarwinなマシンでFelicaを読み書きできればよろしいのであろうというところだ。というのも、Felica開発元のSonyはWindowsを念頭に置いた開発環境ばかり作っているので、既存のサンプルを移植するにできないという話なのだ。
今回使うことにしたのは、libusb + libnfcである。依存関係がシンプルで、移植が簡単そうだったからだ。ネットを見渡すと、これでLinuxでパソリリーダーを使えたというような記述が散見される。実際に始めてみて気づいたのは、使えたって言っても、IDmの読み取りくらいしかしてない人のブログの投稿ばかりなのだ。しかもlibnfcは、NXP純正のリーダーを前提に作られているようで、大Sony様が変更した部分のAPIとか、なにそれ美味しいの、みたいなスタンスだった。筆者が用意したのは、「楽天Edyオフィシャルショップの楽天Edyリーダー()」で、一応、というか中身はそのまんま、SonyのRC-S360/Sだった。
さて、いろんなブログの説明通りにインストールまではできた。Macportsでlibusb-compatまでインストールすれば、あとは、libnfcを ./configureしてmake installで大丈夫だ。もちろんこのままでちゃんと動いたわけではなかった。ログを取るために
./configure –prefix=/opt/local –includedir=/opt/local/include –enable-debug
としてライブラリから再インストールした。
pn53x.cには、若干の変更を加えた。まず、200行目付近
switch (pbtTx[0]) {
case PowerDown:
case InDataExchange:
case InCommunicateThru:
case CommunicateThruEX://RCS360用に追加
case InJumpForPSL:
case InPSL:
あとは、1400行目付近、pn53x_initiator_transceive_bytes関数内
return pnd->last_error;
}
if (CHIP_DATA(pnd)->type == RCS360) {//ここから
// Copy the data into the command frame
abtCmd[0] = CommunicateThruEX;
abtCmd[1] = 0×20;//800×0.5
abtCmd[2] = 0×03;//millisconds
memcpy(abtCmd + 3, pbtTx, szTx);
szExtraTxLen = 3;
}else{//ここまで
// Copy the data into the command frame
if (pnd->bEasyFraming) {
それから、pn53x-internal.hに定義を追加しておけば、とりあえずなんとかなる。
#define CommunicateThruEX 0xA0
最後に、動作検証に使ったプログラムのソースを載せておく。これ以上の話は、別の投稿にしようと思う。
// To compile this simple example:
// $ gcc -o felica_idm felica_idm.c -lnfc -L/opt/local/lib -I/opt/local/include
#include <stdlib.h>
#include <string.h>
#include <nfc/nfc.h>
#include <libkern/OSByteOrder.h>
uint8_t request_edy_service_command[16] = {0×10,/*cmd*/ 0×06,/*IDm*/ 0×00, 0×00, 0×00, 0×00, 0×00, 0×00, 0×00, 0×00,/*sys,serv*/ 0×01, 0x0f, 0×17, 0×01, 0×80, 0×00};
uint8_t read_wo_encryption_edy_command[34] = {0×22, 0×06, 0×00, 0×00, 0×00, 0×00, 0×00, 0×00, 0×00, 0×00, 0×03, 0x0b, 0×11, 0×17, 0×13, 0x0f, 0×17, 0×08, 0×80, 0×00, 0×81, 0×00, 0×82, 0×00, 0×82, 0×01, 0×82, 0×02, 0×82, 0×03, 0×82, 0×04, 0×82, 0×05};
static void
print_hex(const uint8_t *pbtData, const size_t szBytes)
{
size_t szPos;
for (szPos = 0; szPos < szBytes; szPos++) {
printf(“%02x “, pbtData[szPos]);
}
printf(“\n”);
}
union IDmHolder{
uint64_t IDm64;
uint8_t IDm8[8];
};
int
main(int argc, const char *argv[])
{
nfc_device *pnd;
nfc_target nt;
// Allocate only a pointer to nfc_context
nfc_context *context;
// Initialize libnfc and set the nfc_context
nfc_init(&context);
if (context == NULL) {
printf(“Unable to init libnfc (malloc)\n”);
exit(EXIT_FAILURE);
}
// Display libnfc version
const char *acLibnfcVersion = nfc_version();
(void)argc;
printf(“%s uses libnfc %s\n”, argv[0], acLibnfcVersion);
pnd = nfc_open(context, NULL);
if (pnd == NULL) {
printf(“ERROR: %s\n”, “Unable to open NFC device.”);
exit(EXIT_FAILURE);
}
// Set opened NFC device to initiator mode
if (nfc_initiator_init(pnd) < 0) {
nfc_perror(pnd, “nfc_initiator_init”);
exit(EXIT_FAILURE);
}
printf(“NFC reader: %s opened\n”, nfc_device_get_name(pnd));
// Poll for a Felica tag
const nfc_modulation nmFelica = {
.nmt = NMT_FELICA,
.nbr = NBR_212,
};
uint64_t tempIdCS = 0;
while (-1)
{
if (nfc_initiator_select_passive_target(pnd, nmFelica, NULL, 0, &nt) > 0) {
if(tempIdCS != *(uint64_t *)(nt.nti.nfi.abtId)){
printf(“The following (NFC) felica tag was found:\n”);
printf(” mID (NFCID2): “);
printf(“0x%016llx\n\r”, OSSwapBigToHostInt64 (*(uint64_t *)(nt.nti.nfi.abtId)));
tempIdCS = *(uint64_t *)(nt.nti.nfi.abtId);
memcpy(&(read_wo_encryption_edy_command[2]), nt.nti.nfi.abtId, 8);
read_wo_encryption_edy_command[2] += 0×10;//なぜかこれが無いと動かない
printf(“now sending:\n”);
print_hex(read_wo_encryption_edy_command, sizeof(request_service_edy_no_command));
#define MAX_FRAME_LEN 264
uint8_t abtRx[MAX_FRAME_LEN];
uint8_t pbtRxPar;
if (nfc_initiator_transceive_bytes(pnd, read_wo_encryption_edy_command, sizeof(read_wo_encryption_edy_command), abtRx, sizeof(abtRx), 0) > 0) {
printf(“got reply:\n”);
print_hex(abtRx, sizeof(abtRx));
}
}
}
}
// Close NFC device
nfc_close(pnd);
// Release the context
nfc_exit(context);
exit(EXIT_SUCCESS);
}
参考資料
http://www.sony.co.jp/Products/felica/business/tech-support/st_fprcs620s_command.html
http://www.nxp.com/documents/user_manual/157830_PN533_um080103.pdf
http://jennychan.web.fc2.com/format/edy.html#110B
http://www.kokozo.net/AndroidNFC_FeliCa/
http://www.libnfc.org/api/group__initiator.html
パケットをダンプするにあたって、餌食になったアプリ
http://www.adobe.com/jp/newsletters/edge/january2010/articles/article5/index.html