=kthrtty/(+blog)

最近旅してないフルスタックサラリーマンジャーマネの旅行の記録

大画面テレビの録画用HDDの代わりにRasberry Pi4のUSB OTG機能を生かしてUSBtoLAN(NAS)なストレージゲートウェイを構成する

Qiitaは洗練された技術記事しか書いちゃいけない掟があるらしいのと、プライバシについて仕事でGDPR/CCPA/ISO29134系/個情報保護法などを斜め読みしている立場もあって、Qiitaからお引越しした。

目的

最近テレビを10年ぶりに新調した。 以下のようなNASと外付けHDDに記憶装置が分かれてしまっている状況をやめたい。 (記載のないTimeCupsuleも合わせるとリビングで6台のHDDが可動しており、寝ている時にファンが結構うるさく、昔から統合したい思いがあった。)

テレビ---(USB)-----外付けHDD
  |
  +---(LAN)-------L2SW---(LAN)---NAS(DLNA Server/DTCP-IP)
                    |
                  古いテレビ(DLNA Serverへのダビング・ムーブ対応)

ストレージゲートウェイを作って、テレビはUSB外付けHDDに録画しているが、実際にはNASに録画しているように変換してあげる。その結果、1台のRAID機の電源を落とせる。

テレビ---(USB)-----ストレージゲートウェイ(Pi4)
  |                 |
  +---(LAN)-------L2SW---(LAN)---NAS(DLNA Server/DTCP-IP/外付けHDD代替)
                    |
                  古いテレビ(DLNA Serverへのダビング・ムーブ対応)

やることは極めて単純。

  • 家のNASをRaspberry Pi4(以下Pi4)にマウントして録画領域のディスクイメージを作成する。
  • Pi4のUSB OTG機能を利用してPi4が外付けHDDとして振舞うようにする。
  • 家の大画面テレビにUSB接続して、外付けHDDに予約していると見せかけてNASに録画するためのコンバータとして仕上げる。

役に立つかもしれない人

こんな人向け。

  • テレビに外付けHDDを接続して録画をしている。
  • 家にNASも持っている。
  • 外付けHDDではなくNASにLAN経由で直接録画できたらよいのにとイライラしている。
  • LGテレビのようにDLNA/DTCP-IPサーバへの録画機能に対応していない機種を利用している。
  • わざわざレコーダーを買うほどの熱意も予算はない。
  • Pi4の使い所に困っている。
  • nasneなんて持ってない。
  • USB OTGをためしたい。

USB OTG(On-The-Go)とは

通常LinuxをインストールしたPi4はUSBホストとして動作する。そのため、キーボードやマウスを認識することができる。

OTG機能に対応しているハードウェアでは、LinuxだとGadgetFSという仕組みで様々なUSBデバイス(マウスとかキーボード)に化けることができる。AndroidでUSBにマウスを挿すとポインタが表示されるような機種があるが、これもOTG機能である(mini/microのID線をGNDショートしているOTGケーブルを使う)。Androidの場合は普段はPC側がUSBホストになるところが、AndroidがUSBホストになるパターン。

ホスト/ターゲットが切り替わる契機は、

  • 設定で切り替えるものや、
  • OTGケーブルを利用するものや、
  • バイスが起動して最初の数秒だけはターゲットとして動くものや、
  • (ちょっと違うけど)USB信号線のD+をGNDと短絡させる(XiaomiなどでQualcommバイスとして認識させる)手法や、

など様々。

仕事ではUSBホストがどのようなUSBクラス、ベンダID(VID)、プロダクトID(PID)に対応しているかをチェックしたり、なんちゃってUSBファジングを実施するために、USBターゲットとして振舞うfacedancer21umap2/Raspberry Pi Zero Wを使ったりしており、意外にも有用。

OTGに対応した最も有名なハードウェアはRaspberry Pi Zero。ただ、今回は録画に差し支えるような処理性能だと困るので最初からPi4を採用した。結果的にはほとんど性能関係なさそうなのでPi Zeroでも十分かもしれない。

Pi4環境情報

Raspbian(buster)が入っている手元環境を構成変更していく。

cat /etc/os-release 
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
(略)

uname -a
Linux raspberrypi 4.19.106-v7l+ #1297 SMP Tue Feb 25 13:19:54 GMT 2020 armv7l GNU/Linux

テスト用のイメージファイルを用意する

テスト用のイメージファイルを用意する。これは最終的に同じディレクトリにマウントポイントを上書きすることを意図している。

# テスト用にローカルに録画データを記録するイメージファイルを作成する
mkdir -p /root/NAS/ForRecording/
dd if=/dev/zero of=/root/NAS/ForRecording/recording.img bs=1M count=1024
# cifsマウント失敗時の判定のためにcifs上に作るファイルと同名かつ書き込みできないようにしておく
chmod ugo+w /root/NAS/ForRecording/recording.img

