PL-R5をUSBデバイスとして動かすと何ができるか
OTGで実現するEthernet・Serial・Storage

PL-R5のような産業用Raspberry Piの中には、USB OTGに対応しているデバイスがあります。今回使用したPL-R5も対応しています。
USB OTG(On-The-Go)とは、1つのUSBポートがホスト(Host)にもデバイス(Device)にもなれる機能です。

OTG対応ポートは、状況に応じて役割を切り替えられるのが特徴です。

  • USB Ethernetアダプタ
  • USBシリアル通信機器
  • USBストレージ
  • 複数機能を使うComposite

産業用Raspberry PiでUSB OTGの利用は、一例として次のような使い方が考えられます。

  • PC接続で初期設定を実行させる
  • メンテナンス用シリアル接続でアクセスする
  • ログ取得用として実行させる
  • Web管理UIを提供させる

LinuxにはUSB Gadget Frameworkという仕組みがあります。そのため、標準の状態で対応しています。

ガジェット内容
g_etherUSB Ethernet
g_serialUSB Serial
g_mass_storageUSBストレージ
g_hidキーボードやマウス
Composite複数機能を同時提供

Linuxでは現在、ConfigFSベースのUSB Gadget構成が標準です。(/sys/kernel/config/usb_gadget/配下で設定)
g_etherなどの単独モジュールはkernelで自動起動できてお手軽なのですが、g_〜系は複数機能を同時に設定ができません。
そのため、実用面ではやや設定箇所が多いComposite構成で設定することになります。

最初はUSB OTGを有効にするところから、この記事では最終的なCompositeで3つの機能を一度に設定していきます。

記事執筆時点での注意事項

今回の環境は、CM5を元にした産業用Raspberry Piの「PL-R5」を使用しました。OSはRaspberry Pi OS(bookworm)です。ホストPCはLinux/macOSでテストしました。

検証環境
Model: 産業用ラズパイ「PL-R5」(Raspberry Pi Compute Module 5 Rev 1.0)
OS: Debian GNU/Linux 12 (bookworm)
Kernel: 6.12.62+rpt-rpi-v8
macOS: Tahoe 26.3

ホストPCとしてWindowsを利用する場合は、ECM(Ethernet Control Model)が非対応なため、RNDIS(Remote Network Driver Interface Specification)がドライバと共に必要になります。

また、ここでは触れませんが、Raspberry Pi OS Trixieからは、rpi-usb-gadgetパッケージが含まれています。これを有効にすれば、手作業の設定はぜずともUSB Ethernetデバイスとして認識され、Wi-Fiや有線LANを設定しなくてもすぐにSSHで接続できます。

以下、公式サイトからの引用です。

“Starting with Raspberry Pi OS Trixie images dated 20.10.2025 or later, a new package called rpi-usb-gadget is included by default. Once activated, the Raspberry Pi presents itself as a USB Ethernet device, and you can connect via SSH immediately, without configuring Wi-Fi or Ethernet.”
https://www.raspberrypi.com/news/usb-gadget-mode-in-raspberry-pi-os-ssh-over-usb/

一口にUSB OTGといっても色々なやり方や設定方法があります。少しややこしく感じました。
USB Ethernetだけであれば、現在は簡単にできる仕組みが追加されているというわけですね。

事前準備:USB OTGポートをDeviceモードにする(手動)

ここではすべて手動でUSB OTGを設定していきます。

PL-R5のUSB2.0 OTGポートをデバイス側で使うには、先ずはブート時にUSBコントローラをPeripheralモードで有効化する手だてを施します。

最終的にCompositeとしてConfigfsで3つの機能を設定しますが、その場合はcmdline.txtを再度編集する必要がありますので注意してください。

/boot/firmware/config.txtに追記

dtoverlay=dwc2,dr_mode=peripheral

但し、CM5系はdir_mode=hostが標準(デフォルト)で記述してることが多いため、元をコメントアウトしてから追記してください。

[cm5]
#dtoverlay=dwc2,dr_mode=host
dtoverlay=dwc2,dr_mode=peripheral

