#!/bin/sh -e 
 | 
  
 | 
# Uncomment below to see more logs 
 | 
# set -x 
 | 
  
 | 
# Load default env variables from profiles 
 | 
. /etc/profile 
 | 
  
 | 
LOG_FILE=/tmp/usbdevice.log 
 | 
USB_FUNCS_FILE=/tmp/.usbdevice 
 | 
  
 | 
alias usb_enable='touch $USB_FUNCS_FILE' 
 | 
alias usb_disable='rm -f $USB_FUNCS_FILE' 
 | 
alias usb_is_enabled='[ -f $USB_FUNCS_FILE ]' 
 | 
alias usb_set_started='echo $USB_FUNCS > $USB_FUNCS_FILE' 
 | 
usb_get_started() 
 | 
{ 
 | 
    usb_is_enabled || return 0 
 | 
    cat $USB_FUNCS_FILE 
 | 
} 
 | 
  
 | 
CONFIGFS_DIR=/sys/kernel/config 
 | 
USB_GROUP=rockchip 
 | 
USB_STRINGS_ATTR=strings/0x409 
 | 
USB_GADGET_DIR=$CONFIGFS_DIR/usb_gadget/$USB_GROUP 
 | 
USB_GADGET_STRINGS_DIR=$USB_GADGET_DIR/$USB_STRINGS_ATTR 
 | 
USB_FUNCTIONS_DIR=$USB_GADGET_DIR/functions 
 | 
USB_CONFIGS_DIR=$USB_GADGET_DIR/configs/b.1 
 | 
USB_CONFIGS_STRINGS_DIR=$USB_CONFIGS_DIR/$USB_STRINGS_ATTR 
 | 
  
 | 
# Make sure that we own this session (pid equals sid) 
 | 
if ! ps x -o pid,sid | grep -wq "$$$"; then 
 | 
    setsid $0 $@ 
 | 
    exit $? 
 | 
fi 
 | 
  
 | 
# ---- helper functions 
 | 
usb_msg() 
 | 
{ 
 | 
    logger -t $(basename $0) "[$$]: $@" 
 | 
    echo "[$(date +"%F %T")] $@" 
 | 
} 
 | 
  
 | 
usb_pid() 
 | 
{ 
 | 
    case $1 in 
 | 
        ums)        echo 0x0000;; 
 | 
        mtp)        echo 0x0001;; 
 | 
        uvc)        echo 0x0005;; 
 | 
        adb)        echo 0x0006;; 
 | 
        adb_mtp)    echo 0x0011;; 
 | 
        adb_ums)    echo 0x0018;; 
 | 
        uvc_adb)    echo 0x0015;; 
 | 
        uvc_ntb)    echo 0x0017;; 
 | 
        acm)        echo 0x1005;; 
 | 
        *)        echo 0x0019;; 
 | 
    esac 
 | 
} 
 | 
  
 | 
usb_instances() 
 | 
{ 
 | 
    for func in $@; do 
 | 
        VAR=$(echo $func | tr 'a-z' 'A-Z')_INSTANCES 
 | 
        eval echo "\${$VAR:-$func.gs0}" 
 | 
    done 
 | 
} 
 | 
  
 | 
usb_run_stage() 
 | 
{ 
 | 
    for f in $1_pre_$2_hook $1_$2 $1_post_$2_hook; do 
 | 
        type $f >/dev/null 2>/dev/null || continue 
 | 
  
 | 
        usb_msg "Run stage: $f" 
 | 
        eval $f || break 
 | 
    done 
 | 
} 
 | 
  
 | 
usb_wait_files() 
 | 
{ 
 | 
    for i in `seq 200`;do 
 | 
        fuser -s $@ 2>/dev/null && break 
 | 
        sleep .01 
 | 
    done 
 | 
} 
 | 
  
 | 
usb_release_files() 
 | 
{ 
 | 
    for i in `seq 200`;do 
 | 
        fuser -s -k $@ 2>/dev/null || break 
 | 
        sleep .01 
 | 
    done 
 | 
} 
 | 
  
 | 
