Raspberry Pi 4BをOTG設定してUSBストレージとして使う

## この記事について

WindowsPCなどからUSBケーブル接続したラズパイにデータ転送する環境の構築手順の説明です。

* ラズパイのUSB On-The-Go(OTG)設定
* USB mass storage設定
* systemd設定
* microSDカードのROM化設定

相変わらず平日は時間が取れず週末に手を付けましたが、今回もしっかり嵌りポイントがあったので記録を残しておきたいと思います。

## 参考文献
こちらの記事を参考にしました御礼申し上げます。
* [ラズパイを録画用HDDにエミュレートする](https://shikasen-engineer.com/raspberry-pi-emulate-hdd/)
* [RASPBERRY PI ZERO WをUSBメモリにする（パーティションを使用編）](https://www.cagylogic.com/archives/2021/05/10115600/11913.php)
* [Raspberry Pi Zero W](https://www.raspberrypi.com/products/raspberry-pi-zero-w/)
* [保存したファイルのタイムスタンプ時刻が正しくない](https://armadillo.atmark-techno.com/forum/armadillo/2797)



## 環境
 
**Raspberry Pi：** Raspberry Pi 4 Model B Rev 1.2
**OS：** Raspberry Pi OS Lite 64-bit (Raspbian GNU/Linux 11 (bullseye))
**microSDカード：**　16GB、32GB各一枚ずつ
**USBケーブル：** usb-A <-> usb-c オスオス
**クライアント機：** DesktopPC(Windows11 Pro)

## 事前準備

[こちらの記事](https://qiita.com/nsaito9628/items/528ab7ccd0a62515463b#%E8%B5%B7%E5%8B%95%E7%94%A8microsd%E3%82%AB%E3%83%BC%E3%83%89)等を参考に[Raspberry Pi Imager](https://downloads.raspberrypi.org/imager/imager_latest.exe)で起動用microSDカードを作成します。

## 構築手順

### USBストレージ用記憶領域の確保

嵌りポイントその①です。初めに、USBストレージとして割り当てるボリュームを作る為に、microSDカードに空き領域を確保します。

邪道ですが手持ちのサイズ違いmicroSDカードを使って準備しました。
* Imagerで16GBカードに起動イメージを作る
* 16GBカードのimageで一度起動してupdateとupgradeする
* 16GBカードimageを保存して32GBのmicroSDカードに書き込む

記事の手順を使われる際は、普通にラズパイimageの入ったSDカードを圧縮してimage保存する等ご自分のやり易い方法で準備ください。

昨年まではImagerでimage書き込み後に[cmdline.txtから"init=/usr/lib/raspi-config/init_resize.sh"を削除することで](https://denor.jp/usb%E8%B5%B7%E5%8B%95%E7%89%88raspberry-pi-os%E3%81%AE%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E8%87%AA%E5%8B%95%E6%8B%A1%E5%BC%B5%E3%82%92%E6%AD%A2%E3%82%81%E3%82%8B%E3%81%AB)初回起動時にパーティションの自動拡張を止められたようですが、記事執筆時点(2023.05.14)では出来ませんでした。

ということでmicroSDカードを2枚使い「imageを作って別のマシンで圧縮して保存する」より一手間少なく済ませました。

### USB On-The-Go(OTG)設定
#### USB On-The-Go(OTG)
接続したデバイス(WindowsPC)側からラズパイをUSBストレージとして認識させるには起動時OTG設定が必要になります。以下、[WikiPedia](https://ja.wikipedia.org/wiki/USB_On-The-Go)より
> USB On-The-Go（略してUSB OTG）は、USB機器どうしを直接接続するインタフェース規格である。パソコン等をホストとせずに、動作時にホスト機器を動的に切り替える機能を拡張したもの。 IEEE 1394のように直接接続できるので、いろいろな機器に応用できる。2001年12月18日に発行されたUSB OTG 2.0 Revision 1.0 は High Speed (480Mbps) がオプション扱いとなっていたが[1]、2009年5月8日に発行された USB OTG 2.0 Revision 2.0 では High Speed を含み[2]、2011年7月1日に発行された USB OTG 3.0 では SuperSpeed (5Gbps) を含む。

#### /boot/config.txt
ラズパイのOTGを有効にする為に、起動設定に以下の一行を追加します。

```sh:/boot/config.txtに追加
dtoverlay=dwc2
```

### USB mass storage設定

#### /boot/cmdline.txt

g_mass_storageを使ってストレージ起動するのに以下を起動設定に追加します。

```sh:/boot/cmdline.txtに追加
modules-load=dwc2,g_mass_storage
```
#### exfat-fuseインストール

exfatをUSBメモリのフォーマットとして使用するためにexfat-fuseをインストールします。

```sh
$ sudo apt install exfat-fuse
```

#### パーティション設定
microSDカードの空き領域にUSBストレージとして利用する領域を確保します。
初回起動直後のパーティションの状態は以下のようになっています。
16GBのSDカードのimageがそのまま書き込まれています。
```sh
$ sudo parted /dev/mmcblk0
GNU Parted 3.4
Using /dev/mmcblk0
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) p
Model: SD EB1QT (sd/mmc)
Disk /dev/mmcblk0: 32.0GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      4194kB  273MB   268MB   primary  fat32        lba
 2      273MB   15.9GB  15.7GB  primary  ext4
```
15.9GB以降の空き領域にUSBストレージ用のボリュームを作ります。
```sh
(parted) mkpart
```
primary partitionでWindowsファイル用にntfsで作ります。
```sh
Partition type?  primary/extended? primary
File system type?  [ext2]? fat32
```
ボリュームサイズは約1GBにします。
```sh
Start? 15.9GB
End? 16.9GB
(parted) p
Model: SD EB1QT (sd/mmc)
Disk /dev/mmcblk0: 32.0GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      4194kB  273MB   268MB   primary  fat32        lba
 2      273MB   15.9GB  15.7GB  primary  ext4
 3      15.9GB  16.0GB  88.1MB  primary  ntfs         lba
```
partedから抜けます。
```sh
(parted) q
Information: You may need to update /etc/fstab.
```
/etc/fstabを書き換えなさいと優しく怒られますが、fstabではコピーしたファイルのTZが上手く修正出来なかったので今回は別の方法でmountします。

blk03にUSBストレージ用の新しいボリュームができたことを確認します。

```sh
$ sudo fdisk -l
Device         Boot    Start      End  Sectors  Size Id Type
/dev/mmcblk0p1          8192   532479   524288  256M  c W95 FAT32 (LBA)
/dev/mmcblk0p2        532480 31116287 30583808 14.6G 83 Linux
/dev/mmcblk0p3      31117312 33007615  1890304  923M  7 HPFS/NTFS/exFAT
```

#### ボリュームタイプ変更

古い設備にUSB接続する予定があるのでFAT32に変換しておきます。
```sh
$ sudo mkfs.vfat -F 32 /dev/mmcblk0p3
```

#### ラズパイ側のmount point設定
Linuxのファイルシステム側のmount pointとして、user直下に/usbを作ってUSBストレージ用ボリュームをmountします。mount pointは意図してtmpfsに設定しますのでデータロスには注意が必要です。

```sh:/etc/fstab
tmpfs /home/user/usb   tmpfs   defaults,size=1024m,mode=0755   0 0
```

mountコマンドはcronの起動時設定に組み込みます。Windowsのtimestampが+09:00になってしまうのでマウント時にオフセット処理します。

```sh:crontab
@reboot sudo mount -otime_offset=540 /dev/mmcblk0p3 /home/user/usb
```

### systemd設定

サービス化してmass starageを起動/停止します。

```sh
$ sudo nano /lib/systemd/system/usb_storage.service
```

```sh:/lib/systemd/system/usb_storage.service
[Unit]
Description = USB MASS Storage Service
After = local-fs.target

[Service]
ExecStart = /sbin/modprobe g_mass_storage file=/dev/mmcblk0p3 removable=y
ExecStop = /sbin/modprobe -r g_mass_storage
Type = oneshot
RemainAfterExit = true

[Install]
WantedBy = multi-user.target
```
サービスを起動時有効にします。
```sh
$ sudo systemctl enable usb_storage
```

### 動作確認
mass storageがUSBストレージとしてWindowsPCから認識できるか確認します。

#### OTG接続port

結論から言うとPi4BではUSB-c portと相手側の端末をUSBケーブルで接続します。

実は今回のメイン嵌りイベントはここでした。

2023.05.14時点でラズパイのどのUSB portでOTG接続できるかを公式のアナウンスで確認したところ[Zeroに関するドキュメント](https://www.raspberrypi.com/products/raspberry-pi-zero-w/)しか確認することができませんでした。

この部分です。

>Specification  
802.11 b/g/n wireless LAN  
Bluetooth 4.1  
Bluetooth Low Energy (BLE)  
1GHz, single-core CPU  
512MB RAM  
Mini HDMI port and micro USB On-The-Go (OTG) port  
Micro USB power  
HAT-compatible 40-pin header  
Composite video and reset headers  
CSI camera connector  

**micro USB On-The-Go (OTG) port Micro USB power**とあるので給電port以外のUSB portでOTGすると捉えましたが、残念ながら**Pi4BではUSB-c port**を使う必要がありました。半日以上溶かしましたが[こちらの記事](https://shikasen-engineer.com/raspberry-pi-emulate-hdd/)のおかげでクリアすることができました。本当に感謝です。

#### mass storage起動

ここまでで自動起動設定が完成しているので一度電源を落とします。
```sh
$ sudo shutdown now
```

普段は給電に使うラズパイのUSB-cとWindowsPCのUSB portをケーブルでつなぎ再起動します。

しばらくするとWindows側でUSBデバイス認識の通知が出ててラズパイをUSBストレージ化出来たとこが確認できます。

<img width= 500 src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565396/04079527-8059-10f9-87c3-7380a729551b.png>

#### ファイル転送
Windows上で立ち上がったexplorerでUSBドライブ上にサンプルテキストを作成するとラズパイのmount先でも確認できました。

<img width= 600 src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565396/c18bda4e-7db5-d2f8-0209-08a384798d05.png>

```sh
~/usb $ ls -lah
合計 16K
drwxr-xr-x 3 root     root     4.0K  1月  1  1970  .
drwxr-xr-x 6 user     user     4.0K  5月 14 14:16  ..
drwxr-xr-x 2 root     root     4.0K  5月 14 13:22 'System Volume Information'
-rwxr-xr-x 1 root     root        4  5月 14 13:51  test1.txt
```

### microSDカードのROM化設定

最後に連続使用を想定して、[今まで同様](https://qiita.com/nsaito9628/items/0339662ed04ba56bbb44#microsd%E3%82%AB%E3%83%BC%E3%83%89%E3%81%AE%E6%9B%B8%E8%BE%BC%E3%81%BF%E4%BF%9D%E8%AD%B7)microSDカードにROM化設定をします。

ROM化設定/解除/再設定用のscriptを/protectにまとめ、初期設定用のscript(protect_prov.sh)と一緒にuser directoryにコピーして実行します。

```sh
sudo chmod +u+x protect_prov.sh
./protect_prov.sh
```

それぞれのscriptは以下です。

**ROM化設定用script**
```sh:protect_prov.sh
#!/bin/bash

test -d ./root-ro
if [ $? == 0 ]; then
  sudo rm -r ./root-ro
  echo "Elminated old root-ro"
else
  echo "root-ro is not exist" 
fi

sudo cp ~/protect/protect.sh /root
sudo cp ~/protect/protect /bin
sudo cp ~/protect/nonprotect /bin

sudo chmod u+x /root/protect.sh /bin/protect /bin/nonprotect
sudo chown root:root /root/protect.sh /bin/protect /bin/nonprotect

sudo sh /root/protect.sh

sudo rm *prov.sh
sudo rm -r ./protect

sudo reboot
```

```sh:/protect/protect.sh
#!/bin/bash

echo Installing all dependencies
apt-get install git subversion rsync gawk busybox bindfs -y
echo Disabling swap
dphys-swapfile swapoff
dphys-swapfile uninstall
update-rc.d dphys-swapfile disable
systemctl disable dphys-swapfile
echo Cloning repository
git clone https://github.com/josepsanzcamp/root-ro.git
echo Doing the setup
rsync -va root-ro/etc/initramfs-tools/* /etc/initramfs-tools/
mkinitramfs -o /boot/initrd.gz
echo initramfs initrd.gz >> /boot/config.txt
echo Restarting RPI
exit
```

**ROM化解除用script**
```sh:/protect/nonprotect
#!/bin/sh

if [ -e /mnt/boot-ro/config.txt ]; then
    sudo mount -o remount,rw /dev/mmcblk0p1
    sudo grep -v initramfs /mnt/boot-ro/config.txt >/tmp/config.txt
    sudo cp /tmp/config.txt /mnt/boot-ro/config.txt
    sudo reboot
else
    echo Already write enabled
fi
```

**ROM化再設定用script**
```sh:/protect/protect
#!/bin/sh

if [ -e /mnt/boot-ro/config.txt ]; then
    echo Already write protected
else
    sudo grep -v initramfs /boot/config.txt >/tmp/config.txt
    sudo echo initramfs initrd.gz >> /tmp/config.txt
    sudo cp /tmp/config.txt /boot/config.txt
    sudo reboot
fi
```

構築手順の説明は以上となります。


## 追記

**2023.05.14**　誤記訂正
**2023.05.15**　[説明欠落箇所(exfat-fuseインストール)を追記](https://qiita.com/drafts/5f369ba8a8d419e3cbff/edit#exfat-fuse%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)