From b196a9f6ce8ef7d6b09d20ef79ffcf464d1a15ab Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 19 Nov 2023 20:56:52 +0800 Subject: [PATCH] package: new package for usb gadget setup Setting up usb gadgets using g_* kernel modules are considered a legacy approach, but the usb_gadget configfs is a bit annoying to use directly. The usb_gadget configfs works by creating magic directories and writing to magic files under /sys/kernel/config/usbgadget. This new package is an init script to setup usb_gadget configfs using uci. In the config file, gadget/configuration/function sections create corresponding directories. UCI options are magic files available in the configfs and strings/0x409 directories, grabbed with a 'find' command. UDC option in gadget writes the UDC file under the 'gadget' directory to attach the generated gadget config. It's also possible to apply pre-made config templates under /usr/share/usbgadget. The templates use the same UCI config format, with the 'gadget' entry named 'g1'. Currently, there are templates for CDC-ACM and CDC-NCM gadgets written based on existing g_*.ko module code. Certain SBCs come with only a USB device port (e.g. Raspberry Pi Zero). With this script, it's now possible to perform initial setup on them by adding a default NCM gadget. Signed-off-by: Chuanhong Guo --- package/utils/usbgadget/Makefile | 54 +++++++ package/utils/usbgadget/files/presets/acm | 13 ++ package/utils/usbgadget/files/presets/ncm | 13 ++ package/utils/usbgadget/files/usbgadget.conf | 12 ++ package/utils/usbgadget/files/usbgadget.init | 144 +++++++++++++++++++ 5 files changed, 236 insertions(+) create mode 100644 package/utils/usbgadget/Makefile create mode 100644 package/utils/usbgadget/files/presets/acm create mode 100644 package/utils/usbgadget/files/presets/ncm create mode 100644 package/utils/usbgadget/files/usbgadget.conf create mode 100644 package/utils/usbgadget/files/usbgadget.init diff --git a/package/utils/usbgadget/Makefile b/package/utils/usbgadget/Makefile new file mode 100644 index 0000000000..d3a68ea9df --- /dev/null +++ b/package/utils/usbgadget/Makefile @@ -0,0 +1,54 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=usbgadget +PKG_RELEASE:=1 + +PKG_LICENSE:=BSD-2-Clause + +PKG_MAINTAINER:=Chuanhong Guo + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=utils + CATEGORY:=Utilities + DEPENDS:=@USB_GADGET_SUPPORT +kmod-usb-gadget +kmod-usb-lib-composite + TITLE:=init script to create USB gadgets +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/etc/config $(1)/etc/init.d + $(INSTALL_CONF) ./files/usbgadget.conf $(1)/etc/config/usbgadget + $(INSTALL_BIN) ./files/usbgadget.init $(1)/etc/init.d/usbgadget +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) + +# 1: short name +# 2: description +# 3: dependencies on other packages +define GadgetPreset + define Package/$(PKG_NAME)-$(1) + SECTION:=utils + CATEGORY:=Utilities + TITLE+= $(2) gadget preset + DEPENDS+= $(3) + endef + + define Package/$(PKG_NAME)-$(1)/description + This package contains the USB gadget preset for $(3). + endef + + define Package/$(PKG_NAME)-$(1)/install + $(INSTALL_DIR) $$(1)/usr/share/usbgadget + $(INSTALL_CONF) ./files/presets/$(1) $$(1)/usr/share/usbgadget + endef + + $$(eval $$(call BuildPackage,$(PKG_NAME)-$(1))) +endef + +$(eval $(call GadgetPreset,ncm,CDC-NCM,+kmod-usb-gadget-ncm)) +$(eval $(call GadgetPreset,acm,CDC-ACM,+kmod-usb-gadget-serial)) \ No newline at end of file diff --git a/package/utils/usbgadget/files/presets/acm b/package/utils/usbgadget/files/presets/acm new file mode 100644 index 0000000000..f8ce9c4a4a --- /dev/null +++ b/package/utils/usbgadget/files/presets/acm @@ -0,0 +1,13 @@ +config gadget 'g1' + option idVendor '0x0525' + option idProduct '0xa4a7' + option bDeviceClass '2' + option product 'Gadget Serial v2.4' + +config configuration 'cfg1' + option configuration 'ACM' + option gadget 'g1' + +config function 'acm1' + option function 'acm' + option configuration 'cfg1' diff --git a/package/utils/usbgadget/files/presets/ncm b/package/utils/usbgadget/files/presets/ncm new file mode 100644 index 0000000000..cb3a19329e --- /dev/null +++ b/package/utils/usbgadget/files/presets/ncm @@ -0,0 +1,13 @@ +config gadget 'g1' + option idVendor '0x0525' + option idProduct '0xa4a1' + option bDeviceClass '2' + option product 'NCM Gadget' + +config configuration 'cfg1' + option configuration 'NCM' + option gadget 'g1' + +config function 'ncm1' + option function 'ncm' + option configuration 'cfg1' diff --git a/package/utils/usbgadget/files/usbgadget.conf b/package/utils/usbgadget/files/usbgadget.conf new file mode 100644 index 0000000000..0d80fc97df --- /dev/null +++ b/package/utils/usbgadget/files/usbgadget.conf @@ -0,0 +1,12 @@ +# apply a preset under /usr/share/usbgadget +config preset + option name 'ncm' + # specify a UDC to enable this gadget: + # option UDC 'musb-hdrc.2.auto' + +# or create a custom gadget here following the content of presets: +#config gadget 'g1' +# option idVendor ... +# ... +# and add an UDC under the gadget section to enable it: +# option UDC 'musb-hdrc.2.auto' diff --git a/package/utils/usbgadget/files/usbgadget.init b/package/utils/usbgadget/files/usbgadget.init new file mode 100644 index 0000000000..f2e105caae --- /dev/null +++ b/package/utils/usbgadget/files/usbgadget.init @@ -0,0 +1,144 @@ +#!/bin/sh /etc/rc.common + +START=19 + +GADGET_FS=/sys/kernel/config/usb_gadget +GADGET_PRESETS_DIR=/usr/share/usbgadget +GADGET_PREFIX=owrt_ + +log() { + logger -t usbgadget "$@" +} + +apply_configs() { + local fs_path="$1" + local cfg="$2" + for param in $(find "$fs_path" -maxdepth 1 -type f -exec basename '{}' ';'); do + [ "$param" = "UDC" ] && continue + + config_get val "$cfg" "$param" + [ -n "$val" ] && echo "$val" > "${fs_path}/${param}" + done +} + +setup_gadget() { + local cfg="$1" + local gadget_path="${GADGET_FS}/${GADGET_PREFIX}${cfg}" + local param udc + + config_get udc "$cfg" UDC + [ -z "$udc" ] && return + + mkdir "$gadget_path" || return + apply_configs "$gadget_path" "$cfg" + + local strings_path="${gadget_path}/strings/0x409" + mkdir "$strings_path" + apply_configs "$strings_path" "$cfg" +} + +setup_configuration() { + local cfg="$1" + local gadget + config_get gadget "$cfg" gadget + local cfgs_path="${GADGET_FS}/${GADGET_PREFIX}${gadget}/configs" + [ -d "${cfgs_path}" ] || return + local cfg_path="${cfgs_path}/${cfg}.1" + mkdir "$cfg_path" || { + log "failed to create configuration ${cfg}" + return + } + + apply_configs "$cfg_path" "$cfg" + + local strings_path="${cfg_path}/strings/0x409" + mkdir "$strings_path" + apply_configs "$strings_path" "$cfg" +} + +setup_function() { + local cfg="$1" + local usbcfg gadget usbfun + + config_get usbcfg "$cfg" configuration + [ -z "$usbcfg" ] && return + config_get usbfun "$cfg" function + [ -z "$usbfun" ] && return + + config_get gadget "$usbcfg" gadget + local gadget_path="${GADGET_FS}/${GADGET_PREFIX}${gadget}" + local cfg_path="${gadget_path}/configs/${usbcfg}.1" + [ -d "${cfg_path}" ] || return + + local fun_path="${gadget_path}/functions/${usbfun}.${cfg}" + mkdir "$fun_path" || { + log "failed to create function ${usbfun}.${cfg}" + return + } + + apply_configs "$fun_path" "$cfg" + + ln -s "$fun_path" "$cfg_path" +} + +attach_gadget() { + local cfg="$1" + local gadget_path="${GADGET_FS}/${GADGET_PREFIX}${cfg}" + local param udc + + config_get udc "$cfg" UDC + [ -z "$udc" ] && return + + echo "$udc" > "$gadget_path/UDC" +} + +load_gadget() { + config_foreach setup_gadget gadget + config_foreach setup_configuration configuration + config_foreach setup_function function + config_foreach attach_gadget gadget +} + +# use subshell for isolated env +apply_preset() ( + local preset="$1" + local gadget="$2" + UCI_CONFIG_DIR=$GADGET_PRESETS_DIR config_load "$1" + config_set g1 UDC "$2" + load_gadget +) + +load_preset() { + local cfg="$1" + config_get name "$cfg" name + config_get udc "$cfg" UDC + [ -z "$udc" ] && return + apply_preset $name $udc +} + +start() { + grep -q /sys/kernel/config /proc/mounts || \ + mount -t configfs configfs /sys/kernel/config + + [ -d /sys/kernel/config/usb_gadget ] || { + log "usb_gadget support not found." + return 1 + } + + config_load usbgadget + config_foreach load_preset preset + load_gadget +} + +stop() { + for gadget_path in ${GADGET_FS}/${GADGET_PREFIX}* ; do + [ -d "$gadget_path" ] || continue + echo "" > ${gadget_path}/UDC + find ${gadget_path}/configs -maxdepth 2 -type l -exec rm '{}' ';' + rmdir ${gadget_path}/configs/*/strings/* + rmdir ${gadget_path}/configs/* + rmdir ${gadget_path}/functions/* + rmdir ${gadget_path}/strings/* + rmdir $gadget_path + done +}