TL;DR
- Raspberry Pi 3B+ に Sony PaSoRi RC-S330(
054c:02e1)を挿してpython -m nfcするとinput/output error while waiting for ackで初期化に失敗する。 - 権限でもドライバでもなく、Pi3 の USB コントローラ
dwc_otgの split-transaction の不具合が原因。64バイトを超える bulk-OUT 転送に応答が返らない。 - nfcpy は起動時の自己テスト
diagnose('line')で 265バイト送るため、リーダーを使う前に必ず死ぬ。 - カーネル起動オプションに
dwc_otg.fiq_fsm_enable=0 dwc_otg.fiq_enable=0を「セットで」 追加すると直る。 - 落とし穴:
fiq_enable=0を単独指定すると効かない(ドライバが矛盾とみなして補正で戻す)。
環境
- Raspberry Pi 3 Model B+ / Raspberry Pi OS(aarch64, kernel 6.x)
- Python 3.13 + nfcpy 1.0.4(venv)
- PaSoRi RC-S330(FeliCa S330, USB
054c:02e1、RC-S956 チップ)
症状
$ python -m nfc
This is the 1.0.4 version of nfcpy run in Python 3.13 ...
I'm now searching your system for contactless devices
ERROR:nfc.clf.rcs956:input/output error while waiting for ack
Sorry, but I couldn't find any contactless device
lsusb ではちゃんと見えているし、pcscd などの競合もない。udev で plugdev 権限も付けてある。困った。
切り分け
権限・競合は無関係
- デバイスノードは
crw-rw-r-- root plugdev、ユーザーはplugdev所属で rw 可。 -
pcscdは inactive、デバイスを掴んでいるプロセスも無し。 - USB リセット(
USBDEVFS_RESET)を投げても変化なし。
エラーの発生箇所
nfcpy の pn53x.py の command() を追うと、コマンド送信(write_frame)は成功し、その直後の ACK 読み取り(read_frame)で IO エラーになっていた。command() は read の例外を種類問わず「input/output error while waiting for ack」に丸めるので、実体は タイムアウト(チップが応答しない) だった。
転送サイズの境界を測る
エコーコマンドをデータ長を変えて送ってみると——
| フレーム長 | 結果 |
|---|---|
| 64 バイト | OK |
| 65 バイト | 無応答(タイムアウト) |
ちょうど USB フルスピード bulk の最大パケットサイズ 64 バイトが境界。つまり 複数 USB パケットにまたがる bulk-OUT 転送が届いていない。
なぜ nfcpy が即死するか
nfcpy は Device.__init__ で必ず chipset.diagnose('line') という通信自己テストを実行し、これが 262 バイトのデータ(フレーム長 273 バイト) を送る。64バイトの壁を超えるので、リーダーを一度も使わないうちに初期化が落ちる。小さいコマンド(GetFirmwareVersion 等)は通る。
原因:Pi3 の dwc_otg split-transaction
- RC-S330 は フルスピード(12Mbps) デバイスで、Pi3 の内蔵ハイスピードハブ配下にぶら下がる。
- Pi3 の USB コントローラ
dwc_otgは、これを split transaction で駆動する。 - この経路の マルチパケット bulk-OUT 転送に不具合があり、64バイトを超えると応答が返らない。
リーダー側のファームは正常(PC につなげば 265 バイトも通る前提で nfcpy は作られている)。ホスト側 USB スタックの問題。
対処
カーネル起動オプションで FIQ を完全に無効化する。Raspberry Pi OS なら /boot/firmware/cmdline.txt(1行のスペース区切り)の末尾に追記:
dwc_otg.fiq_fsm_enable=0 dwc_otg.fiq_enable=0
再起動後、適用されたか cmdline ではなく実際の値で確認:
cat /sys/module/dwc_otg/parameters/fiq_enable # N なら無効化成功
cat /sys/module/dwc_otg/parameters/fiq_fsm_enable # N
dmesg に FIQ disabled / FIQ split-transaction FSM disabled が出ていれば OK。これで 64バイトの壁が消え、265バイト級のフレームも往復し、python -m nfc がリーダーを検出する。
** found Sony RC-S330 RCS956v1.30 at usb:001:004
ハマりどころ:fiq_enable=0 単独は効かない
最初 dwc_otg.fiq_enable=0 だけを指定したが、まったく効かなかった。原因はドライバの整合性チェック:
dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.
FSM が有効なのに FIQ だけ無効、という矛盾をドライバが「補正」して FIQ を有効に戻してしまう。/sys/module/dwc_otg/parameters/fiq_enable が Y のままで気づいた。
→ 必ず fiq_fsm_enable=0 と fiq_enable=0 をセットで指定すること。
試して効かなかった/不要だったもの:
| 設定 | 結果 |
|---|---|
fiq_fsm_enable=0 単独 |
65バイトが時々通る程度。壁は残る(NG) |
fiq_enable=0 単独 |
ドライバ補正で無効化されず(NG) |
dwc_otg.speed=1(USB全体を低速固定) |
直る見込みだが全USBが12Mbps化(有線LANも低速化)。FIQ無効化で解決したので不要 |
補足:ハードを選べるなら
Pi 4 / Pi 5 は USB コントローラが dwc_otg ではなく xHCI なので、この不具合自体が出ない。リーダーを安定運用したいなら Pi4/5 が無難。
まとめ
-
input/output error while waiting for ackは dwc_otg の split-transaction で >64バイトの bulk-OUT が落ちるのが真因。 -
dwc_otg.fiq_fsm_enable=0 dwc_otg.fiq_enable=0をセットで入れて再起動。 - 効いたかは
/sys/module/dwc_otg/parameters/の実値で確認。 - 確実に避けたいなら Pi4/5。