# usage: usb_mount <src> <mountpoint> <options> 
 | 
usb_mount() 
 | 
{ 
 | 
    mkdir -p $2 
 | 
    mountpoint -q $2 || mount $@ 
 | 
} 
 | 
  
 | 
usb_umount() 
 | 
{ 
 | 
    mountpoint -q $1 || return 0 
 | 
    usb_release_files -m $1 
 | 
    umount $1 
 | 
} 
 | 
  
 | 
usb_symlink() 
 | 
{ 
 | 
    mkdir -p $1 
 | 
    [ -e $2 ] || ln -s $1 $2 
 | 
} 
 | 
  
 | 
usb_try_symlink() 
 | 
{ 
 | 
    usb_symlink $@ &>/dev/null || true 
 | 
} 
 | 
  
 | 
usb_write() 
 | 
{ 
 | 
    if echo "x$1" | grep -q "^x-"; then 
 | 
        OPTS=$1 
 | 
        shift 
 | 
    fi 
 | 
  
 | 
    FILE=$1 
 | 
    shift 
 | 
  
 | 
    if [ -r $FILE ] && [ "$(cat $FILE)" = "$@" ]; then 
 | 
        return 0 
 | 
    fi 
 | 
  
 | 
    echo $OPTS "$@" > $FILE 
 | 
} 
 | 
  
 | 
usb_try_write() 
 | 
{ 
 | 
    usb_write $@ &>/dev/null || true 
 | 
} 
 | 
  
 | 
usb_start_daemon() 
 | 
{ 
 | 
    NAME=$(echo $1 | sed "s#^[^ ]*/\([^ ]*\).*#\1#") 
 | 
    TAG_FILE=/tmp/.usb_$NAME 
 | 
  
 | 
    # Enable spawn 
 | 
    touch $TAG_FILE 
 | 
  
 | 
    # Already started 
 | 
    [ -z "$(usb_get_started)" ] || return 0 
 | 
  
 | 
    # Start and spawn background daemon 
 | 
    { 
 | 
        exec 3<&- 
 | 
  
 | 
        cd / 
 | 
        while usb_is_enabled; do 
 | 
            # Don't spawn after stopped 
 | 
            [ ! -f $TAG_FILE ] || 
 | 
                start-stop-daemon -Sqx $@ || true 
 | 
            sleep .5 
 | 
        done 
 | 
    }& 
 | 
} 
 | 
  
 | 
usb_stop_daemon() 
 | 
{ 
 | 
    NAME=$(echo $1 | sed "s#^[^ ]*/\([^ ]*\).*#\1#") 
 | 
    TAG_FILE=/tmp/.usb_$NAME 
 | 
  
 | 
    # Stop and disable spawn 
 | 
    rm -f $TAG_FILE 
 | 
    start-stop-daemon -Kqox $@ 
 | 
} 
 | 
  
 | 
usb_load_config() 
 | 
{ 
 | 
    USB_CONFIG_FILE=$(find /etc/ -name .usb_config | head -n 1) 
 | 
    [ -n "$USB_CONFIG_FILE" -a -r $USB_CONFIG_FILE ] || return 0 
 | 
  
 | 
    ums_parse() 
 | 
    { 
 | 
        grep "\<$1=" $USB_CONFIG_FILE | cut -d'=' -f2 
 | 
    } 
 | 
    UMS_FILE=$(ums_parse ums_block) 
 | 
    UMS_SIZE=$(ums_parse ums_block_size || echo 0)M 
 | 
    UMS_FSTYPE=$(ums_parse ums_block_type) 
 | 
    UMS_MOUNT=$([ "$(ums_parse ums_block_auto_mount)" != on ]; echo $?) 
 | 
    UMS_RO=$([ "$(ums_parse ums_block_ro)" != on ]; echo $?) 
 | 
  
 | 
    USB_FUNCS=$(grep "usb_.*_en" $USB_CONFIG_FILE | cut -d'_' -f2 | xargs) 
 | 
} 
 | 
  
 | 
