（Ubuntuで）WebUSBでPasoriを扱ってみる


# 結論（2行）
- udevを設定し、root以外でUSBにアクセス出来る様にする。
- PasoriのUSB Driverをunbindする。`sudo sh -c "echo -n '1-13:1.0' >  /sys/bus/usb/drivers/port100/unbind"`

# 実行環境
OS ： Ubuntu 18.04
NFC-Reader : Pasori - Sony RC-S380/P

# はじめに
[WebUSBでFeliCaの一意なIDであるIDmを読む][1]という記事を読みまして、WebUSBでNFCカードが読み込める、という事を知り、私も実際に試してみる事にしました。

ところが実際に自分の環境（Ubuntu）でやってみた所、全く動作しません。

次に、[Web USBでPaSoRiを扱えるOSをまとめてみた][2]という記事を読みました。
どうも、MacOSとAndroid以外では動かないらしい、との情報を得たのですが、どうにも諦めきれません。Mac買えないので…。

[1]:https://qiita.com/saturday06/items/333fcdf5b3b8030c9b05
[2]:https://qiita.com/attakei/items/95cb9b53fa3ede942b3e

色々と情報を調べているうちに、（かなり面倒ではありますが）設定を行えば、Ubuntu環境でもPasoriをWebUSBで動かせそうだという事がわかったので、備忘録としてメモ書きです。


# 最初の実行
[WebUSBでFeliCaの一意なIDであるIDmを読む][1]という記事で、デモページが紹介されています。
このページで、Felicaリーダに接続を実行すると、「SecurityError: Access denied.」と表示され、実行出来ません。

【注意】信頼出来ないWebページで、WebUSBを実行しない様にしてください。

# udev設定
「Access denied」と表示された理由は、Pasoriへのアクセス権が、rootにしか無い事が原因です。
そのため、udevの設定を行い、root以外で、Pasoriにアクセス出来る様にする必要があります。

lsusbを実行し、PasoriのVendorIDとProductIDを調べます。この場合は、VendorIDが054c、ProductIDが06c3です。

```
$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
...(省略)
Bus 001 Device 010: ID 054c:06c3 Sony Corp. 
```

次に、udevのrulesファイルを作成します。

```
$ sudo vim /etc/udev/rules.d/90-nfc.rules
```

vimを開き、下記内容を記載して、保存します。
groupsと書かれた部分には、ログインユーザのグループ名を記載してください。また、ATTRS{idVendor}とATTRS{idProduct}の部分には、lsusbで調べたIDを記載してください。

```
SUBSYSTEMS=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="06c3", ACTION=="add", GROUP="groups", MODE="660"
```

udevを再起動します。

```
$ sudo systemctl restart udev
```

udev再起動後、Pasoriを一度抜いて、再度接続する必要があります。chromeの再起動も必要かも知れません。

これで実行すると、「NetworkError: Unable to claim interface.」と表示されます。
「SecurityError: Access denied.」の表示では無くなった為、アクセスは出来るようになったものと推察します。

# USB DriverのUnbind

root以外でアクセス出来る様にはなりましたが、まだ動作はしません。
調べた所、stackoverflowに下記のようなトピックスがありました。

[Failed to claim interface 0: Device or resource busy][3]

ここに書かれている情報を参考にして、Pasoriのシステムドライバ(と思われるもの)を無効にする事で、WebUSBが動作する事を確認しました。

### lsusbでPasoriデバイスの情報を得る。

lsusbコマンドを使って、USBのBusとPortの情報を取得します。

```
$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
...(省略)
Bus 001 Device 010: ID 054c:06c3 Sony Corp. 

$ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/16p, 480M
    ...(省略)
    |__ Port 13: Dev 10, If 0, Class=Vendor Specific Class, Driver=, 12M
```

`lsusb`コマンドを実行し、PasoriのDevice番号を調べます。この場合は010です。
次に、`lsusb -t`コマンドを実行し、Port番号を調べます。デバイス番号（Dev）が10になっている行を調べます。この場合は13です。

これで、BUSが1、Portが13である事がわかりました。


### Pasori Driver(???)をunbindする。

`/sys/bus/usb/drivers`の中身を調べます。`port100`というディレクトリが存在しています。
（環境によっては、port100という名前で無い可能性があります）

```
$ ls -ltr /sys/bus/usb/drivers
合計 0
drwxr-xr-x 2 root root 0  8月 14 09:25 usbfs
drwxr-xr-x 2 root root 0  8月 14 09:25 usb
drwxr-xr-x 2 root root 0  8月 14 09:25 hub
drwxr-xr-x 2 root root 0  8月 14 09:25 usbhid
drwxr-xr-x 2 root root 0  8月 14 09:25 cdc_acm
drwxr-xr-x 2 root root 0  8月 14 09:25 snd-usb-audio
drwxr-xr-x 2 root root 0  8月 14 09:25 btusb
drwxr-xr-x 2 root root 0  8月 14 14:43 port100
```

きちんと調べた訳では無い為、正しいかどうかは解りませんが、これがPasoriのシステムドライバの様です。（断言はしません）

`port100`の中身を確認します。

