コスモリサーチの凄腕エンジニアたちが日々、難題と格闘した記録
Copyright Cosmo Research Corp.

USB_gadgetで2つのデバイスを同時に見せる on Zynq

参考資料

やること

Linux Kernel Config

比較的新しいconfigfsでのUSB Gadget制御の仕組みを使うため、以下を有効にする。
その他必要な設定はおそらくデフォルトで有効になっているが、不安な場合はZynq Linux USB Device Driverを確認のこと。

Device Drivers > USB support > USB Gadget Support
    <M>   USB functions configurable through configfs
        [*]     RNDIS
        [*]     Mass storage

Device Tree Source

  • dr_mode: peripheralに変更(OTGでも大丈夫かもしれないが未確認)
  • usb-phy: 指定が必要
--- a/xsdk/devicetree_bsp/pcw.dtsi   Tue Jul 11 11:08:17 2017 +0900
+++ b/xsdk/devicetree_bsp/pcw.dtsi    Fri Sep 15 20:48:37 2017 +0900
@@ -82,12 +82,24 @@
    status = "okay";
 };
 &usb0 {
-  dr_mode = "host";
+   dr_mode = "peripheral"; // for USB Gadget change from "host"
    phy_type = "ulpi";
    status = "okay";
    usb-reset = <&gpio0 7 0>;
+   usb-phy = <&usb_phy0>; // for USB Gadget
 };
 &clkc {
    fclk-enable = <0x1>;
    ps-clk-frequency = <33333333>;
 };
+
+// for USB Gadget
+/ {
+   usb_phy0: phy0 {
+       compatible = "ulpi-phy";
+       #phy-cells = <0>;
+       reg = <0xe0002000 0x1000>;
+       view-port = <0x170>;
+       drv-vbus;
+   };
+};
\ No newline at end of file

Gadgetの有効化

以下(usb_config_multi.sh)を実行する。出来る限りコメントを書いた。

#!/bin/sh

# https:/github.com/torvalds/linux/blob/master/Documentation/usb/gadget_configfs.txt
# http:/irq5.io/2016/12/22/raspberry-pi-zero-as-multiple-usb-gadgets/
# 上記2つを参考に同時に二つのガジェットを有効化する。
#   1. mass storageで、SDカードをUSBメモリとして見せる
#   2. rndisで、USBをetherデバイスとして見せる

# ドライバを依存関係含めてロード
modprobe usb_f_rndis
modprobe usb_f_mass_storage

# configfsをマウント
mount -t configfs none /sys/kernel/config

# usb_gadgetの下に任意のディレクトリを生成する
g=/sys/kernel/config/usb_gadget/multi
mkdir ${g}

# USBの各種設定(VID/PIDは必須)
echo "64"     > ${g}/bMaxPacketSize0
echo "0x200"  > ${g}/bcdUSB    # USB2.0
echo "0x100"  > ${g}/bcdDevice # 適当
echo "0x03FD" > ${g}/idVendor  # Xilinx
echo "0x0104" > ${g}/idProduct # Multifunction Composite Gadget

# 複数functionのcomposite USB向けの設定
# refer: https://msdn.microsoft.com/en-us/library/windows/hardware/ff540054.aspx
echo "0xEF"   > ${g}/bDeviceClass
echo "0x02"   > ${g}/bDeviceSubClass
echo "0x01"   > ${g}/bDeviceProtocol

# functionsに登録
mkdir ${g}/functions/rndis.rn0
mkdir ${g}/functions/mass_storage.ms0

# rndis固有の設定(設定しないとランダムなmacアドレスを生成する)
# echo "${dev_mac}"  > ${g}/functions/rndis.rn0/dev_addr
# echo "${host_mac}" > ${g}/functions/rndis.rn0/host_addr

# mass storage固有の設定
# fileにストレージとして見せるデバイスを指定する
echo /dev/mmcblk0p1 > ${g}/functions/mass_storage.ms0/lun.0/file
echo 1              > ${g}/functions/mass_storage.ms0/lun.0/removable

# functionとconfigを関連付け
mkdir ${g}/configs/c.1
ln -s ${g}/functions/rndis.rn0        ${g}/configs/c.1/
ln -s ${g}/functions/mass_storage.ms0 ${g}/configs/c.1/

# rndisをwindowsで見えるようにするための設定
echo "1"       > ${g}/os_desc/use
echo "0xcd"    > ${g}/os_desc/b_vendor_code
echo "MSFT100" > ${g}/os_desc/qw_sign
echo "RNDIS"   > ${g}/functions/rndis.rn0/os_desc/interface.rndis/compatible_id
echo "5162001" > ${g}/functions/rndis.rn0/os_desc/interface.rndis/sub_compatible_id
ln -s ${g}/configs/c.1 ${g}/os_desc

# デバイス有効化
echo "ci_hdrc.0" > ${g}/UDC

Gadgetの動作確認

Mass Storageは接続するだけでSDカードの中身が見える。

Etherは、Zynq側で以下を実行しておくとPCで接続したときにネットワークデバイスとして見える。

ifconfig usb0 192.168.1.2 # 好きなIPアドレスを設定
ifconfig usb0 up          # network起動

Gadgetの削除

以下(usb_remove_multi.sh)を実行する。Gadgetの有効化で使用したusb_config_multi.shと対応している。

#!/bin/sh

# https://github.com/torvalds/linux/blob/master/Documentation/usb/gadget_configfs.txt
# http://irq5.io/2016/12/22/raspberry-pi-zero-as-multiple-usb-gadgets/
# 上記を参考にusb_config_multi.shで追加したガジェット2つを無効化する。

# ガジェットを無効化
g=/sys/kernel/config/usb_gadget/multi
echo "" > ${g}/UDC

# functionsのsimlink先を削除
rm ${g}/os_desc/c.1
rm ${g}/configs/c.1/rndis.rn0
rm ${g}/configs/c.1/mass_storage.ms0

# Configurationsを削除
rmdir ${g}/configs/c.1/

# functionsのsimlink元を削除
rmdir ${g}/functions/rndis.rn0
rmdir ${g}/functions/mass_storage.ms0

# gadget本体を削除
rmdir ${g}

# ドライバを依存関係含めてアンロード
modprobe -r usb_f_rndis
modprobe -r usb_f_mass_storage

# configfsをアンマウント
umount /sys/kernel/config