# ---- adb 
 | 
ADB_INSTANCES=${ADB_INSTANCES:-ffs.adb} 
 | 
  
 | 
adb_prepare() 
 | 
{ 
 | 
    usb_mount adb /dev/usb-ffs/adb -o uid=2000,gid=2000 -t functionfs 
 | 
    usb_start_daemon /usr/bin/adbd 
 | 
    usb_wait_files -m /dev/usb-ffs/adb 
 | 
} 
 | 
  
 | 
adb_stop() 
 | 
{ 
 | 
    usb_stop_daemon /usr/bin/adbd 
 | 
} 
 | 
  
 | 
# ---- ntb 
 | 
NTB_INSTANCES=${NTB_INSTANCES:-ffs.ntb} 
 | 
  
 | 
ntb_prepare() 
 | 
{ 
 | 
    usb_mount ntb /dev/usb-ffs/ntb -o uid=2000,gid=2000 -t functionfs 
 | 
} 
 | 
  
 | 
# ---- uac1 
 | 
uac1_prepare() 
 | 
{ 
 | 
    for f in $(find . -name "*_feature_unit"); do 
 | 
        echo 1 >$f 
 | 
    done 
 | 
} 
 | 
  
 | 
# ---- uac2 
 | 
uac2_prepare() 
 | 
{ 
 | 
    uac1_prepare 
 | 
} 
 | 
  
 | 
# ---- mtp 
 | 
mtp_prepare() 
 | 
{ 
 | 
    echo "MTP" > os_desc/interface.MTP/compatible_id 
 | 
    echo 1 > $USB_GADGET_DIR/os_desc/use 
 | 
} 
 | 
  
 | 
mtp_start() 
 | 
{ 
 | 
    usb_start_daemon /usr/bin/mtp-server 
 | 
    usb_wait_files /dev/mtp_usb 
 | 
} 
 | 
  
 | 
mtp_stop() 
 | 
{ 
 | 
    usb_stop_daemon /usr/bin/mtp-server 
 | 
    usb_release_files /dev/mtp_usb 
 | 
  
 | 
    echo 0 > $USB_GADGET_DIR/os_desc/use 
 | 
} 
 | 
  
 | 
# ---- acm 
 | 
ACM_INSTANCES=${ACM_INSTANCES:-acm.gs6} 
 | 
  
 | 
# ---- rndis 
 | 
# Nothing special 
 | 
  
 | 
# ---- uvc 
 | 
UVC_INSTANCES=${UVC_INSTANCES:-uvc.gs6} 
 | 
  
 | 
uvc_add_yuyv() 
 | 
{ 
 | 
    WIDTH=$(echo $1 | cut -d'x' -f1) 
 | 
    HEIGHT=$(echo $1 | cut -d'x' -f2) 
 | 
    DIR=${HEIGHT}p 
 | 
  
 | 
    [ ! -d $DIR ] || return 0 
 | 
  
 | 
    mkdir -p $DIR 
 | 
    echo $WIDTH > $DIR/wWidth 
 | 
    echo $HEIGHT > $DIR/wHeight 
 | 
    echo 333333 > $DIR/dwDefaultFrameInterval 
 | 
    echo $((WIDTH * HEIGHT * 20)) > $DIR/dwMinBitRate 
 | 
    echo $((WIDTH * HEIGHT * 20)) > $DIR/dwMaxBitRate 
 | 
    echo $((WIDTH * HEIGHT * 2)) > $DIR/dwMaxVideoFrameBufferSize 
 | 
    echo -e "333333\n666666\n1000000\n2000000" > $DIR/dwFrameInterval 
 | 
} 
 | 
  
 | 
