RC-S320とLinuxでFelicaのIDmを読み出す

 SONY純正のFelica reader/writerのPasoriの中でもかなり初期のモデルにあたるRC-S320を入手していました。取り急ぎ使用する必要性も無かったので長いこと放置してるうちに、対応するWindows版ソフトウェア(NFCポートソフトウェア Version 5.4.8.6)は提供終了になっていました。
ソニー株式会社 | FeliCa | 個人のお客様 | サポート・ダウンロード | 基本ソフトウェア | NFCポートソフトウェア Version 5.4.8.6
 そんなわけで、Linuxでの使用を試みました(本投稿に記載の情報は2023年6月時点のものです)。使用するための準備から、C言語による自作プログラムでのID読み出しまでを記載します。

Felicaを読み出すイメージ / Bing Image Creatorで作成

 

前提環境

OS
Ubuntu 20.04
ARCH
x86_64

 

USB接続

 とりあえず、何も考えずにUSBポートにRC-S320を挿してみたところ、dmesgに以下の出力が確認できました。

[   22.919334] usb 1-2: new low-speed USB device number 5 using xhci_hcd
[   23.073295] usb 1-2: New USB device found, idVendor=054c, idProduct=01bb, bcdDevice= 1.28
[   23.073301] usb 1-2: New USB device strings: Mfr=0, Product=0, SerialNumber=0

 単に接続されただけで、特にドライバモジュールが読まれているような挙動は確認できません。

 lsusbでこのデバイス(idVendor=054c, idProduct=01bb)の詳細を確認してみると以下の通りでした。

$ lsusb -v -d 054c:01bb

Bus 001 Device 005: ID 054c:01bb Sony Corp. FeliCa S320 [PaSoRi]
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass          255 Vendor Specific Class
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x054c Sony Corp.
  idProduct          0x01bb FeliCa S320 [PaSoRi]
  bcdDevice            1.28
  iManufacturer           0
  iProduct                0
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0019
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              10

 特に興味深い情報は無さそうですが、バスパワーでも最大100mAしか消費しないことが判りますので、RaspberryPiなど電力供給能力が心もとない機器と組み合わせても不安は少なそうです。

 ここまでで、ハードウェアとしては認識されていることが確認できていますが、当然ながらソフトウェアが無ければPasoriを使ってFelicaにアクセスすることはできません。調べてみるとlibpafeというライブラリを使うとRC-S320Linuxで使うことが出来そうです。

libpafeインストール

 libpafeは下記リポジトリで公開されています。
github.com
 最終更新が12年前の2011年で止まっていますので、そのまま動くのか不安がありましたが、最低限の知識があれば今でも使用可能です。
 READMEにインストール手順が載っていますので、(基本的には)ほぼその通り作業しました。

# download source code
$ git clone https://github.com/rfujita/libpafe

# configure
$ cd libpafe
$ ./configure

# make (multi-thread)
$ make -j4

# install (require sudo)
$ sudo make install

 最後のmake install中に、以下の出力が含まれています。このmakeスクリプトでは/usr/local/libに出来上がったlibpafeのバイナリをデプロイしてくれるのですが、ここに置いたライブラリをリンクするには、例示されてるような手段でライブラリのパスを指定してあげる必要があるよと言ってるようです(後述)。