# クイックフォーマットする(フォーマットはテレビに応じて。我が家はNTFSをマウントできる。)
losetup /dev/loop0 /root/NAS/ForRecording/recording.img
mkfs -t ntfs -Q -L raspi4-storage-gw /dev/loop0

# おためしマウント
mount -t ntfs /dev/loop0 /mnt
mount | grep loop0
/dev/loop0 on /mnt type fuseblk (rw,relatime,user_id=0,group_id=0,allow_other,blksize=4096)

# イメージファイル開放
unmount /mnt
losetup -d /dev/loop0

Pi4のUSB type-C(給電ポート)でOTG機能を有効化

Very simple OTG on pi4にしたがって、以下修正。

# Enable USB OTG function
dtoverlay=dwc2

モジュールロードの方法はいくつかある。(モジュールによってはうまくいかないものもある。今回のg_mass_storageモジュールはまさにそれ。)

dwc2モジュールロード

方法1)/boot/cmdline.txtrootwaitのあとにmodules-load=dwc2のようにロードするモジュールを指定する。

方法2)/etc/modulesにモジュールロードのパラメータを指定して記載する。

echo "dwc2" | sudo tee -a /etc/modules

g_mass_storageモジュールロード

rootでcrontab -eして再起動時にイメージファイルの場所を指定したうえで動作するようにcron登録。

crontab -e
(以下入力)
@reboot sudo modprobe g_mass_storage file=/root/NAS/ForRecording/recording.img removeable=1

再起動してロードされているモジュールを確認し、存在していればOK。この時点でPC/MacからUSB給電していると、ホストのマシンにすでにストレージとして認識されているはず。。

lsmod | grep -E '(dwc|mass)'
g_mass_storage         16384  0
usb_f_mass_storage     49152  2 g_mass_storage
libcomposite           57344  2 g_mass_storage,usb_f_mass_storage
dwc2                  167936  0
udc_core               53248  3 usb_f_mass_storage,dwc2,libcomposite

sambaマウントして録画領域のディスクイメージを作る

NASなしでPi4に外付けHDDを接続させてもOK。NASよりもスループットに優れた高速コンバータになると思われる。 今回はNASを持っているので、SMBマウントする。

mbclient -L {host}
(共有名を確認)

mount -t cifs //{host}/{sharename} {mount_point} -o user={user},password={password},vers=3.0,iocharset=utf8
(SMBのバージョンやロケールは環境に応じて自由に)

マウントできていそうだったらfstabに設定。リブートして自動マウントされるか確認しておく。なお、マウントタイミングではNWが疎通しておらずマウントには失敗する。単に後からmount -aたたくだけで済むようにしたいがための設定。

# cifs
//{host}/{sharename} {mount_point} cifs username={user},password={password},vers=3.0,iocharset=utf8 0 2

先ほど同様、ddするなりしてディスクイメージを作る。録画用にサイズを大きくとるわけで、この工程が一番時間がかかる。LAN経由でやったら半日くらいかかったので、ローカルの容量が潤沢なら手元で作ってコピーする方が早い。NASによっては圧縮した状態で送り込んでCLIで展開できるものもあるので、都合の良い方法を選択すること。

ここまでできたら、CIFSマウントとモジュールロードを再起動時に一連の流れでやってもらうために以下書いておく。

crontab -e
(以下入力)

# removable=1だとUSBメモリ、0だと外付けHDDとして見える
@reboot sleep 5; mount -a && sudo modprobe g_mass_storage file=/root/NAS/ForRecording/recording.img removeable=0

こんな感じになる。

image.png

速度調査

BlackMagic Disk Speed Testで測定。

ローカルのSSDは高速。 image.png

USB経由NAS書き込みだと、こんなもの。 メーカー公称値が112Mbpsだから、100Mbps弱の性能が出ているということになる。 そもそも遅すぎるのはここでは無視する。 image.png

安定運用のポイントは電源

単純にテレビのバスパワーで使っているととにかく不安定。バスパワーじゃ電源容量不足気味だし、テレビの電源落とした時の省電力設定など邪魔でしかない。 実運用するときには、セルフパワーのUSBハブ、もしくはGPIOのVCC/GNDで電力しっかり給電してあげること。

ちなみに、我が家のテレビではハブを介していると録画できなかったので、GPIOに5Vを入れて安定可動している。

追記(2020/07/24)

Linux Kernelのg_mass_storageのドキュメント. https://www.kernel.org/doc/Documentation/usb/mass-storage.txt

参考