uvc_add_mjpeg() 
 | 
{ 
 | 
    WIDTH=$(echo $1 | cut -d'x' -f1) 
 | 
    HEIGHT=$(echo $1 | cut -d'x' -f2) 
 | 
    DIR=${HEIGHT}p 
 | 
  
 | 
    [ ! -d $DIR ] || return 0 
 | 
  
 | 
    mkdir -p $DIR 
 | 
    echo $WIDTH > $DIR/wWidth 
 | 
    echo $HEIGHT > $DIR/wHeight 
 | 
    echo 333333 > $DIR/dwDefaultFrameInterval 
 | 
    echo $((WIDTH * HEIGHT * 20)) > $DIR/dwMinBitRate 
 | 
    echo $((WIDTH * HEIGHT * 20)) > $DIR/dwMaxBitRate 
 | 
    echo $((WIDTH * HEIGHT * 2)) > $DIR/dwMaxVideoFrameBufferSize 
 | 
    echo -e "333333\n666666\n1000000\n2000000" > $DIR/dwFrameInterval 
 | 
} 
 | 
  
 | 
uvc_add_h264() 
 | 
{ 
 | 
    WIDTH=$(echo $1 | cut -d'x' -f1) 
 | 
    HEIGHT=$(echo $1 | cut -d'x' -f2) 
 | 
    DIR=${HEIGHT}p 
 | 
  
 | 
    [ ! -d $DIR ] || return 0 
 | 
  
 | 
    mkdir -p $DIR 
 | 
    echo $WIDTH > $DIR/wWidth 
 | 
    echo $HEIGHT > $DIR/wHeight 
 | 
    echo 333333 > $DIR/dwDefaultFrameInterval 
 | 
    echo $((WIDTH * HEIGHT * 10)) > $DIR/dwMinBitRate 
 | 
    echo $((WIDTH * HEIGHT * 10)) > $DIR/dwMaxBitRate 
 | 
    echo -e "333333\n666666\n1000000\n2000000" > $DIR/dwFrameInterval 
 | 
} 
 | 
  
 | 
uvc_support_resolutions() 
 | 
{ 
 | 
    case ${1:-yuyv} in 
 | 
        yuyv)    echo "640x480 1280x720";; 
 | 
        mjpeg)    echo "640x480 1280x720 1920x1080 2560x1440 2592x1944";; 
 | 
        h264)    echo "640x480 1280x720 1920x1080";; 
 | 
    esac 
 | 
} 
 | 
  
 | 
uvc_prepare() 
 | 
{ 
 | 
    UVC_DIR=$(pwd) 
 | 
  
 | 
    usb_symlink $UVC_DIR/control/header/h $UVC_DIR/control/class/fs/h 
 | 
    usb_symlink $UVC_DIR/control/header/h $UVC_DIR/control/class/ss/h 
 | 
  
 | 
    usb_symlink $UVC_DIR/streaming/header/h $UVC_DIR/streaming/class/fs/h 
 | 
    usb_symlink $UVC_DIR/streaming/header/h $UVC_DIR/streaming/class/hs/h 
 | 
    usb_symlink $UVC_DIR/streaming/header/h $UVC_DIR/streaming/class/ss/h 
 | 
  
 | 
    UVC_YUYV_RES=$(uvc_support_resolutions yuyv) 
 | 
    if [ -n "$UVC_YUYV_RES" ]; then 
 | 
        usb_try_symlink $UVC_DIR/streaming/uncompressed/u \ 
 | 
            $UVC_DIR/streaming/header/h/u 
 | 
        cd $UVC_DIR/streaming/uncompressed/u 
 | 
  
 | 
        for res in $UVC_YUYV_RES; do 
 | 
            uvc_add_yuyv $res 
 | 
        done 
 | 
    fi 
 | 
  
 | 
    UVC_MJPEG_RES=$(uvc_support_resolutions mjpeg) 
 | 
    if [ -n "$UVC_MJPEG_RES" ]; then 
 | 
        usb_try_symlink $UVC_DIR/streaming/mjpeg/m \ 
 | 
            $UVC_DIR/streaming/header/h/m 
 | 
        cd $UVC_DIR/streaming/mjpeg/m 
 | 
  
 | 
        for res in $UVC_MJPEG_RES; do 
 | 
            uvc_add_mjpeg $res 
 | 
        done 
 | 
    fi 
 | 
  
 | 
    UVC_H264_RES=$(uvc_support_resolutions h264) 
 | 
    if [ -n "$UVC_H264_RES" ]; then 
 | 
        usb_try_symlink $UVC_DIR/streaming/framebased/f \ 
 | 
            $UVC_DIR/streaming/header/h/f 
 | 
        cd $UVC_DIR/streaming/framebased/f 
 | 
  
 | 
        for res in $UVC_H264_RES; do 
 | 
            uvc_add_h264 $res 
 | 
        done 
 | 
  
 | 
        usb_try_write -ne guidFormat "\\x48\\x32\\x36\\x34\\x00\\x00\\x10\\x00\\x80\\x00\\x00\\xaa\\x00\\x38\\x9b\\x71" 
 | 
    fi 
 | 
} 
 | 
  
 | 
