ミルク色の記録

やったこと、やってみたこと

Raspberry PI ZeroのUSB OTGで遊んだ

Raspberry PI Zeroのセットアップで、電源供給用じゃない方のマイクロUSBポートとホストPCを繋いで仮想ネットワーク経由で接続する云々の情報をみて、どういう仕組みでやっているんだろうと調べてみたら、Raspberry PI ZeroではUSB OTG(On-The-Go)が使えるということを知った。

USB OTG (On-The-Go)

USB OTGはAndroidのUSBポートにマウスとかキーボード繋げばマウスやキーボードとして動くけど、同じポートでPCに繋いだ時はPC側でストレージとして見えるみたいなアレっぽい。

USB On-The-Go(略してUSB OTG)は、USB機器どうしを直接接続するインタフェース規格である。パソコン等をホストとせずに、動作時にホスト機器を動的に切り替える機能を拡張したもの。 IEEE 1394のように直接接続できるので、いろいろな機器に応用できる。

引用: USB On-The-Go - Wikipedia

調べてみたら手持ちのRaspberry PI B系では構造上できないようなので、Raspberry PI Zero WHを買ってみた。送料とか色々考えてとりあえずAmazonでポチリ。

シリアル通信したり、ネットワークにしたり、キーボードにしたりと色々できるみたいだけど、とりあえず今回はストレージとして使う方法を試してみた。

USB OTGでマスストレージ

環境は以前に作成したRaspbian Stretch Liteの2019-04-08バージョンのパーティションをいじって、bootとrootfsの他にextrafsを追加したもの。今出ているBusterの2019-06-20バージョンはとりあえず保留。

環境準備

まずはUSB OTGを利用するために,/boot/config.txtに次のUSB OTGの有効化を追加する。

dtoverlay=dwc2

反映するためにOSを再起動する。

続いて、ストレージとして利用するためのパーティションを作成する。イメージファイルとかでも良いらしいけど、今回は専用パーティションを作って行う。

現状のパーティションが次のとおり。

$ sudo fdisk -l /dev/mmcblk0
Disk /dev/mmcblk0: 7.4 GiB, 7932477440 bytes, 15493120 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xc1dc39e5

Device         Boot   Start     End Sectors  Size Id Type
/dev/mmcblk0p1         8192   96042   87851 42.9M  c W95 FAT32 (LBA)
/dev/mmcblk0p2        98304 6389759 6291456    3G 83 Linux
/dev/mmcblk0p3      6389760 8388607 1998848  976M 83 Linux

これに1GBのFAT32パーティションを追加して次の様にした。

$ sudo fdisk -l /dev/mmcblk0
Disk /dev/mmcblk0: 7.4 GiB, 7932477440 bytes, 15493120 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xc1dc39e5

Device         Boot   Start      End Sectors  Size Id Type
/dev/mmcblk0p1         8192    96042   87851 42.9M  c W95 FAT32 (LBA)
/dev/mmcblk0p2        98304  6389759 6291456    3G 83 Linux
/dev/mmcblk0p3      6389760  8388607 1998848  976M 83 Linux
/dev/mmcblk0p4      8388608 10485759 2097152    1G  b W95 FAT32

パーティションを切った後にOSを再起動して、vfatでフォーマットする。

$ sudo mkfs -t vfat /dev/mmcblk0p4

これでパーティションの準備は完了。

マスストレージの有効化と解除

環境を準備して次のコマンドを実行すると接続しているホストPC側にUSBストレージとして認識させることができる。 パラメータのオプションは linux/mass-storage.txt at master · torvalds/linux · GitHub を参考にした。

色々試していたらWindowsでUSBストレージとして認識させるにはremovable=yが必要だった。

$ sudo modprobe g_mass_storage file=/dev/mmcblk0p4 removable=y

lsmodで確認すると次の様に表示される。

$ lsmod | grep g_mass_storage
g_mass_storage          4651  0
usb_f_mass_storage     38658  2 g_mass_storage
libcomposite           49673  2 g_mass_storage,usb_f_mass_storage

dmesgを確認すると次のようなログが確認できる。

[   46.534849] Mass Storage Function, version: 2009/09/11
[   46.534873] LUN: removable file: (no medium)
[   46.535068] LUN: removable file: /dev/mmcblk0p4
[   46.535077] Number of LUNs=1
[   46.537657] g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
[   46.537675] g_mass_storage gadget: userspace failed to provide iSerialNumber
[   46.537681] g_mass_storage gadget: g_mass_storage ready
[   46.537695] dwc2 20980000.usb: bound driver g_mass_storage
[   46.654687] dwc2 20980000.usb: new device is high-speed
[   47.346817] dwc2 20980000.usb: new device is high-speed
[   47.414698] dwc2 20980000.usb: new device is high-speed
[   47.632787] dwc2 20980000.usb: new address 1
[   47.657761] g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage

マスストレージを解除するには次のコマンドを実行する。

$ sudo modprobe -r g_mass_storage

OS起動時にマスストレージを有効にしてみる

準備が整ったので、OS起動時に自動的にマスストレージを有効にできるようにしてみる。

最初に思いつくのは単純な方法で/etc/rc.localに起動コマンドを書いておくというもの。

/etc/rc.localを使った方法

/etc/rc.localを次の様に編集する。

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

