[Linux] 複数のシリアルデバイスのパスを固定にする

Linuxで複数のシリアルデバイスを同時に使うケース。例えばGPSモジュールとLTE通信モジュールを同時に接続した時に、標準であればどちらも /dev/ttyUSB0 か /dev/ttyUSB1 と認識されますが、末尾の数字は認識された順番で割り振られるために、起動時のタイミングによっては逆のモジュールに当たってしまうことがあり得ます。これでは、自動起動する状態のプログラムではうまくデバイスにアクセスすることができない状態になってしまいます

これを防ぐためにudevのルールを書いて固定なパスを定義することで、どのような順番であってもモジュールにアクセスできるようにします

# udevのルールについて

udevはデバイスにアクセスするためのデバイスファイルを管理するためのシステムです。デバイスが接続された時に `/dev` 配下にデバイスファイルを作成したり、逆に切断した時に削除することもありますが、ここでは特定のシリアルデバイスが接続された時にシンボリックリンクを作成して固定されたパスで特定のデバイスにアクセスできるためのルールについて説明します

## ルールのファイルについて

udevのルールは `/etc/udev/rules.d` 配下に置きます。ファイル名は2桁の数字と `-` から始まり、 `.rules` という拡張子が付きます

```shell:udevファイル名の例
70-persistent-net.rules
```

2桁の数字は番号が若い順から呼ばれます。その為、ユーザーが任意に定めたルールであれば `10` などの若い番号を指定して優先的に適用させる方がいいでしょう。同じデバイスに対するルールが先に呼ばれてしまうと、後の番号のルールは適用されなくなってしまうからです

## 適用されているルールの確認

あるデバイスに対して適用されているルールを確認してみます。以下は `/dev/sda` に対してのudevのルールを確認する例[^1]です

```shell-session
# udevadm test `udevadm info -q path -n /dev/sda`
```

出力例

```
# udevadm test `udevadm info -q path -n /dev/sda`
calling: test
version 3.2.9
This program is for debugging only, it does not run any program
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.

=== trie on-disk ===
tool version:          3
file size:         8270736 bytes
header size             80 bytes
strings            2110312 bytes
nodes              6160344 bytes
Load module index
timestamp of '/etc/udev/rules.d' changed
Reading rules file: /lib/udev/rules.d/01-md-raid-creating.rules
Reading rules file: /lib/udev/rules.d/10-dm.rules
Reading rules file: /lib/udev/rules.d/11-dm-lvm.rules
Reading rules file: /lib/udev/rules.d/13-dm-disk.rules
Reading rules file: /lib/udev/rules.d/40-slackware.rules
Reading rules file: /lib/udev/rules.d/40-usb_modeswitch.rules
Reading rules file: /lib/udev/rules.d/50-compat_firmware.rules
Reading rules file: /lib/udev/rules.d/50-udev-default.rules
Reading rules file: /lib/udev/rules.d/60-block.rules
Reading rules file: /lib/udev/rules.d/60-cdrom_id.rules
Reading rules file: /lib/udev/rules.d/60-drm.rules
Reading rules file: /lib/udev/rules.d/60-evdev.rules
Reading rules file: /lib/udev/rules.d/60-input-id.rules
        :
```

このコマンドで指定したデバイスに対するルールを探していることが分かります。他にも色々な事が出力されていますが、詳しい説明は他を参考にしてください。ここでは自分で書いたルールが正しく読み込まれていることの確認までで留めておきます

`/lib/udev/rules.d` にあるルール(システムで用意されているルール)と `/etc/udev/rules.d` にあるルール(ユーザーで設定したルール)が2桁の番号順で読み込まれます。正しく読み込まれていれば `Reading rules file: ` に続いてファイル名が出ますが、ルールにエラーがあるとエラーメッセージが出てくるので注意してみましょう

## ルールの書き方

udevのルールの書き方については http://www.reactivated.net/writing_udev_rules.html を参考にしてください

udevのルールを上手く使いこなせるようになると、デバイスが接続された時に特定のコマンドを実行するというような事も行えるようになります。

参考

* Improved Udev Rule For Arch Linux
  - https://soosck.wordpress.com/2011/01/19/improved-udev-rule-arch-linux/

# 同じデバイスを複数使いたい場合

`/dev/serial/by-id/` 配下に接続したUSBポートによって割り振られるパスが生成されるので、これにシンボリックリンクを作成してアクセスする例がありました

http://tclip.blog.fc2.com/blog-entry-86.html

しかしこの方法はカーネルのバージョンアップや、USBコントローラーが複数ある場合(増設ボードをつけているだけでなく、USB3.xのポートとUSB2.0のポートが混在するボードの場合なども)でも認識される順番が変わってしまう可能性が考えられ、使える環境が限られる方法となるので注意が必要です

# 参考

* udev - ArchWiki
  - https://wiki.archlinux.jp/index.php/Udev#USB_.E6.8C.BF.E5.85.A5.E3.81.A7.E5.AE.9F.E8.A1.8C

[^1]: どこかのサイトには `udevinfo` というコマンドが紹介されているかもしれないですが、これは現在では `udevadm info` に置き換わっています