# TODO: Start UVC daemon in uvc_start 
 | 
# TODO: Stop UVC daemon in uvc_stop 
 | 
  
 | 
# ---- hid 
 | 
HID_INSTANCES=${HID_INSTANCES:-hid.usb0} 
 | 
  
 | 
hid_prepare() 
 | 
{ 
 | 
    echo 1 > protocol 
 | 
    echo 1 > subclass 
 | 
    echo 8 > report_length 
 | 
    echo -ne "\\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0" \ 
 | 
        > report_desc 
 | 
} 
 | 
  
 | 
# ---- ums 
 | 
UMS_INSTANCES=${UMS_INSTANCES:-mass_storage.0} 
 | 
  
 | 
ums_prepare() 
 | 
{ 
 | 
    if [ ! -f $UMS_FILE ]; then 
 | 
        usb_msg "Formating $UMS_FILE($UMS_SIZE) to $UMS_FSTYPE" 
 | 
        truncate -s $UMS_SIZE $UMS_FILE 
 | 
        mkfs.$UMS_FSTYPE $UMS_FILE || \ 
 | 
            usb_msg "Failed to format $UMS_FILE to $UMS_FSTYPE" 
 | 
    fi 
 | 
} 
 | 
  
 | 
ums_stop() 
 | 
{ 
 | 
    echo > lun.0/file 
 | 
    usb_umount $UMS_MOUNTPOINT 
 | 
  
 | 
    [ "$UMS_MOUNT" -eq 1 ] || return 0 
 | 
  
 | 
    # Try auto fstype firstly 
 | 
    usb_mount $UMS_FILE $UMS_MOUNTPOINT -o sync 2>/dev/null || \ 
 | 
        usb_mount $UMS_FILE $UMS_MOUNTPOINT -o sync -t $UMS_FSTYPE 
 | 
} 
 | 
  
 | 
ums_start() 
 | 
{ 
 | 
    case "$USB_STATE" in 
 | 
        CONFIGURED) 
 | 
            if [ "$(cat lun.0/ro)" != "$UMS_RO" ]; then 
 | 
                echo > lun.0/file 
 | 
                echo $UMS_RO > lun.0/ro 
 | 
            fi 
 | 
  
 | 
            if ! grep -wq $UMS_FILE lun.0/file; then 
 | 
                usb_umount $UMS_MOUNTPOINT 
 | 
                echo $UMS_FILE > lun.0/file 
 | 
            fi 
 | 
            ;; 
 | 
        DISCONNECTED) 
 | 
            ums_stop 
 | 
            ;; 
 | 
    esac 
 | 
} 
 | 
  
 | 
# ---- global 
 | 
