First attempt!

This commit is contained in:
Miguel Fuertes committed 2025-10-07 16:32:18 +02:00
1 parent dc3b6d15dc
commit a86fb9c737
4 files changed
+607

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=uci-usb-gadget
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_MAINTAINER:=Miguel Fuertes <hkfuertes@gmail.com>
PKG_LICENSE:=GPL-2.0-only
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Universal USB Gadget Manager
DEPENDS:=@USB_GADGET_SUPPORT +bash +kmod-usb-gadget +kmod-usb-gadget-eth +kmod-usb-configfs
PKGARCH:=all
endef
define Package/$(PKG_NAME)/description
Universal USB Gadget Manager for OpenWrt using configfs.
Supports RNDIS, ECM, NCM, ACM serial, and mass storage functions.
Works on any Linux device with USB gadget support (Raspberry Pi, MSM8916, etc).
endef
define Package/$(PKG_NAME)/conffiles
/etc/config/usbgadget
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/install
# Install main script
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) ./files/usb-gadget.sh $(1)/usr/sbin/usb-gadget
# Install init script
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/usb-gadget.init $(1)/etc/init.d/usb-gadget
# Install UCI config
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/usbgadget.config $(1)/etc/config/usbgadget
# Create storage directory
$(INSTALL_DIR) $(1)/var/lib/usb-gadget
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || {
# Enable service on first install
/etc/init.d/usb-gadget enable
echo "USB Gadget service installed. Configure in /etc/config/usbgadget"
echo "Start with: /etc/init.d/usb-gadget start"
}
exit 0
endef
define Package/$(PKG_NAME)/prerm
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || {
# Stop and disable service before removal
/etc/init.d/usb-gadget stop
/etc/init.d/usb-gadget disable
}
exit 0
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
@@ -0,0 +1,34 @@
# /etc/config/usbgadget
config device 'usb'
option gadget_name 'g1' # Nombre del gadget (cualquier string)
option vendor_id '0x1d6b' # Linux Foundation
option product_id '0x0104' # Multifunction Composite Gadget
option device_version '0x0100' # v1.0.0
option manufacturer 'Linux'
option product 'USB Gadget'
option udc_device '' # Auto-detect si está vacío
config function 'rndis'
option enabled '1'
option description 'MS-Windows compatible ethernet'
config function 'ecm'
option enabled '0'
option description 'CDC ECM ethernet'
config function 'ncm'
option enabled '0'
option description 'CDC NCM ethernet (Best Performance)'
config function 'ums'
option enabled '0'
option description 'Mass storage'
option image_path '/var/lib/usb-gadget/storage.img'
option image_size '512M'
option readonly '0'
config function 'acm'
option enabled '0'
option description 'Serial console'
option shell '1'
+462
View File
@@ -0,0 +1,462 @@
#!/bin/bash
# Universal USB Gadget Manager for Linux
# Requires: bash, UCI (OpenWrt)
# Compatible: OpenWrt, Raspberry Pi, any device with USB gadget support
set -e
# Load UCI library
. /lib/functions.sh
# ============================================================================
# Configuration
# ============================================================================
declare -A CFG
load_config() {
config_load usbgadget
# Gadget name (configurable, works on any hardware)
config_get CFG[gadget_name] usb gadget_name "g1"
# Paths
CFG[gadget_path]="/sys/kernel/config/usb_gadget/${CFG[gadget_name]}"
CFG[config_path]="${CFG[gadget_path]}/configs/c.1"
CFG[functions_path]="${CFG[gadget_path]}/functions"
# USB Device IDs
config_get CFG[vendor_id] usb vendor_id "0x1d6b"
config_get CFG[product_id] usb product_id "0x0104"
config_get CFG[device_version] usb device_version "0x0100"
config_get CFG[manufacturer] usb manufacturer "Linux"
config_get CFG[product] usb product "USB Gadget"
config_get CFG[udc_device] usb udc_device ""
# Functions (boolean)
config_get_bool CFG[rndis] rndis enabled 0
config_get_bool CFG[ecm] ecm enabled 0
config_get_bool CFG[ncm] ncm enabled 0
config_get_bool CFG[acm] acm enabled 0
config_get_bool CFG[ums] ums enabled 0
# ACM options
config_get_bool CFG[acm_shell] acm shell 1
# UMS options
config_get CFG[ums_image] ums image_path "/var/lib/usb-gadget/storage.img"
config_get CFG[ums_size] ums image_size "512M"
config_get_bool CFG[ums_readonly] ums readonly 0
}
# ============================================================================
# Logging
# ============================================================================
log() {
logger -t usb-gadget "$*"
echo "[$(date '+%H:%M:%S')] $*"
}
error() {
log "ERROR: $*"
exit 1
}
# ============================================================================
# Utilities
# ============================================================================
get_serial_number() {
[[ -f /etc/machine-id ]] && {
sha256sum < /etc/machine-id | cut -d' ' -f1 | cut -c1-16
return
}
echo "$(date +%s)-$(shuf -i 1000-9999 -n 1)"
}
generate_mac() {
local prefix="$1"
local serial="$(get_serial_number)"
local hash="$(echo "${serial}${prefix}" | md5sum | cut -c1-12)"
# Set locally administered, unicast
local b1="$(printf '%02x' $((0x${hash:0:2} & 0xfe | 0x02)))"
echo "${b1}:${hash:2:2}:${hash:4:2}:${hash:6:2}:${hash:8:2}:${hash:10:2}"
}
find_udc() {
# Return configured UDC if set
[[ -n "${CFG[udc_device]}" ]] && {
echo "${CFG[udc_device]}"
return
}
# Auto-detect UDC - works on ALL hardware:
# - Raspberry Pi (dwc2): 20980000.usb, fe980000.usb, 1000480000.usb
# - MSM8916 (ci_hdrc): ci_hdrc.0
# - Orange Pi (musb): musb-hdrc.4.auto
# - etc...
local udc="$(ls /sys/class/udc/ 2>/dev/null | head -1)"
[[ -n "$udc" ]] || error "No UDC device found. Is USB gadget support enabled?"
echo "$udc"
}
sysfs_write() {
local path="$1"
local value="$2"
echo "$value" > "$path" || error "Failed to write '$value' to $path"
}
# ============================================================================
# USB Functions Setup
# ============================================================================
setup_rndis() {
log "Enabling RNDIS"
local func="${CFG[functions_path]}/rndis.usb0"
mkdir -p "$func"
sysfs_write "${func}/host_addr" "$(generate_mac rndis-host)"
sysfs_write "${func}/dev_addr" "$(generate_mac rndis-dev)"
ln -sf "$func" "${CFG[config_path]}/"
# Windows compatibility
sysfs_write "${CFG[gadget_path]}/os_desc/use" "1"
sysfs_write "${CFG[gadget_path]}/os_desc/b_vendor_code" "0xcd"
sysfs_write "${CFG[gadget_path]}/os_desc/qw_sign" "MSFT100"
sysfs_write "${func}/os_desc/interface.rndis/compatible_id" "RNDIS"
sysfs_write "${func}/os_desc/interface.rndis/sub_compatible_id" "5162001"
ln -sf "${CFG[config_path]}" "${CFG[gadget_path]}/os_desc/"
echo "+RNDIS"
}
setup_ecm() {
log "Enabling ECM"
local func="${CFG[functions_path]}/ecm.usb0"
mkdir -p "$func"
sysfs_write "${func}/host_addr" "$(generate_mac ecm-host)"
sysfs_write "${func}/dev_addr" "$(generate_mac ecm-dev)"
ln -sf "$func" "${CFG[config_path]}/"
echo "+ECM"
}
setup_ncm() {
log "Enabling NCM"
local func="${CFG[functions_path]}/ncm.usb0"
mkdir -p "$func"
sysfs_write "${func}/host_addr" "$(generate_mac ncm-host)"
sysfs_write "${func}/dev_addr" "$(generate_mac ncm-dev)"
ln -sf "$func" "${CFG[config_path]}/"
echo "+NCM"
}
setup_acm() {
log "Enabling ACM"
local func="${CFG[functions_path]}/acm.GS0"
mkdir -p "$func"
ln -sf "$func" "${CFG[config_path]}/"
# Manage shell (OpenWrt specific)
if [[ -f /etc/inittab ]]; then
sed -i '/ttyGS0/d' /etc/inittab
[[ "${CFG[acm_shell]}" == "1" ]] && \
echo "ttyGS0::askfirst:/usr/libexec/login.sh" >> /etc/inittab
kill -HUP 1 2>/dev/null || true
fi
echo "+ACM"
}
setup_ums() {
local image="${CFG[ums_image]}"
# Create image if doesn't exist
[[ ! -f "$image" ]] && {
log "Creating storage image: $image (${CFG[ums_size]})"
mkdir -p "$(dirname "$image")"
truncate -s "${CFG[ums_size]}" "$image" || \
dd if=/dev/zero of="$image" bs=1M count="${CFG[ums_size]%M}"
}
log "Enabling UMS"
local func="${CFG[functions_path]}/mass_storage.0"
mkdir -p "$func"
sysfs_write "${func}/lun.0/ro" "${CFG[ums_readonly]}"
sysfs_write "${func}/lun.0/file" "$image"
ln -sf "$func" "${CFG[config_path]}/"
echo "+UMS"
}
# ============================================================================
# Network Configuration (OpenWrt specific)
# ============================================================================
add_to_bridge() {
local ifname_file="$1"
[[ ! -f "$ifname_file" ]] && return
local iface="$(cat "$ifname_file")"
log "Adding $iface to bridge"
uci -q delete "network.@device[0].ports=$iface" 2>/dev/null || true
uci add_list "network.@device[0].ports=$iface"
}
setup_network() {
# Skip if not OpenWrt
command -v uci &>/dev/null || {
log "Not OpenWrt, skipping network configuration"
return
}
sleep 1
[[ "${CFG[rndis]}" == "1" ]] && add_to_bridge "${CFG[functions_path]}/rndis.usb0/ifname"
[[ "${CFG[ecm]}" == "1" ]] && add_to_bridge "${CFG[functions_path]}/ecm.usb0/ifname"
[[ "${CFG[ncm]}" == "1" ]] && add_to_bridge "${CFG[functions_path]}/ncm.usb0/ifname"
uci commit network
/etc/init.d/network reload
}
# ============================================================================
# Main Gadget Management
# ============================================================================
setup_gadget() {
log "Setting up USB gadget"
# Load configuration
load_config
log "Gadget name: ${CFG[gadget_name]}"
log "Gadget path: ${CFG[gadget_path]}"
# Prepare system
modprobe libcomposite || error "Failed to load libcomposite module"
mountpoint -q /sys/kernel/config || {
log "Mounting configfs"
mount -t configfs none /sys/kernel/config
}
# Create gadget directory
mkdir -p "${CFG[gadget_path]}" || error "Failed to create gadget directory"
cd "${CFG[gadget_path]}"
# Device descriptors
sysfs_write idVendor "${CFG[vendor_id]}"
sysfs_write idProduct "${CFG[product_id]}"
sysfs_write bcdDevice "${CFG[device_version]}"
# Composite device class
sysfs_write bDeviceClass "0xEF"
sysfs_write bDeviceSubClass "0x02"
sysfs_write bDeviceProtocol "0x01"
# Strings (USB descriptors)
mkdir -p strings/0x409
sysfs_write strings/0x409/serialnumber "$(get_serial_number)"
sysfs_write strings/0x409/manufacturer "${CFG[manufacturer]}"
sysfs_write strings/0x409/product "${CFG[product]}"
# Configuration
mkdir -p "${CFG[config_path]}/strings/0x409"
local config_string=""
local has_wakeup=0
# Setup enabled functions in order
# RNDIS must be first for Windows compatibility
if [[ "${CFG[rndis]}" == "1" ]]; then
config_string+="$(setup_rndis)"
has_wakeup=1
fi
[[ "${CFG[acm]}" == "1" ]] && config_string+="$(setup_acm)"
if [[ "${CFG[ecm]}" == "1" ]] && [[ "${CFG[ncm]}" != "1" ]]; then
config_string+="$(setup_ecm)"
has_wakeup=1
fi
if [[ "${CFG[ncm]}" == "1" ]]; then
config_string+="$(setup_ncm)"
has_wakeup=1
fi
[[ "${CFG[ums]}" == "1" ]] && config_string+="$(setup_ums)"
# Configuration attributes
if [[ $has_wakeup -eq 1 ]]; then
sysfs_write "${CFG[config_path]}/bmAttributes" "0xe0" # Self-powered + remote wakeup
else
sysfs_write "${CFG[config_path]}/bmAttributes" "0xc0" # Self-powered
fi
sysfs_write "${CFG[config_path]}/MaxPower" "250" # 500mA
# Set configuration string (remove leading +)
sysfs_write "${CFG[config_path]}/strings/0x409/configuration" "${config_string#+}"
# Find and enable UDC
local udc="$(find_udc)"
log "Enabling UDC: $udc"
sysfs_write UDC "$udc"
# Wait for ACM device if enabled
if [[ "${CFG[acm]}" == "1" ]]; then
log "Waiting for ttyGS0 device..."
for i in {1..10}; do
[[ -c /dev/ttyGS0 ]] && break
sleep 1
done
[[ ! -c /dev/ttyGS0 ]] && log "Warning: /dev/ttyGS0 not found after 10s"
fi
# Configure network (OpenWrt only)
setup_network
log "USB Gadget setup complete"
}
teardown_gadget() {
log "Tearing down USB gadget"
# Load config to get paths
load_config
[[ ! -d "${CFG[gadget_path]}" ]] && {
log "Gadget not found, nothing to tear down"
return
}
cd "${CFG[gadget_path]}"
# Disable gadget
echo "" > UDC 2>/dev/null || true
# Remove from network bridge (OpenWrt)
if command -v uci &>/dev/null; then
for ifname in functions/*/ifname; do
[[ -f "$ifname" ]] && {
local iface="$(cat "$ifname")"
uci -q delete "network.@device[0].ports=$iface" 2>/dev/null || true
}
done
uci commit network
/etc/init.d/network reload
fi
# Remove configuration links
rm -f configs/c.1/* 2>/dev/null || true
rm -f os_desc/c.1 2>/dev/null || true
rmdir configs/c.1/strings/0x409 2>/dev/null || true
rmdir configs/c.1 2>/dev/null || true
# Remove function directories
for func in functions/*; do
[[ -d "$func" ]] && rmdir "$func" 2>/dev/null || true
done
# Remove strings
rmdir strings/0x409 2>/dev/null || true
# Remove gadget
cd /sys/kernel/config/usb_gadget
rmdir "${CFG[gadget_name]}" 2>/dev/null || true
# Clean shell from inittab
if [[ -f /etc/inittab ]]; then
sed -i '/ttyGS0/d' /etc/inittab
kill -HUP 1 2>/dev/null || true
fi
log "Teardown complete"
}
status() {
load_config
if [[ -d "${CFG[gadget_path]}" ]] && [[ -s "${CFG[gadget_path]}/UDC" ]]; then
echo "USB Gadget: ${CFG[gadget_name]}"
echo "Status: Active"
echo "UDC: $(cat "${CFG[gadget_path]}/UDC")"
echo ""
echo "Enabled functions:"
ls -1 "${CFG[config_path]}" 2>/dev/null | grep -v strings | sed 's/^/ - /'
# Show ACM status
if [[ "${CFG[acm]}" == "1" ]]; then
echo ""
echo "Serial console:"
if [[ -c /dev/ttyGS0 ]]; then
if [[ "${CFG[acm_shell]}" == "1" ]]; then
echo " /dev/ttyGS0 - Available (with shell)"
else
echo " /dev/ttyGS0 - Available (raw TTY)"
fi
else
echo " /dev/ttyGS0 - Not found"
fi
fi
# Show network interfaces
echo ""
echo "Network interfaces:"
for ifname in "${CFG[functions_path]}"/*/ifname; do
[[ -f "$ifname" ]] && {
local iface="$(cat "$ifname")"
local state="$(cat /sys/class/net/$iface/operstate 2>/dev/null || echo unknown)"
echo " - $iface ($state)"
}
done
return 0
else
echo "USB Gadget: ${CFG[gadget_name]}"
echo "Status: Inactive"
return 1
fi
}
# ============================================================================
# Main Entry Point
# ============================================================================
case "${1:-}" in
start)
teardown_gadget
setup_gadget
;;
stop)
teardown_gadget
;;
restart)
teardown_gadget
setup_gadget
;;
status)
status
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
echo ""
echo "Universal USB Gadget Manager"
echo "Compatible with any Linux device with USB gadget support"
exit 1
;;
esac
@@ -0,0 +1,40 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2025 OpenWrt.org
START=99
STOP=10
USE_PROCD=1
PROG=/usr/sbin/usb-gadget
start_service() {
# Check if USB gadget support is available
[ -d /sys/kernel/config ] || {
logger -t usb-gadget "ERROR: configfs not available"
return 1
}
# Run setup
$PROG start
}
stop_service() {
$PROG stop
}
restart() {
$PROG restart
}
status_service() {
$PROG status
}
service_triggers() {
procd_add_reload_trigger "usbgadget"
}
reload_service() {
restart
}