もう1つの/boot/firmware/cmdline.txtにも追記があります。
以下を最後尾に半角スペースで区切り記入します。
cmdline.txtは1行で記述するルールです。改行はしません。

modules-load=dwc2

再起動すればUSB OTGのデバイスモードが有効になります。

USB Composite(Configfsで設定)

USB OTGを使う場合、単独での設定ではなく、Ethernetやシリアル通信を複合的に使う方が望ましいです。
複数機能を同時提供させるには、g_~系での設定はできませんから、configfsで設定をします。これはUSB Compositeと呼ばれます。

• 単機能(Ethernet/Serial/Mass Storage) → 複数指定できない。Kernelで自動起動(systemd不要)
• 複合機能(Mass Storage+Ethernet など) → configfs + systemd作成

注意点として、g_etherなどとconfigfsの設定は同時に使うと競合してしまいますから、必ずどちらかで設定をします。

今回はCompositeにすることで、Ethernet + Serial + Mass StorageがUSBケーブル1本で提供できるようにしていきます。

各機能だけをConfigfsで設定することもできます。
1つ注意点として、後で追加編集などする場合、設定で有効にしたUDCを一旦解除し、改めてUDCを有効にしないと壊れることがあることです。

私もテストで1度やってしまいました。手順を間違うと起こりえます。

今回は3つの機能をすべて一度で設定していくスクリプトファイルを用意しました。

USB Compositeの設定

最初に先ほど編集した/boot/firmware/cmdline.txtを以下を付け加えます。再起動して有効にしてください。

modules-load=dwc2,libcomposite

libcompositeを読み込ませてから、/sys/kernel/config/usb_gadget/に必要な設定ファイルを作成していきます。

今回はCM5としたディレクトリを作成しました。名前は何でも構いませんが、他でも指定しているので合わせて変更してください。

配下にディレクトリを作成すると、次のようなファイルが作成されているのが分かります。

UDC           bDeviceProtocol  bMaxPacketSize0  bcdUSB   functions  idVendor   os_desc  webusb
bDeviceClass  bDeviceSubClass  bcdDevice        configs  idProduct  max_speed  strings

ここにあるファイルに変更をしていきます。

usb_gadget設定例

接続するUSBガジェットの設定をしていきます。先ずは以下が今回指定した内容です。
後でまとめてスクリプトとして実行しますが、どのような指定をしていったのかを1つずつ説明します。

sudo modprobe libcomposite

cd /sys/kernel/config/usb_gadget/
sudo mkdir cm5
cd cm5

echo 0x1d6b | sudo tee idVendor      # Linux Foundation
echo 0x0104 | sudo tee idProduct     # Multifunction Composite
echo 0x0100 | sudo tee bcdDevice     # デバイスのリリース番号v1.0.0
echo 0x0200 | sudo tee bcdUSB        # USB2.0

sudo mkdir strings/0x409   # en-US 英語文字
echo "0001" | sudo tee strings/0x409/serialnumber
echo "RaspberryPi" | sudo tee strings/0x409/manufacturer
echo "CM5 Composite" | sudo tee strings/0x409/product

始めにsudo modprobe libcompositeと読み込ませます。これはスクリプト作成&実行前に念のため一度だけ実行しておきます。

途中のベンダーIDとプロダクトIDは、本来は各メーカーで決められていますが、今回はテストなのでベンダーは「Linux Foundation」のID、プロダクトIDは「Multifunction Composite」のIDを利用しました。
bcdDeviceはリリース番号なのでv1.0.0、bcdUSBはUSB2.0の値を設定しました。

他、strings/0x409は英語(en-US)文字指定、serialnumberとmanufacturer、productは任意ですが分かりやすいようにしておきます。
productなどホストPC側で表示される部分でもあるので、分かりやすい命名が良いでしょう。

これらを踏まえて、スクリプトファイル内で指定しました。

Composite設定のスクリプトファイル

