【OSS】nfcpyにコントリビュートを目指して
前回→ICカードリードしたかった By (Mac, RC-S300)
Windows(WSL)で抗ってみます。
USBプログラミング初めてなので頑張ります!
目的
nfcpyにRC-S300を対応すること
https://github.com/nfcpy/nfcpy/issues/214 のissueを解決しPRを出す
課題
RC-S300のVID, PIDを見つける
RC-S300の情報のやりとりをソースコードで表現する
方法(進捗)
開発環境:
WSL2 (Ubuntu22.04)
python3.10 (venv)
各所 ${プロジェクトディレクトリ}で置き換えています。このディレクトリの構成は以下の通りです
code: bash
$ tree -L 1 ${プロジェクトディレクトリ}
.
├── main.py
└── venv
ひとまず、以下のコードを起点に解析を進める
code: main.py
import nfc
with nfc.ContactlessFrontend("usb") as clf:
print(clf)
1. VID, PIDを見つける
VIDはvendorIDでSonyなどの企業を指すIDです。
PIDはproductIDでRC-S380などの製品を指すIDです。
lsusbコマンドで調べたところ
code: bash
$ lsusb
...
Bus 001 Device 002: ID 054c:0dc9 Sony Corp. FeliCa Port/PaSoRi 4.0
0x054cがVID, 0x0dc9がPIDだとわかる
今度からSonyのことは0x054cと呼んでやりましょう
補足:WSLでUSBプログラミングする時の注意点
デフォルトのWSLはUSBの情報を取得できないので気をつけましょう
https://learn.microsoft.com/ja-jp/windows/wsl/connect-usb を参考にして、wslにデバイスをアタッチすること
Wiresharkインストール後(というかUSBPcapをインストール後)にアタッチしようとすると
「usbipd: warning: The service is currently not running; a reboot should fix that.」とでるかもしれないので
code: powershell
Restart-Service "usbipd"
でサービスを再起動させよう。
※ちなみにアタッチするとき既にVIDとPIDはわかります
usb_device_mapに追加する
code: nfc/clf/device.py
...
usb_device_map = {
(0x054c, 0x0193): "pn531", # PN531 (Sony VID/PID)
(0x04cc, 0x0531): "pn531", # PN531 (Philips VID/PID), SCM SCL3710
(0x04cc, 0x2533): "pn533", # NXP PN533 demo board
(0x04e6, 0x5591): "pn533", # SCM SCL3711
(0x04e6, 0x5593): "pn533", # SCM SCL3712
(0x054c, 0x02e1): "rcs956", # Sony RC-S330/360/370
(0x054c, 0x06c1): "rcs380", # Sony RC-S380
(0x054c, 0x06c3): "rcs380", # Sony RC-S380
(0x054c, 0x0dc9): "rcs300", # Sony RC-S300 <= New!!
(0x072f, 0x2200): "acr122", # ACS ACR122U
}
...
実行してみる
code: bash
$ python main.py
Traceback (most recent call last):
File "${プロジェクトディレクトリ}/main.py", line 4, in <module>
with nfc.ContactlessFrontend("usb") as clf:
File "${プロジェクトディレクトリ}/venv/lib/python3.10/site-packages/nfc/clf/__init__.py", line 76, in __init__
raise IOError(errno.ENODEV, os.strerror(errno.ENODEV))
OSError: Errno 19 No such device
少し調べると、チュートリアルのようなものを発見 https://nfcpy.readthedocs.io/en/latest/topics/get-started.html
code: bash
$ python -m nfc
...
-- better assign the device to the 'plugdev' group
sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"0dc9\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'
sudo udevadm control -R # then re-attach device
I'm not trying serial devices because you haven't told me
なるほど、udevadmというLinuxのデバイスマネージャーさんにrcs300が入部したことを教えるのですね
code: bash
$ sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"0dc9\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'
$ sudo udevadm control -R
Failed to send reload request: No such file or directory
ちゃんと読めって話ですが、「入部届を出してからじゃないと僕(udevadm)は入部許可しません!」っていってましたね(超意訳)。なんか許可がないと何もできない感じ、日本社会ぽいですね。「よしなにやってくれよ~」って思います。
WSLの場合複雑なのですが、
1. USBを物理接続し、WSLにアタッチ
2. sudo sh -c '...' (ただし、過去に実行した場合はスキップ)
3. sudo service udev restart でデーモン再起動
4. sudo udevadm control -R
5. USBを抜く
6. 再度接続、アタッチする
code: bash
$ python main.py
Traceback (most recent call last):
File "${プロジェクトディレクトリ}/main.py", line 4, in <module>
with nfc.ContactlessFrontend("usb") as clf:
File "${プロジェクトディレクトリ}/venv/lib/python3.10/site-packages/nfc/clf/__init__.py", line 75, in __init__
if path and not self.open(path):
File "/${プロジェクトディレクトリ}/venv/lib/python3.10/site-packages/nfc/clf/__init__.py", line 149, in open
self.device = device.connect(path)
File "${プロジェクトディレクトリ}/venv/lib/python3.10/site-packages/nfc/clf/device.py", line 88, in connect
driver = importlib.import_module("nfc.clf." + module)
File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(namelevel:, package, level)
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'nfc.clf.rcs300'
次の課題が判明しました。rcs300というモジュールを作成することです。
2. Wiresharkでパケキャプしてみる
rcs300のモジュールを作成するには、そのプロトコルを理解する必要がありそうです。
具体的には、どの命令が出された時にどういうデータが流れるか。ということです。
こういうときは安価にWiresharkを使ってみます。
wiresharkはUSB通信のパケキャプもできるので便利です。
余談)研究でもwiresharkと睨めっこしてます。ethercatのプロトコル難しい!最近はpythonのsocketでスレーブ制御できるかどうかやってます。
https://scrapbox.io/files/64abf4b2f504b1001cf4315a.png
RC-S300を特定する
私のUSB事情は複雑でハブを通して様々なデバイスが接続されているので抜いたりして、「RS-S300にICカードをかざした時にパケットが流れる」これを特定します。
https://scrapbox.io/files/64abf838d51cbe001b03ffff.png
特定できました。
基本知識無しでは解析できないので、先にUSBCCIDやらFelicaなどのプロトコルを理解する必要がありそうですね。
3. 仕様を理解する
https://www.sony.co.jp/Products/felica/business/tech-support/ を参照して勉強中
4. 解析開始
1回のデータ取得で以下のプロセスを経ている
https://scrapbox.io/files/64ac244adee750001ce8677c.png
ざっくりまとめると以下の通りである
1. Escape
2. ICC Power Off
3. Slot Status
4. Data Block
5. Transfer Block
(4 ~ 5を計8回繰り返す)
6. ICC Power Off
7. Slot Status
パケット構成
USB URB
USB CCID
Data
戦略
とりあえずDataに着眼する
2パターン以上試し、何かしらの傾向を探す
関係ないけど、放置するとリードしてくれない現象どうにかしたい。
スリープモードみたいなものがあるのか?
リードしていた。起動時と異なりLEDランプが点滅しないので安定状態ではこれが正常なのだろう
※リードをしているときはLEDが点滅すると教えてくれたドキュメントが間違っている!?
パターン1
転がっていたAimeを使う
1. Escape
ホスト(PC)からカードリーダーへの通信: Data: ffcaf00000
カードリーダーからホスト(PC)への通信: Data: 019000
2. ICC Power Off
ホスト(PC)からカードリーダーへの通信
Dataのやりとり無し
3. Slot Status
カードリーダーからホスト(PC)への通信
Dataのやりとり無し
4. Data Block
カードリーダーからホスト(PC)への通信
1回目: Data: 3b8f8001804f0ca000000306030001000000006a
2回目~8回目: Data: 6a82
5. Transfer Block
ホスト(PC)からカードリーダーへの通信
1回目: Data: 00a404000ba0000003974349445f0100
2回目: Data: 00ca7f6800
3回目: Data: 00a4040009a00000030800001000
4回目: Data: 00a4040009a00000039742544659
5回目: Data: 00a404000ba0000003974349445f0100
6回目: Data: 00ca7f6800
7回目: Data: 00a4040009a00000030800001000
8回目: Data: 00a4040009a00000039742544659
1, 2, 3, 4と5, 6, 7, 8が同じ
6. ICC Power Off
2と同じ
7. Slot Status
3と同じ
パターン2
同じAimeで2回目(別日にパケキャプ)
https://scrapbox.io/files/64b23161f58e74001cc9f106.png
あれ、なんかプロセス減ってる。
こっちが正しいのか?
でも、Data Blockが一番しりたいデータであることは確証できた
https://scrapbox.io/files/64b232385fc5c7001c96ad0a.png
パターン1のData Blockと同じ
改めてhttps://www.sony.co.jp/Products/felica/business/tech-support/data/M621_FormatGuidelines_1.21j.pdf
を読む。