guest@blog.cmj.tw: ~/posts $

RPi Zero W


IoT

無聊閒閒沒事情做又開始玩開發板,這次目標是 Raspberry Pi Zero W :這是一片 ARMv6、512MB RAM、802.11 b/g/n WiFi 的開發板,在上面安裝 ArchLinux

Install OS

在網路上找到很多安裝的方式,目前我參考了 這個 script 改寫成我自己的版本:

以下是 sh script:

#! /bin/sh -exu
DEV=$1

IMG_NAME=ArchLinuxARM-rpi-latest.tar.gz
ARCH_LINUX_IMG=http://os.archlinuxarm.org/os/${IMG_NAME}
LOCAL_IMG=/tmp/${IMG_NAME}

function umountboot {
    umount boot || true
    umount root || true
}

function download_image_and_verify {
    if [ ! -f "${LOCAL_IMG}" ]; then
        # download the image
        wget ${ARCH_LINUX_IMG} -O ${LOCAL_IMG}
    fi

    wget ${ARCH_LINUX_IMG}.md5 -qO - | sed "s%${IMG_NAME}%${LOCAL_IMG}%g" | md5sum -c
    if [ $? != 0 ]; then
        exit -1
    fi
}


cd $(mktemp -d)

download_image_and_verify

parted -s ${DEV} mklabel msdos
parted -s ${DEV} mkpart primary fat32 1 128
parted -s ${DEV} mkpart primary ext4 128 -- -1
mkfs.vfat ${DEV}1
mkfs.ext4 -F ${DEV}2

# auto-umount
trap umountboot EXIT

mkdir -p boot && mount ${DEV}1 boot
mkdir -p root && mount ${DEV}2 root

bsdtar -xpf ${LOCAL_IMG} -C root
sync
mv root/boot/* boot

Scenario 1 - hostname

第一個客製化系統就是修改你 RPI 上的 hostname :這可以用獨特的關鍵字來判斷正在使用哪一個開發版。 在安裝完 OS 之後先不要執行 umount 的情況之下,可以執行以下指令 (HOSTNAME 需要自行設定) 就可以修改成自己的名稱st:

#! /bin/sh
echo ${HOSTNAME} > root/etc/hostname

Scenario 2 - USB Gadget

可以透過載入各種不同的 USB Gadget module 之後,就可以將 USB 裝置視為是另一種方面的裝置了。

Serial Port over USB

透過安裝 g_serial 之後可以將 USB 視為是 serial port 的裝置:

#! /bin/sh

# load dwc2 when boot
sed -i "s/rootwait/rootwait modules-load=dwc2,g_serial/g" /boot/cmdline.txt"
echo "dtoverlay=dwc2" >> /boot/config.txt
# enable getty systemd service
systemctl enable getty@ttyGS0.service

另一種寫法則是透過 systemd 的設定檔負責載入 kernel module 跟啟用 ttyGS0:

#! /bin/sh

## Setup by each gadget case ##
cat >> root/usr/lib/systemd/system/usb_serial.service << EOF
# USB serial port gadget
[Unit]
Description=USB Serial Port Gadget
After=systemd-modules-load.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/modprobe g_serial
ExecStart=/bin/systemctl start serial-getty@ttyGS0.service

ExecStop=/bin/systemctl stop serial-getty@ttyGS0.service
ExecStop=/sbin/modprobe -r g_serial

[Install]
WantedBy=getty.target
EOF

# enable getty systemd service
systemctl enable usb_serial.service

Mass Storage over USB

透過安裝 g_mass_storage 之後可以將 RPi 視為是一個可讀寫的 USB 裝置,但是在我的測試環境中第二次使用就會出現 dmesg 的錯誤訊息:

[601761.743841] sd 6:0:0:0: [sdb] Synchronizing SCSI cache
[601761.743921] sd 6:0:0:0: [sdb] Synchronize Cache(10) failed: Result: hostbyte=DID_NO_CONNECT driverbyte=DRIVER_OK

不過依然可以用以下 shell script 安裝:

#! /bin/sh

# load dwc2 when boot
sed -i "s/rootwait/rootwait modules-load=dwc2,g_mass_storage/g" /boot/cmdline.txt"
echo "dtoverlay=dwc2" >> /boot/config.txt
# touch a file as storage
dd if=/dev/zero of=/root/usbstorage.img bs=1MB count=100
echo "options g_mass_storage file=/root/usbstorage.img" >> /etc/modprobe.d/mass_storage_gadget.conf

經過操作之後可以進入到 RPi 執行指令 file /root/usbstorage.img 之後可以看到是一個 ext4 的檔案系統格式: /root/usbstorage.img: Linux rev 1.0 ext4 filesystem data

Multiple Gadget

接下來透過 libcomposite 這個套件可以使用各種 USB Gadget 模組,並透過 sysfs 做各種設定:

#! /bin/sh -e
# /usr/sbin/usbgadget
# ref: https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt
USB_GADGET_PATH=/sys/kernel/config/usb_gadget
USB_GADGET_NAME=g

# load libcomposite and USB gadget modules
modprobe -r g_ether
modprobe libcomposite

# make new USB gadget in configs
mkdir -p ${USB_GADGET_PATH}/${USB_GADGET_NAME}/configs/c.1
cd ${USB_GADGET_PATH}/${USB_GADGET_NAME}

# General USB property #
echo 0x1d6b > idVendor     # Linux Foundation
echo 0x0104 > idProduct    # Multifunction Composite Gadget
echo 0x0100 > bcdDevice    # v1.0.0
echo 0x0200 > bcdUSB       # USB2

mkdir -p strings/0x409
echo "deadbeef00112233"    > strings/0x409/serialnumber
echo "RPi0w"               > strings/0x409/manufacturer
echo "RPi0w USB Gadget"    > strings/0x409/product

mkdir -p configs/c.1/strings/0x409
echo "Config 1: ECM network" > configs/c.1/strings/0x409/configuration
echo 250                     > configs/c.1/MaxPower

# create function #
mkdir -p functions/acm.usb0    # serial
mkdir -p functions/ecm.usb0    # ECM ethernet
# enable function #
ln -s functions/acm.usb0   configs/c.1/   ## Serial Adapter ##
ln -s functions/ecm.usb0   configs/c.1/   ## ECM Ethernet Adapter ##


udevadm settle -t 20 || :
ls /sys/class/udc/ > UDC

透過產生 /usr/sbin/usbgadget 檔案並產生相對的 systemd 設定檔,就可以在 RPi 上面同時產生 serial 跟 CDC ECM 網路裝置。