CM5用・複合USBガジェット(ECM + ACM + Mass Storage)を一度に設定するスクリプトを提示します。
前提条件:config.txt、cmdline.txtでdwc2とlibcompositeはロード済み。

中身はおおまかに次の流れです。

  • UDC解除
  • gadget生成
  • ecm/acm/mass_storage作成
  • configへリンク
  • UDC再バインド
  • USB Ethernetの設定

cm5-usb-gadget.sh

sudo nano /usr/local/sbin/cm5-usb-gadget.sh

#!/bin/bash
set -euo pipefail

G=/sys/kernel/config/usb_gadget/cm5
UDC_DEV=$(ls /sys/class/udc | head -n 1)
[ -z "$UDC_DEV" ] && exit 1

# 既存解除(再実行対策)
if [ -d "$G" ]; then
    echo "" > "$G/UDC" || true
    rm -rf "$G"
fi

mkdir -p "$G"
cd "$G"

# ===== Device Descriptor =====
echo 0x1d6b > idVendor      # Linux Foundation
echo 0x0104 > idProduct     # Multifunction Composite Gadget
echo 0x0200 > bcdUSB
echo 0x0100 > bcdDevice

mkdir -p strings/0x409
echo "0123456789" > strings/0x409/serialnumber
echo "Raspberry Pi" > strings/0x409/manufacturer
echo "CM5 USB Gadget" > strings/0x409/product

# ===== Config =====
mkdir -p configs/c.1
mkdir -p configs/c.1/strings/0x409
echo "ECM + ACM + MassStorage" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower

# ===== ECM (USB Ethernet) =====
mkdir -p functions/ecm.usb0
echo "02:00:00:00:00:01" > functions/ecm.usb0/dev_addr
echo "02:00:00:00:00:02" > functions/ecm.usb0/host_addr
ln -s functions/ecm.usb0 configs/c.1/

# ===== ACM (USB Serial) =====
mkdir -p functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/

# ===== Mass Storage =====
IMG="/opt/usb/piusb.bin"
LABEL="CM5USB"
mkdir -p /opt/usb

if [ ! -f "$IMG" ]; then
    dd if=/dev/zero of="$IMG" bs=1M count=128
    mkfs.vfat -n "$LABEL" "$IMG"
fi

fatlabel "$IMG" "$LABEL" || true

mkdir -p functions/mass_storage.usb0
echo 1 > functions/mass_storage.usb0/stall
echo 0 > functions/mass_storage.usb0/lun.0/removable
echo 0 > functions/mass_storage.usb0/lun.0/ro
echo "$IMG" > functions/mass_storage.usb0/lun.0/file
ln -s functions/mass_storage.usb0 configs/c.1/

# ===== Bind =====
echo "$UDC_DEV" > UDC

# ====== USB Ethernet 固定IP設定 ======
sleep 1

if ip link show usb0 >/dev/null 2>&1; then
    ip addr flush dev usb0 || true
    ip addr add 192.168.7.2/24 dev usb0 || true
    ip link set usb0 up || true
fi

それぞれ環境によって異なる箇所もありますが、このままでも問題ないと思われます。

今回は冒頭で”既存解除(再実行対策)”をしています。
これは毎起動時に実行させるからです。仮にどこかの設定値を変更して起動しなおしても、反映できるようにするためです。

Mass Storageの部分で、イメージファイルの場所と名前、接続時にラベルが表示された方が分かりやすいので、そのラベルを指定しています。
任意に変更したい場合はここを変更してください。

IMG="/opt/usb/piusb.bin"
LABEL="CM5USB"

Bindの箇所でecho $UDC_DEV > UDCとUDCの値を書き込んでUDCを有効にしています。

最後に、IPアドレスは固定にしました。
設定しないこともできるのですが、色々と分かりにくいので決め打ちしています。(169.254.x.x/16になる)

USB Ethernetは独立ネットワークとして動作させたいため、家庭内LANと異なる第3オクテットを選ぶ必要があります。
192.168.7.0/24ならば、一般的な家庭LANと重複しにくいため安全というわけです。

