#!/bin/sh
#
# Copyright (c) 2012-2015, 2018-2021, The Linux Foundation. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of The Linux Foundation nor the names of its
#       contributors may be used to endorse or promote products derived from
#       this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE DISCLAIMED.  IN NO
# EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
# Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause-Clear

# Starts the USB Android Gadget.
export KERNEL=`uname -r`
export SOFTAP_W_DUN="N"
export ANDROID_LOG_TAGS="*:d"
export LS_COLORS="none"
export USB_ROLE_SWITCH="N"

usb_bind() {
	# Block suspend so that bind operation is not terminated
	echo usb > /sys/power/wake_lock
	timeout=0
	udcname=`ls -1 /sys/bus/platform/devices/ | grep -E 'hsusb|ssusb' | head -n 1`
	while :
	do
		cur_role=`cat /sys/bus/platform/devices/${udcname}/mode`
		if [ $cur_role == "host" ]; then
			break
		fi

		UDC=`ls -1 /sys/class/udc | head -n 1`
		if [ ! -z $UDC ]; then
			echo $UDC > /sys/kernel/config/usb_gadget/g1/UDC
			break
		fi
		if [ "$timeout" -ge 25 ]; then
			echo "No role switch notification. Enabling USB in debug mode" > /dev/kmsg
			echo peripheral > /sys/bus/platform/devices/${udcname}/mode
			sleep 2
			UDC=`ls -1 /sys/class/udc | head -n 1`
			echo $UDC > /sys/kernel/config/usb_gadget/g1/UDC
			break
		fi
		timeout=`expr $timeout + 1`
		sleep 1
	done
	echo usb > /sys/power/wake_unlock
}

# Supports "DWC3_RW_RPM_XHCI" traces
enable_dwc3_ftraces() {
        cd /sys/kernel/debug/tracing/instances

        if [ $(cat /data/enable_dwc3_ftraces | grep -c "DWC3") -gt 0 ]; then
                mkdir usb
                echo 1 > usb/events/dwc3/enable

                # Excplicitely disable readl/writel traces
                echo 0 > usb/events/dwc3/dwc3_readl/enable
                echo 0 > usb/events/dwc3/dwc3_writel/enable
                echo 1 > usb/tracing_on
        fi

        if [ $(cat /data/enable_dwc3_ftraces | grep -c "RW") -gt 0 ]; then
                mkdir dwc3_rw_traces
                echo 1 > dwc3_rw_traces/events/dwc3/dwc3_readl/enable
                echo 1 > dwc3_rw_traces/events/dwc3/dwc3_writel/enable
                echo 1 > dwc3_rw_traces/tracing_on
        fi

        if [ $(cat /data/enable_dwc3_ftraces | grep -c "RPM") -gt 0 ]; then
                mkdir rpm_usb
                echo 'name~"a600000.*"' > rpm_usb/events/rpm/filter
                echo 1 > rpm_usb/events/rpm/rpm_resume/enable
                echo 1 > rpm_usb/events/rpm/rpm_suspend/enable
                echo 1 > rpm_usb/events/rpm/rpm_return_int/enable
                echo 1 > rpm_usb/tracing_on
        fi

        if [ $(cat /data/enable_dwc3_ftraces | grep -c "XHCI") -gt 0 ]; then
                mkdir usb_xhci
                echo 1 > usb_xhci/events/xhci-hcd/enable
                echo 1 > usb_xhci/tracing_on
        fi
        cd /
}

wait_for_debugfs() {
        timeout=0
        while :
        do
		# wait for the trace framework to come up
                if [ -d /sys/kernel/debug/tracing/instances ]; then
                        enable_dwc3_ftraces
                        break
                fi

                if [ "$timeout" -ge 25 ]; then
                        break
                fi

                timeout=`expr $timeout + 1`
                sleep 1
        done
}