Libraries have been installed in:
/usr/local/lib

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the `LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the `LD_RUN_PATH' environment variable
during linking
- use the `-Wl,-rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.

 

udev設定

 READMEの指示通り、/etc/udev/pasori.rulesを以下の内容で作成します(要sudo)。

ACTION!="add", GOTO="pasori_rules_end"
SUBSYSTEM=="usb_device", GOTO="pasori_rules_start"
SUBSYSTEM!="usb", GOTO="pasori_rules_end"
LABEL="pasori_rules_start"

ATTRS{idVendor}=="054c", ATTRS{idProduct}=="01bb", MODE="0664", GROUP="plugdev"
ATTRS{idVendor}=="054c", ATTRS{idProduct}=="02e1", MODE="0664", GROUP="plugdev"

LABEL="pasori_rules_end"

RC-S320で使用するだけならidProduct=02e1の行(恐らくRC-S330)は無くても問題無いはずです。

 同様にREADMEの指示のように/etc/udev/rules.dにシンボリックリンクを貼った後に設定を反映します(要sudo)。

cd /etc/udev/rules.d
sudo ln -s ../pasori.rules 010_pasori.rules
sudo udevadm control --reload-rules

RC-S320を既にUSBポートに接続している場合は一旦抜いて接続し直しましょう。reload-rulesしても接続し直すまでは反映されません。
 

動作確認

 libpafe/testsに2つサンプルプログラム(pasori_testとfelica_dump)が有るので実行してみましょう。
 pasori_testはここまでの手順に問題が無ければ以下のような出力が得られるはずです。

$ ./pasori_test
PaSoRi (RC-S320)
firmware version 1.40
Echo test... success
EPROM test... success
RAM test... success
CPU test... success
Polling test... success

 もし、

$ ./pasori_test
error

 となった場合はsudoして実行してみてください。sudoでsuccess表示に変わった場合、udev周りの設定をどこか間違えていますので確認してください。

 felica_dumpはタイムアウトでエラーにならないように予めPasoriSuicaおサイフケータイのようなFelicaを載せた状態で実行しましょう。すると以下のような出力が得られます。

$ ./felica_dump
# lpdump : Fri Jun 16 19:52:58 2023
# --- IDm info (FeliCa) ---
# Manufacture Date = 2015/7/30
#               SN = ****
# Manufacture Code = 0101
#      Equip. Code = 0108
  system num 3
# FELICA SYSTEM_CODE = 0003
# card IDm = ****************
# card PMm = ****************
# area num = 8
# service num = 50
~以下略。一部の値は*で投稿用にマスクしています。~

 ここまでで無事にPasoriFelicaにアクセスできていることが確認できました。
 次は自作プログラムでFelicaのIDを読み出してみましょう。
 

IDmを読み出す

 前提として個々のFelica毎に異なる8byteのIDがIDmと呼ばれています。この値は製造時に設定され変更することができないユニークな値です。このため、IDmとユーザーを予め紐づけておくことで、例えば出欠確認のようなアプリケーションができます。というわけで、野良FelicaアプリではIDmの読み出しが主な目的となることが多いようです。
 ここではIDmを読み出して表示するだけの簡単なプログラムをCで書いてみましょう。libpafeのREADMEに関数リファレンスが載ってますので、IDmを読み出す関数はfelica_get_idm()であることが判ります。これを叩くための最小限のコードの実装例は以下のようになります。やってることはPasoriを開いて初期化した後、ポーリングしてFelica情報を得てIDmを取り出して表示、最後に後始末といった感じです。

#include	<stdio.h>
#include	<stdlib.h>
#include	<stdint.h>
#include	"libpafe.h"

int main(void){
	int ret_code = EXIT_SUCCESS;
	pasori *psr;
	felica *flc;
	uint8_t *idm;
	
	// Memmory allocation for IDm
	idm = (uint8_t*) malloc(FELICA_IDM_LENGTH);
	if(idm == NULL){
		fprintf(stderr, "Memory allocation error\n");
		return EXIT_FAILURE;
	}
	
	// Open pasori
	psr = pasori_open();
	if(psr == NULL){
		fprintf(stderr, "Failed to open pasori\n");
		return EXIT_FAILURE;
	}
	
	// Initialize pasori
	if(pasori_init(psr) != 0){
		fprintf(stderr, "Failed to initialize pasori\n");
		ret_code = EXIT_FAILURE;
	}
	else{
		// Polling
		flc = felica_polling(psr, FELICA_POLLING_ANY, 0, 0);
		if(flc == NULL){
			fprintf(stderr, "Failed to polling\n");
			ret_code = EXIT_FAILURE;
		}
		else{
			// Get IDm
			if(felica_get_idm(flc, idm) != 0){
				fprintf(stderr, "Failed to get IDm\n");
				ret_code = EXIT_FAILURE;
			}
			else{
				fprintf(stdout, "IDm: ");
				for(int i=0; i<FELICA_IDM_LENGTH; i++){
					fprintf(stdout, "%02X", *(idm+i));
				}
				fprintf(stdout, "\n");
			}
		}
	}
	
	// Free the memory
	free(idm);
	if(flc != NULL)	free(flc);
	
	// Close pasori
	pasori_close(psr);
	
	return ret_code;
}

 このソースコードをreadIDm.cとしてコンパイルを試みると、恐らくlibpafe.hが見つからないとエラーが発生するはずです。
 libpafeをmake installしたときのmakeスクリプトを見るとヘッダファイルは/usr/local/include/libpafe/にデプロイされていますので、以下のように明示的に指定してコンパイルしてみましょう。

gcc ./readIDm.c -I/usr/local/include/libpafe/ -lpafe -Wall -o readIDm

 今度はコンパイルできたと思いますが、実行してみるとライブラリが見つからないとエラーが発生するはずです。libpafeをmake installしたときに現れたメッセージに書かれていたように、ライブラリのパスを明示する必要がありますので、以下のように指定してコンパイル(リンク)しなおしてみましょう。

gcc ./readIDm.c -I/usr/local/include/libpafe/ -Wl,-rpath /usr/local/lib -lpafe -Wall -o readIDm

 これで出来たバイナリをPasoriFelicaを載せて実行してみると16桁(8byteの16進数表現)のIDmが標準出力に出力されるはずです。そしてその値はlibpafeに同梱のサンプルプログラムのfelica_dumpの出力に含まれる最初のIDmの値と同一となっていればlibpafeを使用した自作プログラムで正常にIDmが読み出せていると判断できます。

 libpafeを使用すれば、このような100行にも満たない比較的小さなコードで容易にIDmを読み出すことができると解りました。*1
 



以上。

*1:蛇足ながらコード書くよりも冒頭に載せた良さげな感じの挿絵をBing Image Creatorに出力させる方がプロンプトの試行錯誤が多く大変でした。