# for MASS Storage
modprobe g_mass_storage file=/dev/mmcblk0p4 removable=y

exit 0

これで再起動して動作確認。

dmesgでマスストレージが有効化されたタイミングを見てみる。

[   25.047610] Mass Storage Function, version: 2009/09/11
[   25.047631] LUN: removable file: (no medium)
[   25.047818] LUN: removable file: /dev/mmcblk0p4
[   25.047827] Number of LUNs=1
[   25.061717] g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
[   25.061738] g_mass_storage gadget: userspace failed to provide iSerialNumber
[   25.061744] g_mass_storage gadget: g_mass_storage ready
[   25.061758] dwc2 20980000.usb: bound driver g_mass_storage
[   25.179253] dwc2 20980000.usb: new device is high-speed
[   25.558496] dwc2 20980000.usb: new device is high-speed
[   25.684040] dwc2 20980000.usb: new device is high-speed
[   25.761661] dwc2 20980000.usb: new address 1
[   25.785904] g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage

/etc/rc.localだとOS起動の最後のほうで動くので、25秒くらいかかってるのがわかる。

これだと、ホストPC側に繋いでからUSBストレージとして認識されるまでが結構待たされる気がする。

systemdでサービス化する方法

次にsystemdでoneshotなサービスを作って実行する方法でやってみる。

/etc/rc.localは元に戻しておいて、/lib/systemd/system/storage.serviceを次の様に作る。

[Unit]
Description = MASS Storage Service
After = local-fs.target

[Service]
ExecStart = /sbin/modprobe g_mass_storage file=/dev/mmcblk0p4
Type = oneshot

[Install]
WantedBy = multi-user.target

作ったサービスを有効化して、再起動する。

$ sudo systemctl enable storage.service

再びdmesgでマスストレージが有効化されたタイミングを見てみる。

[   15.075042] Mass Storage Function, version: 2009/09/11
[   15.075070] LUN: removable file: (no medium)
[   15.075312] LUN: file: /dev/mmcblk0p4
[   15.075326] Number of LUNs=1
[   15.083453] g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
[   15.083479] g_mass_storage gadget: userspace failed to provide iSerialNumber
[   15.083488] g_mass_storage gadget: g_mass_storage ready
[   15.083507] dwc2 20980000.usb: bound driver g_mass_storage
[   15.203126] dwc2 20980000.usb: new device is high-speed
[   15.225530] uart-pl011 20201000.serial: no DMA platform data
[   15.582358] dwc2 20980000.usb: new device is high-speed
[   15.707818] dwc2 20980000.usb: new device is high-speed
[   15.785478] dwc2 20980000.usb: new address 1
[   15.809602] g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage

これだと15秒くらいで有効化されている。だいたい10秒くらい縮まったか。

initramfsで有効化する方法

そもそもSDカードのパーティションへのアクセスとmodprobeが使えるタイミングなら良いわけで、initramfsでやってしまえば良いのでは...?と思ったのでやってみる。

systemdのサービスは無効化しておく。

$ sudo systemctl disable storage.service

そして、/etc/initramfs-tools/hooks/usbstorageを作る。

#!/bin/sh

. /usr/share/initramfs-tools/scripts/functions
. /usr/share/initramfs-tools/hook-functions

manual_add_modules g_mass_storage

作った/etc/initramfs-tools/hooks/usbstorageに実行権限を与えて、オーナーを変えておく。

$ sudo chmod +x /etc/initramfs-tools/hooks/usbstorage
$ sudo chown root.root /etc/initramfs-tools/hooks/usbstorage

次に/etc/initramfs-tools/scripts/local-bottom/usbstorageを作る。

#!/bin/sh

PREREQ=""

prereqs()
{
    echo "$PREREQ"
}

case $1 in
    prereqs)
        prereqs
        exit 0
    ;;
esac

. /scripts/functions

log_begin_msg "Setting up usbstorage:"
modprobe g_mass_storage file=/dev/mmcblk0p4 removable=y
log_end_msg "Done..."

exit 0

こちらも実行権限を与えて、オーナーを変えておく。

$ sudo chmod +x /etc/initramfs-tools/scripts/local-bottom/usbstorage
$ sudo chown root.root /etc/initramfs-tools/scripts/local-bottom/usbstorage

これらを使ってinitramfsを作る。

$ sudo mkinitramfs -o /boot/initrd.gz

/boot/config.txtを編集し、末尾に次のinitramfs有効化を追加する。

# enable initramfs
initramfs initrd.gz

改めて再起動して動作を確認する。

dmesgを見ると,

[    4.672923] Mass Storage Function, version: 2009/09/11
[    4.672945] LUN: removable file: (no medium)
[    4.673187] LUN: removable file: /dev/mmcblk0p4
[    4.673318] Number of LUNs=1
[    4.679278] g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
[    4.679300] g_mass_storage gadget: userspace failed to provide iSerialNumber
[    4.679308] g_mass_storage gadget: g_mass_storage ready
[    4.679328] dwc2 20980000.usb: bound driver g_mass_storage
[    4.795750] dwc2 20980000.usb: new device is high-speed
[    5.172263] dwc2 20980000.usb: new device is high-speed
[    5.250174] dwc2 20980000.usb: new address 1
[    5.274662] g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage

起動してから5秒くらいで有効化されるようになった。30秒近く待たされるより速くなったと思う。