```
$ ls -ltr /sys/bus/usb/drivers/port100/
合計 0
--w------- 1 root root 4096  8月 14 14:43 uevent
-rw-r--r-- 1 root root 4096  8月 14 15:02 remove_id
-rw-r--r-- 1 root root 4096  8月 14 15:02 new_id
lrwxrwxrwx 1 root root    0  8月 14 15:02 module -> ../../../../module/port100
--w------- 1 root root 4096  8月 14 15:02 bind
--w------- 1 root root 4096  8月 14 15:21 unbind
lrwxrwxrwx 1 root root    0  8月 14 16:26 1-13:1.0 -> ../../../../devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13:1.0

```

`1-13:1.0`の様に、数値で始まるファイル名がある事を確認します。
「1-13」となっていますが、これはUSBのBUSとPort番号を表しています。lsusbで調べた情報と一致する筈です。

Pasoriドライバを無効化します。無効化コマンドは下記の通りです。
なお、`1-13:1.0`の部分には、先程調べたファイル名を記載します。

```
sudo sh -c "echo -n '1-13:1.0' >  /sys/bus/usb/drivers/port100/unbind"
```

余談ですが、最初、下記のように実行して失敗しました。

```
sudo echo -n '1-13:1.0' >  sudo /sys/bus/usb/drivers/port100/unbind
```

更に余談ですが、Pasoriドライバを再び有効化する場合は、下記のコマンドを実行します。
もしくは、USBを一度引き抜いて、再度差し込んでも同じです。

```
sudo sh -c "echo -n '1-13:1.0' >  /sys/bus/usb/drivers/port100/bind"
```

これで私の環境では実行出来る様になりました。

# 問題点
udevの設定は、一度設定を行えば、再度設定をする必要はありません。

問題はdriverのunbindの方です。
**一度Pasoriを引き抜き、再度差し込むと、Driverが再び読み込まれる為、WebUSBへのアクセスが出来なくなります。**
PCを再起動しても同様です。

Pasoriを抜き差しした際は、必ずunbindコマンドを再実行する必要があります。

Pasoriデバイス挿入時に、unbindが自動実行されるように設定すれば、解決するとは思います。
軽く調べた範囲では、udev設定で、コマンドの自動実行が出来そうですが、ここではその方法は紹介しません。

# なぜこれで動作するのか
[Failed to claim interface 0: Device or resource busy][3]を読むと、この様な事が書かれています。

>While this should allow you to use this device with WebUSB it is not likely useful as a long term solution because of the manual steps involved. WebUSB is designed for devices without built-in operating system drivers.
>
>Google翻訳
>これによりWebUSBでこのデバイスを使用できるようになりますが、手作業のために長期的な解決策としては役に立たない可能性があります。 WebUSBは、オペレーティングシステムドライバが組み込まれていないデバイス用に設計されています。

[3]:https://stackoverflow.com/questions/47695160/failed-to-claim-interface-0-device-or-resource-busy

この情報を信用する限り、OSでシステムドライバが用意されるデバイスは、WebUSBで動かす事が出来ない、という事の様です。

Pasoriリーダーは、Linuxでシステムドライバが用意されている為、WebUSBで動かす事が出来ないが、システムドライバを無効にする事で、WebUSBで動かす事が出来る様になった、という事なのだと思います。

# 補足（2019/4/21追記）

Windowsで実施した際に、私の環境では動作しなかった、という現象を、2019/3/27に確認しています。

内容としては、「TypeError: A filter containing a protocolCode must also contain a subclassCode.」というエラーが現れ、実行出来なかった、というものです。

Linuxでこの現象が発生するかどうかは解りませんが、解決策を下記記事で書いていますので、もし、同じ様なエラーに遭遇された方は、こちらを参照してみて下さい。

（Windowsで）WebUSBでPasoriを扱ってみる
https://qiita.com/frameair/items/abcaebbd654c304a0906

# 終わりに
他のLinuxディストリビューションでも、似た様な方法で出来そうな気がします。（検証はしていませんが。）

時間があったら、Raspberry PI ~~や、Windows~~ で出来るかどうか試してみたいです。
~~試してはいませんが、もしかしたら、WindowsでもUSBドライバを無効にすれば、WebUSBでPasoriが使える様になるのでしょうか…。~~

Windowsで試してみました。

（Windowsで）WebUSBでPasoriを扱ってみる
https://qiita.com/frameair/items/abcaebbd654c304a0906

# 参考にしたQiitaの記事
WebUSBでFeliCaの一意なIDであるIDmを読む
https://qiita.com/saturday06/items/333fcdf5b3b8030c9b05

Web USBでPaSoRiを扱えるOSをまとめてみた
https://qiita.com/attakei/items/95cb9b53fa3ede942b3e

# 参考資料
libusb のデバイスを udev ルールで root 以外にアクセス許可する方法
http://d.hatena.ne.jp/ayokoyama/20140407/p1

Failed to claim interface 0: Device or resource busy
https://stackoverflow.com/questions/47695160/failed-to-claim-interface-0-device-or-resource-busy

Manual driver binding and unbinding
https://lwn.net/Articles/143397/