192.168.7.1  # ホストPCとして
192.168.7.2  # Pi

ちなみに、固定IPの設定と共にusb0を有効(up)にさせていますから一石二鳥です。

スクリプトを作成したら、実行権限の付与をしておきます。

sudo chmod +x /usr/local/sbin/cm5-usb-gadget.sh

systemd化する(自動起動対応)

設定が自動的に有効になるようにサービスを作成をします。

sudo nano /etc/systemd/system/cm5-usb-gadget.service

[Unit]
Description=CM5 USB Composite Gadget
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/cm5-usb-gadget.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

この設定内容は、oneshotサービスとして起動時に1回だけcm5-usb-gadget.shが実行されます。
つまり、毎回configfsを再構築させます。

先ほどから触れているように、USB OTGの内容を一部変更して有効にしたいなら、また再起動すればOKというわけです。
shファイルの最初でUDCを解除しているため実現できています。

RemainAfterExit=yesによりサービスは active (exited) になり、状態を保持したい構成系サービスで有効的です。常駐はしません。

最後、忘れずに作成したサービスを有効化(enable)しておきます。ここでstartはしません。shファイルが実行されるからです。

sudo systemctl enable cm5-usb-gadget.service

これで再起動すれば、3つの機能を有効にしたUSB OTGが動作します。

個別に知っておくと助かる方法

知っておくと何かと助かる場面もあると思い残しておきます。

UDCの解除・有効

何度か触れているUDCは、最初は何もない空のファイルです。
Configfs方式では、UDCに自身のUDCデバイス名(例:1000480000.usb)の値を書き込んだ後で有効になります。

そのため、何か構成を変更や追加をする度に書き込みたいのですが、すでにUDCとして有効の場合は、解除(値を削除)してから再度有効にする必要があります。
この書き換えで少々トラブルになったため、手動で行うコマンドを残しておきます。

以下のコマンドで表示されるUDC名(例:1000480000.usb)を使います。

ls /sys/class/udc

値を書き込むコマンド。

echo "1000480000.usb" | sudo tee /sys/kernel/config/usb_gadget/cm5/UDC > /dev/null

UDCの解除は、値が既にあるUDCを空にします。
新たにUDCにFunctionを加えるなど、変更する場合も同じようにUDCを空にしてから設定し、再度有効にする必要があります。

値を空にするコマンド。

echo "" | sudo tee /sys/kernel/config/usb_gadget/cm5/UDC > /dev/null

解除して各functionを変更したら、先ほど同様に再度有効にします。
なお、各functionはスクリプトを参考にコマンドを実行する必要があります。

echo "1000480000.usb" | sudo tee /sys/kernel/config/usb_gadget/cm5/UDC > /dev/null

エラーの調査

詳しい説明は省きますが、うまくいかない場合に試すコマンドを残しておきます。

journalctl -u cm5-usb-gadget.service

通信の調査で試したいコマンド。すべてPi側で実行します。

ip route
ip route | grep usb0
ip addr show usb0
cat /sys/class/net/usb0/carrier
ip link show
systemctl status cm5-usb-gadget.service
nmcli dev status

単機能と複合機能

単機能で使うなら、ここでは取り上げていないg_etherやg_serialが最もお手軽です。
しかし、細かな設定ができたり、そもそも複数の機能を使うならConfigfs方式で設定します。

少し設定箇所が多いので手動だと面倒ですね。
スクリプトファイルは一度作成してしまえば良いので手数が少なくなりますし、一覧性もあって管理がしやすいのが便利です。

サクッとUSB OTGを試したいなら、スクリプトファイルをコピペして試してみてください。


記事寄稿:ラズパイダ

非エンジニアでも楽しく扱えるRaspberry Pi 情報サイト raspida.com を運営。ラズベリーパイに長年触れた経験をもとに、ラズベリーパイを知る人にも、これから始めたいと興味を持つ人にも参考になる情報・トピックを数多く発信。PiLinkのサイトへは産業用ラズベリーパイについて技術ブログ記事を寄稿。