Files
msm8916-openwrt/msm89xx/base-files/etc/init.d/msm8916-usb-gadget.sh
T
2025-09-14 22:47:31 +02:00

372 lines
10 KiB
Bash
Executable File

#!/bin/bash
# Main script for MSM8916 USB Gadget
#SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
#CONFIG_FILE="${SCRIPT_DIR}/msm8916-usb-gadget.conf"
CONFIG_FILE="/etc/msm8916-usb-gadget.conf"
GADGET_PATH="/sys/kernel/config/usb_gadget/msm8916"
# Load configuration
[ -f "${CONFIG_FILE}" ] && . "${CONFIG_FILE}"
# Set defaults if not configured
: ${USB_VENDOR_ID:="0x1d6b"}
: ${USB_PRODUCT_ID:="0x0104"}
: ${USB_DEVICE_VERSION:="0x0100"}
: ${USB_MANUFACTURER:="MSM8916"}
: ${USB_PRODUCT:="USB Gadget"}
: ${NETWORK_BRIDGE:="br0"}
# Helper functions
log() {
logger -t msm8916-usb-gadget "$@"
echo "$(date '+%Y-%m-%d %H:%M:%S') $@"
}
error() {
log "ERROR: $@"
exit 1
}
find_udc_device() {
if [ -n "${UDC_DEVICE}" ]; then
echo "${UDC_DEVICE}"
return
fi
# MSM8916 typically uses ci_hdrc.0
if [ -e "/sys/class/udc/ci_hdrc.0" ]; then
echo "ci_hdrc.0"
return
fi
# Fallback: first available UDC
udc=$(ls /sys/class/udc/ 2>/dev/null | head -1)
if [ -n "${udc}" ]; then
echo "${udc}"
else
error "No UDC device found"
fi
}
get_serial_number() {
# Use machine-id as the source of uniqueness
if [ -f /etc/machine-id ]; then
sha256sum < /etc/machine-id | cut -d' ' -f1 | cut -c1-16
else
# Fallback to random if machine-id doesn't exist
echo "$(date +%s)-$(shuf -i 1000-9999 -n 1)"
fi
}
generate_mac_address() {
local prefix="$1"
local serial="$(get_serial_number)"
local hash="$(echo "${serial}${prefix}" | md5sum | cut -c1-12)"
# Extract bytes
local b1="${hash:0:2}"
local b2="${hash:2:2}"
local b3="${hash:4:2}"
local b4="${hash:6:2}"
local b5="${hash:8:2}"
local b6="${hash:10:2}"
# Set locally administered, unicast
b1="$(printf '%02x' $((0x${b1} & 0xfe | 0x02)))"
echo "${b1}:${b2}:${b3}:${b4}:${b5}:${b6}"
}
load_mac_addresses() {
# Generate new MAC addresses for enabled functions
[ "${ENABLE_RNDIS}" = "1" ] && {
MAC_RNDIS_HOST="$(generate_mac_address "rndis-host")"
MAC_RNDIS_DEV="$(generate_mac_address "rndis-dev")"
}
[ "${ENABLE_ECM}" = "1" ] && {
MAC_ECM_HOST="$(generate_mac_address "ecm-host")"
MAC_ECM_DEV="$(generate_mac_address "ecm-dev")"
}
[ "${ENABLE_NCM}" = "1" ] && {
MAC_NCM_HOST="$(generate_mac_address "ncm-host")"
MAC_NCM_DEV="$(generate_mac_address "ncm-dev")"
}
}
create_storage_image() {
local image_path="$1"
local size="${UMS_IMAGE_SIZE:-100M}"
log "Creating storage image: ${image_path} (${size})"
# Create the raw image file
truncate -s "${size}" "${image_path}" || \
dd if=/dev/zero of="${image_path}" bs=1M count="${size%M}"
log "Storage image created successfully (raw format)"
}
setup_gadget() {
log "Setting up USB gadget"
# Load required modules
modprobe libcomposite
# Mount configfs if not already mounted
if ! mountpoint -q /sys/kernel/config; then
log "Mounting configfs"
mount -t configfs none /sys/kernel/config
fi
# Create storage image if needed and UMS is enabled
if [ "${ENABLE_UMS}" = "1" ] && [ ! -f "${UMS_IMAGE}" ]; then
mkdir -p "$(dirname "${UMS_IMAGE}")"
create_storage_image "${UMS_IMAGE}"
fi
# Create gadget
mkdir -p "${GADGET_PATH}" || error "Failed to create gadget directory"
cd "${GADGET_PATH}" || error "Failed to change to gadget directory"
# Basic device info
echo "${USB_VENDOR_ID}" > idVendor || error "Failed to set vendor ID"
echo "${USB_PRODUCT_ID}" > idProduct || error "Failed to set product ID"
echo "${USB_DEVICE_VERSION}" > bcdDevice || error "Failed to set device version"
# Composite device descriptors
echo "0xEF" > bDeviceClass
echo "0x02" > bDeviceSubClass
echo "0x01" > bDeviceProtocol
# Strings
mkdir -p strings/0x409
echo "$(get_serial_number)" > strings/0x409/serialnumber
echo "${USB_MANUFACTURER}" > strings/0x409/manufacturer
echo "${USB_PRODUCT}" > strings/0x409/product
# Load MAC addresses
load_mac_addresses
local cfg="configs/c.1"
mkdir -p "${cfg}/strings/0x409"
local cfg_str=""
local has_wakeup=0
# RNDIS (must be first for Windows compatibility)
if [ "${ENABLE_RNDIS}" = "1" ]; then
log "Enabling RNDIS"
cfg_str="${cfg_str}+RNDIS"
mkdir -p functions/rndis.usb0
echo "${MAC_RNDIS_HOST}" > functions/rndis.usb0/host_addr
echo "${MAC_RNDIS_DEV}" > functions/rndis.usb0/dev_addr
ln -sf functions/rndis.usb0 "${cfg}"
# Windows compatibility
echo 1 > os_desc/use
echo 0xcd > os_desc/b_vendor_code
echo MSFT100 > os_desc/qw_sign
echo RNDIS > functions/rndis.usb0/os_desc/interface.rndis/compatible_id
echo 5162001 > functions/rndis.usb0/os_desc/interface.rndis/sub_compatible_id
ln -sf "${cfg}" os_desc
has_wakeup=1
fi
# ACM Serial ports
if [ "${ENABLE_ACM}" = "1" ]; then
log "Enabling ACM serial (${ACM_COUNT:-1} ports)"
# Create multiple ACM ports if requested
acm_count="${ACM_COUNT:-1}"
for i in $(seq 0 $((acm_count - 1))); do
cfg_str="${cfg_str}+ACM"
mkdir -p functions/acm.GS${i}
# Link directly to config
ln -sf functions/acm.GS${i} "${cfg}"
done
fi
# ECM
if [ "${ENABLE_ECM}" = "1" ] && [ "${ENABLE_NCM}" != "1" ]; then
log "Enabling ECM"
cfg_str="${cfg_str}+ECM"
mkdir -p functions/ecm.usb0
echo "${MAC_ECM_HOST}" > functions/ecm.usb0/host_addr
echo "${MAC_ECM_DEV}" > functions/ecm.usb0/dev_addr
ln -sf functions/ecm.usb0 "${cfg}"
has_wakeup=1
fi
# NCM (preferred over ECM)
if [ "${ENABLE_NCM}" = "1" ]; then
log "Enabling NCM"
cfg_str="${cfg_str}+NCM"
mkdir -p functions/ncm.usb0
echo "${MAC_NCM_HOST}" > functions/ncm.usb0/host_addr
echo "${MAC_NCM_DEV}" > functions/ncm.usb0/dev_addr
ln -sf functions/ncm.usb0 "${cfg}"
has_wakeup=1
fi
# Mass Storage
if [ "${ENABLE_UMS}" = "1" ]; then
if [ ! -f "${UMS_IMAGE}" ]; then
log "Warning: UMS image ${UMS_IMAGE} not found"
else
log "Enabling UMS"
cfg_str="${cfg_str}+UMS"
mkdir -p functions/mass_storage.0
echo "${UMS_READONLY:-0}" > functions/mass_storage.0/lun.0/ro
echo "${UMS_IMAGE}" > functions/mass_storage.0/lun.0/file
ln -sf functions/mass_storage.0 "${cfg}"
fi
fi
# Set configuration attributes
if [ "${has_wakeup}" = "1" ]; then
echo "0xe0" > "${cfg}/bmAttributes" # Self-powered with remote wakeup
else
echo "0xc0" > "${cfg}/bmAttributes" # Self-powered
fi
echo "${cfg_str:1}" > "${cfg}/strings/0x409/configuration"
# Enable gadget
local udc="$(find_udc_device)"
log "Using UDC: ${udc}"
echo "${udc}" > UDC || error "Failed to enable UDC"
# Configure network interfaces
# setup_network()
# Use uci instead!
}
setup_network() {
log "Configuring network interfaces"
# Wait for interfaces to appear
sleep 1
# Check if bridge exists
if ! ip link show "${NETWORK_BRIDGE}" >/dev/null 2>&1; then
log "Warning: Bridge ${NETWORK_BRIDGE} does not exist"
return
fi
# Add interfaces to bridge
if [ "${ENABLE_RNDIS}" = "1" ] && [ -f functions/rndis.usb0/ifname ]; then
rndis_if="$(cat functions/rndis.usb0/ifname)"
log "Adding ${rndis_if} to bridge ${NETWORK_BRIDGE}"
ip link set "${rndis_if}" up
ip link set "${rndis_if}" master "${NETWORK_BRIDGE}" || true
fi
if [ "${ENABLE_ECM}" = "1" ] && [ -f functions/ecm.usb0/ifname ]; then
ecm_if="$(cat functions/ecm.usb0/ifname)"
log "Adding ${ecm_if} to bridge ${NETWORK_BRIDGE}"
ip link set "${ecm_if}" up
ip link set "${ecm_if}" master "${NETWORK_BRIDGE}" || true
fi
if [ "${ENABLE_NCM}" = "1" ] && [ -f functions/ncm.usb0/ifname ]; then
ncm_if="$(cat functions/ncm.usb0/ifname)"
log "Adding ${ncm_if} to bridge ${NETWORK_BRIDGE}"
ip link set "${ncm_if}" up
ip link set "${ncm_if}" master "${NETWORK_BRIDGE}" || true
fi
}
teardown_gadget() {
log "Tearing down USB gadget"
# Check if configfs is mounted
if ! mountpoint -q /sys/kernel/config; then
log "Configfs not mounted, nothing to tear down"
return
fi
# Check if gadget directory exists
if [ ! -d "/sys/kernel/config/usb_gadget" ]; then
log "USB gadget configfs not available"
return
fi
cd /sys/kernel/config/usb_gadget
if [ ! -d msm8916 ]; then
return
fi
cd msm8916
# Disable gadget
echo "" > UDC || true
# Remove network interfaces from bridge
for func in functions/*/ifname; do
if [ -f "${func}" ]; then
iface="$(cat "${func}")"
ip link set "${iface}" nomaster || true
ip link set "${iface}" down || true
fi
done
# Remove configuration - use wildcard to catch all 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 functions
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 ..
rmdir msm8916 2>/dev/null || true
}
status() {
if [ -d "${GADGET_PATH}" ] && [ -s "${GADGET_PATH}/UDC" ]; then
echo "USB Gadget is active"
echo "UDC: $(cat ${GADGET_PATH}/UDC)"
echo "Functions:"
ls -1 ${GADGET_PATH}/configs/c.1/ 2>/dev/null | grep -v strings
return 0
else
echo "USB Gadget is inactive"
return 1
fi
}
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}"
exit 1
;;
esac