usb_init() 
 | 
{ 
 | 
    usb_msg "Initializing" 
 | 
  
 | 
    echo 0x2207 > idVendor 
 | 
    echo 0x0310 > bcdDevice 
 | 
    echo 0x0200 > bcdUSB 
 | 
  
 | 
    mkdir -p $USB_GADGET_STRINGS_DIR 
 | 
    SERIAL=$(grep Serial /proc/cpuinfo | cut -d':' -f2) 
 | 
    echo ${SERIAL:-0123456789ABCDEF} > $USB_GADGET_STRINGS_DIR/serialnumber 
 | 
    echo $USB_GROUP  > $USB_GADGET_STRINGS_DIR/manufacturer 
 | 
    echo "rk3xxx"  > $USB_GADGET_STRINGS_DIR/product 
 | 
  
 | 
    mkdir -p $USB_CONFIGS_DIR 
 | 
    echo 500 > $USB_CONFIGS_DIR/MaxPower 
 | 
  
 | 
    echo 0x1 > os_desc/b_vendor_code 
 | 
    echo MSFT100 > os_desc/qw_sign 
 | 
    ln -s $USB_CONFIGS_DIR os_desc/ 
 | 
  
 | 
    mkdir -p $USB_CONFIGS_STRINGS_DIR 
 | 
} 
 | 
  
 | 
usb_funcs_grep() 
 | 
{ 
 | 
    echo $USB_FUNCS | xargs -n 1 | sort | uniq | grep $@ || true 
 | 
} 
 | 
  
 | 
usb_funcs_sort() 
 | 
{ 
 | 
    { 
 | 
        for func in $@; do 
 | 
            usb_funcs_grep -E $func 
 | 
        done 
 | 
        usb_funcs_grep -vE $(echo $@ | tr ' ' '|') 
 | 
    } | uniq | xargs 
 | 
} 
 | 
  
 | 