case "$1" in
  init)
	# enable concurrent SoftAP and DUN
	if [ -f /etc/data/usb/softap_w_dun ]
	then
		SOFTAP_W_DUN=`cat /etc/data/usb/softap_w_dun`
	fi

	# bail out from script if qcom_gadget is enabled and
	# SoftAP with DUN is not enabled
	enabled=`cat /sys/bus/platform/devices/qcom_gadget/enabled`
	if [ "$enabled" == "Y" ]; then
		if [ "$SOFTAP_W_DUN" == "N" ]; then
			exit 0
		else
			echo "SOFTAP + DUN is enabled, disabling qcom_gadget" > /dev/kmsg
			echo 0 > /sys/bus/platform/devices/qcom_gadget/enabled
			usb_qti_partition=`cat /proc/mtd | grep "usb_qti" | cut -d ":" -f1`
			if [ "$usb_qti_partition" != "" ]; then
				flash_erase -q /dev/$usb_qti_partition 0 4
			fi
		fi
	fi

	if [ -d /sys/class/android_usb/android0/f_ffs ]
	then
		echo adb > /sys/class/android_usb/android0/f_ffs/aliases
	fi

	# get soc platform to update product related information
	if [ -f /sys/devices/soc0/hw_platform ]; then
		socplatform=`cat /sys/devices/soc0/machine` 2> /dev/null
		soc_hwplatform=`cat /sys/devices/soc0/hw_platform`
		soc_subtypeid=`cat /sys/devices/soc0/platform_subtype_id`
	else
		socplatform=`cat /sys/devices/system/soc/soc0/machine` 2> /dev/null
	fi
	echo "soc:" $socplatform

	# find serialno from cmdline
	serialno=`cat /proc/cmdline | grep -o "androidboot.serialno=[A-Za-z0-9]*" | cut -d "=" -f2`
	if [ "$serialno" == "" ]; then
		echo "serialno not found with cmdline, use default"
		serialno="12345678"
	fi
	echo "serialno:" $serialno

	msm_serial=`cat /sys/devices/soc0/serial_number`
	msm_serial_hex=`printf %08X $msm_serial`
	product_string="$socplatform-$soc_hwplatform _SN:$msm_serial_hex"

	# try to mount configfs, and create instance of USB functions if it goes through
	mount -t configfs none /sys/kernel/config
	if [ -d /sys/kernel/config/usb_gadget ]; then
		echo "Configuring the USB gadget using ConfigFS..."
		cd /sys/kernel/config/usb_gadget
		mkdir g1
		cd g1
		mkdir strings/0x409
		mkdir configs/c.1
		mkdir configs/c.1/strings/0x409
		mkdir functions/mass_storage.0
		mkdir functions/mass_storage.1
		mkdir functions/ffs.adb
		mkdir functions/diag.diag
		mkdir functions/diag.diag_mdm
		mkdir functions/cser.dun.0
		mkdir functions/cser.nmea.1
		#+quectel-20230410, add cser.dun.2 for modem AT port
		mkdir functions/cser.dun.2
		#-quectel-20230410, add cser.dun.2 for modem AT port
		mkdir functions/gser.0
		#johnson-20240827, add for recovery usbcfg
		mkdir functions/gser.1
		mkdir functions/rmnet_bam.rmnet
		mkdir functions/gsi.rmnet
		mkdir functions/gsi.rndis
		mkdir functions/gsi.ecm
		mkdir functions/gsi.mbim
		mkdir functions/gsi.rmnet.v2x
		mkdir functions/gsi.dpl
		mkdir functions/gsi.gps
		mkdir functions/ncm.0
		mkdir functions/qdss.qdss
		mkdir functions/qdss.qdss_mdm
		mkdir functions/qdss.qdss_sw
		mkdir functions/uac1.uac1
		mkdir functions/uac2.0
		mkdir functions/uvc.0
		mkdir functions/rndis.rndis
		mkdir functions/ipc.ipc
		mkdir functions/ecm.ecm
		mkdir functions/hid.usb0

		kernel_version=${KERNEL%%.*}
		if [ "$kernel_version" -ge "5" ]; then
			mkdir functions/ffs.diag
		fi

		if [ ! -s "/etc/adb_devid" ]
		then
			echo $serialno > /etc/adb_devid
			sync
		fi
		cat /etc/adb_devid > strings/0x409/serialnumber
		echo "QCOM" > strings/0x409/manufacturer
		echo "$product_string" > strings/0x409/product
		if [ -e functions/diag.diag/serial ]; then
			echo "$serialno" > functions/diag.diag/serial
		fi

		# correct SELinux labels on new /dev nodes
		if [ -x /sbin/restorecon ]
		then
			/sbin/restorecon /dev/at_usb0 /dev/at_usb1
		fi

		# Set the needed permissions for bind operation to happen from
		# adbd context as well
		chmod 0660 /sys/kernel/config/usb_gadget/g1/UDC
		chown root:adb /sys/kernel/config/usb_gadget/g1/UDC
		qdss_func="qdss"
		setprop sys.usb.configfs 1
		cd /
	fi

	if [ -d /sys/class/android_usb/android0 ]
	then
		echo "$product_string" > /sys/class/android_usb/android0/iProduct
	fi

	# mount functionfs
	mkdir -p /dev/usb-ffs/adb
	mount -o uid=2000,gid=2000 -t functionfs adb /dev/usb-ffs/adb

	if [ -e /sys/kernel/config/usb_gadget/g1/functions/ffs.diag ]; then
		mkdir -p /dev/ffs-diag
		mount -o uid=1000,gid=1000,no_disconnect=1,rmode=0550,fmode=0660 -t functionfs diag /dev/ffs-diag
	fi

	mount -o remount,gid=5,mode=620 /dev/pts

	case `source /sbin/usb/target` in
		*9x15* )
			# Nothing to do here for 9x15
			;;
		*9x25* )
			echo "Unbind EHCI HSIC host device driver"
			echo msm_hsic_host > /sys/bus/platform/drivers/msm_hsic_host/unbind
			;;
		*9x35* )
			echo "Unbind EHCI HSIC host device driver"
			echo f9a15000.hsic_host > /sys/bus/platform/drivers/msm_hsic_host/unbind
			echo 1 > /sys/devices/virtual/android_usb/android0/f_rndis_qc/max_pkt_per_xfer
			echo 3 > /sys/module/dwc3/parameters/bulk_ep_xfer_timeout_ms
			;;
		*8916* )
			echo 9091 > /etc/usb/boot_hsusb_comp
                        ;;
		*9640* )
			echo 16384 > /sys/module/g_android/parameters/rndis_dl_max_xfer_size
			;;
		*9650* )
			echo 16384 > /sys/module/g_android/parameters/gsi_in_rndis_aggr_size
			if [ -e /dev/mhi_ctrl ]; then
				echo 901F > /etc/usb/boot_hsusb_comp
			fi
			;;
		*sdx20* )
			echo Y > /sys/module/libcomposite/parameters/enable_l1_for_hs
			echo Y > /sys/module/dwc3/parameters/enable_dwc3_u1u2
			if [ -e /dev/mhi_ctrl ]; then
				echo 901F > /etc/usb/boot_hsusb_comp
			fi
			;;
		*sdxpoorwills* | *sdxprairie* )
			if [ -e /dev/mhi_ctrl ]; then
				echo 901F > /etc/usb/boot_hsusb_comp
			fi
			;;
		*8909* | *8053* | *8017* | *qcs605* | *qcs40x* | *sa2150p* | *qcs610* )
			echo 901D > /etc/usb/boot_hsusb_comp
		        ;;
		*sm8150* )
			echo 901D > /etc/usb/boot_hsusb_comp
			if [ -f /sys/bus/platform/devices/a600000.ssusb/mode ]; then
				default_mode=`cat /sys/bus/platform/devices/a600000.ssusb/mode`
				case "$default_mode" in
				    "none")
					echo peripheral > /sys/bus/platform/devices/a600000.ssusb/mode
				    ;;
				esac
			fi
		        ;;
		*sdxpinn* | *sdxkova* )
			adbd_params="0 y"
			qdss_func="qdss_sw"
			if [ -d /sys/class/usb_role/a600000.ssusb-role-switch ]; then
				USB_ROLE_SWITCH="Y"
			fi
			;;
	esac

	#
	# Initialize UVC conifguration.
	#
	if [ -d /sys/kernel/config/usb_gadget/g1/functions/uvc.0 ]; then
		cd /sys/kernel/config/usb_gadget/g1/functions/uvc.0

		echo 3072 > streaming_maxpacket
		echo 10 > streaming_maxburst
		mkdir control/header/h
		ln -s control/header/h control/class/fs/
		ln -s control/header/h control/class/ss

		mkdir -p streaming/uncompressed/u/360p
		echo -e "333333\n666666\n1000000\n5000000\n" > streaming/uncompressed/u/360p/dwFrameInterval
		echo 333333 > streaming/uncompressed/u/360p/dwDefaultFrameInterval

		mkdir -p streaming/uncompressed/u/720p
		echo 1280 > streaming/uncompressed/u/720p/wWidth
		echo 720 > streaming/uncompressed/u/720p/wHeight
		echo 29491200 > streaming/uncompressed/u/720p/dwMinBitRate
		echo 29491200 > streaming/uncompressed/u/720p/dwMaxBitRate
		echo 1843200 > streaming/uncompressed/u/720p/dwMaxVideoFrameBufferSize
		echo 333333 > streaming/uncompressed/u/720p/dwDefaultFrameInterval
		echo -e "333333\n666666\n1000000\n5000000\n" > streaming/uncompressed/u/720p/dwFrameInterval

		mkdir -p streaming/uncompressed/u/1080p
		echo 1920 > streaming/uncompressed/u/1080p/wWidth
		echo 1080 > streaming/uncompressed/u/1080p/wHeight
		echo 66355200 > streaming/uncompressed/u/1080p/dwMinBitRate
		echo 995328000 > streaming/uncompressed/u/1080p/dwMaxBitRate
		echo 4147200 > streaming/uncompressed/u/1080p/dwMaxVideoFrameBufferSize
		echo 333333 > streaming/uncompressed/u/1080p/dwDefaultFrameInterval
		echo -e "333333\n666666\n1000000\n5000000\n" > streaming/uncompressed/u/1080p/dwFrameInterval

		mkdir -p streaming/mjpeg/m/360p
		echo 640 > streaming/mjpeg/m/360p/wWidth
		echo 360 > streaming/mjpeg/m/360p/wHeight
		echo 460800   > streaming/mjpeg/m/360p/dwMaxVideoFrameBufferSize
		echo 18432000  > streaming/mjpeg/m/360p/dwMinBitRate
		echo 55296000 > streaming/mjpeg/m/360p/dwMaxBitRate
		echo -e "333333\n666666\n1000000\n5000000\n" > streaming/mjpeg/m/360p/dwFrameInterval
		echo 333333 > streaming/mjpeg/m/360p/dwDefaultFrameInterval

		mkdir -p streaming/mjpeg/m/720p
		echo 1280 > streaming/mjpeg/m/720p/wWidth
		echo 720 > streaming/mjpeg/m/720p/wHeight
		echo 29491200 > streaming/mjpeg/m/720p/dwMinBitRate
		echo 29491200 > streaming/mjpeg/m/720p/dwMaxBitRate
		echo 1843200 > streaming/mjpeg/m/720p/dwMaxVideoFrameBufferSize
		echo 333333 > streaming/mjpeg/m/720p/dwDefaultFrameInterval
		echo -e "333333\n666666\n1000000\n5000000\n" > streaming/mjpeg/m/720p/dwFrameInterval

		mkdir -p streaming/mjpeg/m/1080p
		echo 1920 > streaming/mjpeg/m/1080p/wWidth
		echo 1080 > streaming/mjpeg/m/1080p/wHeight
		echo 66355200 > streaming/mjpeg/m/1080p/dwMinBitRate
		echo 995328000 > streaming/mjpeg/m/1080p/dwMaxBitRate
		echo 4147200 > streaming/mjpeg/m/1080p/dwMaxVideoFrameBufferSize
		echo 333333 > streaming/mjpeg/m/1080p/dwDefaultFrameInterval
		echo -e "333333\n666666\n1000000\n5000000\n" > streaming/mjpeg/m/1080p/dwFrameInterval

		mkdir -p streaming/mjpeg/m/1440p
		echo 2560 > streaming/mjpeg/m/1440p/wWidth
		echo 1440 > streaming/mjpeg/m/1440p/wHeight
		echo 117964800 > streaming/mjpeg/m/1440p/dwMinBitRate
		echo 1769472000 > streaming/mjpeg/m/1440p/dwMaxBitRate
		echo 7372800 > streaming/mjpeg/m/1440p/dwMaxVideoFrameBufferSize
		echo 333333 > streaming/mjpeg/m/1440p/dwDefaultFrameInterval
		echo -e "333333\n" > streaming/mjpeg/m/1440p/dwFrameInterval

		mkdir -p streaming/mjpeg/m/2160p
		echo 3840 > streaming/mjpeg/m/2160p/wWidth
		echo 2160 > streaming/mjpeg/m/2160p/wHeight
		echo 265420800 > streaming/mjpeg/m/2160p/dwMinBitRate
		echo 3981312000 > streaming/mjpeg/m/2160p/dwMaxBitRate
		echo 16588800 > streaming/mjpeg/m/2160p/dwMaxVideoFrameBufferSize
		echo 333333 > streaming/mjpeg/m/2160p/dwDefaultFrameInterval
		echo -e "333333\n666666\n1000000\n5000000\n" > streaming/mjpeg/m/2160p/dwFrameInterval

		echo 0x04 > /sys/kernel/config/usb_gadget/g1/functions/uvc.0/streaming/mjpeg/m/bmaControls

		mkdir -p streaming/h264/h/960p
		echo 1920 > streaming/h264/h/960p/wWidth
		echo 960 > streaming/h264/h/960p/wHeight
		echo 40 > streaming/h264/h/960p/bLevelIDC
		echo -e "333667\n" > streaming/h264/h/960p/dwFrameInterval

		mkdir -p streaming/h264/h/1920p
		echo -e "333667\n" > streaming/h264/h/1920p/dwFrameInterval

		mkdir streaming/header/h
		ln -s streaming/uncompressed/u streaming/header/h
		ln -s streaming/mjpeg/m streaming/header/h
		ln -s streaming/h264/h streaming/header/h
		ln -s streaming/header/h streaming/class/fs/
		ln -s streaming/header/h streaming/class/hs/
		ln -s streaming/header/h streaming/class/ss/
		cd /
	fi

	# enable debug message
	if [ -f /sbin/usb/debuger/default_debug ];
	then
		rm -rf /sbin/usb/debuger/statusFile
		usb_debug -f on n /sbin/usb/debuger/default_debug
	fi

	# boot hsic composition:
	if [ -d /sys/class/android_usb/android1 ]
	then
		pid=`cat /etc/usb/boot_hsic_comp`
		# make 901D as default composition
                if [ -z "$pid" ]
		then
                    pid="901D"
		fi

		/sbin/usb/compositions/$pid y
		# let hsic compostion script run before starting hsusb
		sleep 1
	fi

	# boot hsusb composition:
	if [ -d /sys/class/android_usb/android0 ]
	then
		pid=`cat /etc/usb/boot_hsusb_comp`
		# put USB in LPM and exit with success if device is in mhi mode
		if [ "$pid" == "none" ]; then
			udcname=`ls -1 /sys/bus/platform/devices/ | grep -E 'ssusb|hsusb' | head -n 1`
			echo none > /sys/bus/platform/devices/${udcname}/mode
			exit 0
		fi
		# make 901D as default composition
                if [ -z "$pid" ]
		then
                    pid="901D"
		fi

		# check if more than 1 argument is passed
		if [ $# -gt 1 ]; then
			# if second argument is n, do nothing
			if [ $2 != 'n' ]; then
				/sbin/usb/compositions/$pid n $adbd_params $qdss_func
			fi
		else
			/sbin/usb/compositions/$pid n $adbd_params $qdss_func
		fi

		# targets with cable detection support on Modem might need
		# more time for UDC to come up. So schedule a bind operation
		# accordingly
		udcname=`ls -1 /sys/class/udc | head -n 1`
		if [ -z $udcname ] && [ "$USB_ROLE_SWITCH" == "Y" ]; then
			usb_bind &
		fi

		# Enable traces if user sets enable_dwc3_ftraces
		if [ -s /data/enable_dwc3_ftraces ]; then
			wait_for_debugfs &
		fi
	fi
	;;

  start)
	udcname=`ls -1 /sys/class/udc | head -n 1`
	if [ -d /sys/kernel/config/usb_gadget ]; then
		echo "Starting USB Android Gadget"
		rm -f /tmp/usb_bind_in_progress
		echo $udcname > /sys/kernel/config/usb_gadget/g1/UDC
	fi
	;;

  stop)
	if [ -d /sys/kernel/config/usb_gadget ]; then
		echo "Stopping USB Android Gadget"
		echo start > /tmp/usb_bind_in_progress
		echo none > /sys/kernel/config/usb_gadget/g1/UDC
	fi
        ;;

  restart)
	echo "Restarting USB Android Gadget"
	$0 stop
	$0 start
        ;;

  bind_udc)
	# On owrt targets we dont have usbd (due to memory constraints) nor udev,
	# hence use the hotplug.d framework to bind UDC after exiting host mode.
	# And unfortunately we were unable to detect UDC events in hotplug.d, so
	# the expectation is that USB2.0 roothub removal would indicate that we
	# are exiting host mode and entering device mode.

	timeout=0
	while :
	do
		if [ $timeout == 3 ]; then
			break
		fi

		udcname=`ls -1 /sys/class/udc | head -n 1`
		if [ ! -z $udcname ]; then
			# Dont do anything if its already binded
			checkudc=$(cat /sys/kernel/config/usb_gadget/g1/UDC)
			if [ -z $checkudc ]; then
				# ADBD might have gotten killed by the time we switch
				# back to device mode, so reload adbd service
				/etc/init.d/adbd.init restart
				sleep 1
				echo $udcname > /sys/kernel/config/usb_gadget/g1/UDC
			fi
			break;
		fi
		timeout=$(expr "$timeout" + 1)
		sleep 1
	done
        ;;
  *)
        echo "Usage usb { start | stop | restart}" >&2
        exit 1
        ;;
esac