usb_prepare() 
 | 
{ 
 | 
    usb_load_config 
 | 
  
 | 
    # Allow function/variable overriding 
 | 
    [ -d /etc/usbdevice.d ] && . /etc/usbdevice.d/* 
 | 
  
 | 
    UMS_FILE=${UMS_FILE:-/userdata/ums_shared.img} 
 | 
    UMS_SIZE=${UMS_SIZE:-256M} 
 | 
    UMS_FSTYPE=${UMS_FSTYPE:-vfat} 
 | 
    UMS_MOUNT=${UMS_MOUNT:-0} 
 | 
    UMS_MOUNTPOINT=${UMS_MOUNTPOINT:-/mnt/ums} 
 | 
    UMS_RO=${UMS_RO:-0} 
 | 
  
 | 
    # Orders required by kernel 
 | 
    USB_FUNCS="$(usb_funcs_sort rndis uac uvc adb ntb ums mtp acm)" 
 | 
    USB_CONFIG="$(echo "$USB_FUNCS" | tr ' ' '_')" 
 | 
  
 | 
    if [ ! -d $USB_GADGET_DIR ]; then 
 | 
        mountpoint -q $CONFIGFS_DIR || \ 
 | 
            mount -t configfs none $CONFIGFS_DIR 
 | 
  
 | 
        mkdir -p $USB_GADGET_DIR 
 | 
        cd $USB_GADGET_DIR 
 | 
  
 | 
        # Global initialize 
 | 
        usb_run_stage usb init 
 | 
    fi 
 | 
  
 | 
    USB_STATE=$(cat /sys/class/android_usb/android0/state) 
 | 
    USB_UDC=$(ls /sys/class/udc/ | head -n 1) 
 | 
  
 | 
    # Parse started USB functions 
 | 
    OLD_FUNCS=$(usb_get_started) 
 | 
  
 | 
    # Stop old USB functions when USB functions changed 
 | 
    if [ -n "$OLD_FUNCS" ] && [ "$OLD_FUNCS" != "$USB_FUNCS" ]; then 
 | 
        usb_msg "Functions changed $OLD_FUNCS -> $USB_FUNCS" 
 | 
        usb_stop 
 | 
    fi 
 | 
  
 | 
    # Update USB PID 
 | 
    echo "$(usb_pid "$USB_CONFIG")" > $USB_GADGET_DIR/idProduct 
 | 
} 
 | 
  
 | 
usb_start() 
 | 
{ 
 | 
    usb_msg "Starting functions: $USB_FUNCS" 
 | 
  
 | 
    echo "$USB_CONFIG" > $USB_CONFIGS_STRINGS_DIR/configuration 
 | 
  
 | 
    for func in $USB_FUNCS; do 
 | 
        for instance in $(usb_instances $func); do 
 | 
            usb_msg "Preparing instance: $instance" 
 | 
  
 | 
            if ! mkdir -p $USB_FUNCTIONS_DIR/$instance 2>/dev/null; then 
 | 
                usb_msg "Failed to create instance: $instance" 
 | 
                continue 
 | 
            fi 
 | 
  
 | 
            cd $USB_FUNCTIONS_DIR/$instance &>/dev/null || continue 
 | 
  
 | 
            usb_run_stage $func prepare 
 | 
  
 | 
            # Make symlink after prepared (required by UVC) 
 | 
            usb_symlink $USB_FUNCTIONS_DIR/$instance \ 
 | 
                $USB_CONFIGS_DIR/f-$instance 
 | 
        done 
 | 
    done 
 | 
  
 | 
    usb_write $USB_GADGET_DIR/UDC $USB_UDC 
 | 
  
 | 
    for func in $USB_FUNCS; do 
 | 
        for instance in $(usb_instances $func); do 
 | 
            cd $USB_FUNCTIONS_DIR/$instance &>/dev/null || continue 
 | 
  
 | 
            usb_msg "Starting instance: $instance" 
 | 
            usb_run_stage $func start 
 | 
        done 
 | 
    done 
 | 
  
 | 
    # Store started functions 
 | 
    usb_set_started 
 | 
} 
 | 
  
 | 
usb_stop() 
 | 
{ 
 | 
    if [ -n "$OLD_FUNCS" ]; then 
 | 
        usb_msg "Stopping functions: $OLD_FUNCS" 
 | 
    fi 
 | 
  
 | 
    usb_write $USB_GADGET_DIR/UDC "" 
 | 
  
 | 
    for func in $USB_FUNCS; do 
 | 
        for instance in $(usb_instances $func); do 
 | 
            cd $USB_FUNCTIONS_DIR/$instance &>/dev/null || continue 
 | 
  
 | 
            usb_msg "Stopping instance: $instance" 
 | 
            usb_run_stage $func stop 
 | 
        done 
 | 
    done 
 | 
  
 | 
    rm -f $USB_CONFIGS_DIR/f-* 
 | 
  
 | 
    # Clear functions to avoid stopping them again 
 | 
    unset OLD_FUNCS 
 | 
} 
 | 
  
 | 
usb_restart() 
 | 
{ 
 | 
    usb_run_stage usb stop 
 | 
    usb_run_stage usb start 
 | 
} 
 | 
  
 | 
ACTION=${1:-update} 
 | 
if [ "$ACTION" = update ]; then 
 | 
    usb_is_enabled || exit 0 
 | 
fi 
 | 
  
 | 
# Lock it 
 | 
exec 3<$0 
 | 
flock -x 3 
 | 
  
 | 
echo "Starting $0 ${ACTION}, log saved to $LOG_FILE" 
 | 
  
 | 
# Redirect outputs to log file 
 | 
exec >>$LOG_FILE 2>&1 
 | 
  
 | 
usb_msg "Handling ${ACTION} request" 
 | 
  
 | 
usb_run_stage usb prepare 
 | 
  
 | 
case "$ACTION" in 
 | 
    start|update) 
 | 
        usb_enable 
 | 
        usb_run_stage usb start 
 | 
        ;; 
 | 
    stop) 
 | 
        usb_disable 
 | 
        usb_run_stage usb stop 
 | 
        ;; 
 | 
    restart) 
 | 
        usb_enable 
 | 
        usb_run_stage usb restart 
 | 
        ;; 
 | 
    *) 
 | 
        echo "Usage: usbdevice [start|stop|restart|update]" >&2 
 | 
        ;; 
 | 
esac 
 | 
  
 | 
usb_msg "Done $ACTION request" 
 | 
echo 
 | 
  
 | 
# Unlock it 
 | 
flock -u 3 
 |