#!/bin/bash
# $1: -d - tear down

#FUNCS=("acm.usb0" "ncm.usb0" "acm.usb1")
#FUNCS=("mass_storage.usb0")
#FUNCS=("hid.usb0")
FUNCS=("uvc.usb0")
#FUNCS=("uvc.usb0" "hid.usb0")
#FUNCS=("SourceSink.usb0")

FUNCS=("rndis.usb0" "ecm.usb0" "mass_storage.usb0")
FUNCS=("acm.usb0" "ecm.usb0" "mass_storage.usb0")
FUNCS=("ecm.usb0" "ncm.usb0" "mass_storage.usb0")

CFS=/sys/kernel/config/usb_gadget
VID="0x1d6d"
PID="0x0104"
LANG="0x409"

RNDIS_DEV_ADDR="12:22:33:44:55:66"
RNDIS_HOST_ADDR="12:22:33:44:55:65"
ECM_DEV_ADDR="12:22:33:44:55:68"
ECM_HOST_ADDR="12:22:33:44:55:67"

hid_report="\\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"



init()
{
	zcat /proc/config.gz | grep 'CONFIGFS_FS=' > /dev/null || exit 1
	lsmod | grep libcomposite > /dev/null || modprobe libcomposite || exit 2
	mount | grep configfs > /devnull || 
		mount -t configfs none $(dirname $CFS) || exit 3
}

create_gadget()
{
	[ ! -d ${CFS}/g1 ] || exit 5
	mkdir ${CFS}/g1 && cd ${CFS}/g1 || exit 6
	echo "$VID" > idVendor
	echo "$PID" > idProduct

	mkdir strings/$LANG
	echo "0123456789" > strings/$LANG/serialnumber
	echo "Foo Inc" > strings/$LANG/manufacturer
	echo "Bar gadget" > strings/$LANG/product
}

# configuraton naming: configs/<name>.<number>
create_config()
{
	[ -d ${CFS}/g1 ] && cd ${CFS}/g1 || exit 5
	mkdir configs/c.1
	mkdir configs/c.1/strings/$LANG
	echo "conf1" > configs/c.1/strings/$LANG/configuration
}

# $1 - function name
# function naming: functions/<name>.<instance_name>
create_func_single()
{
	local _func=$1

	mkdir functions/${_func} || return
	case $_func in
	"mass_storage."*)
 		[ -f /dev/shm/gmsc.file ] || 
			dd if=/dev/zero of=/dev/shm/gmsc.file bs=1M count=32
		echo /dev/shm/gmsc.file > functions/${_func}/lun.0/file
		;;
	"hid."*)
		echo 1 > functions/${_func}/protocol
		echo 1 > functions/${_func}/subclass
		echo 8 > functions/${_func}/report_length
		echo -ne $hid_report > functions/${_func}/report_desc
		;;
	"uvc."*)
		mkdir functions/${_func}/control/header/h
		cd functions/${_func}/control
		ln -s header/h class/fs
		ln -s header/h class/ss
		cd ${CFS}/g1

		_w=640
		_h=360
		_fps=30

		mkdir -p functions/${_func}/streaming/uncompressed/u/${_h}p
		cd functions/${_func}/streaming/uncompressed/u/${_h}p
		echo $_w > wWidth
		echo $_h > wHeight
		echo $((_h * _w * 2 * _fps)) > dwMaxBitRate
		echo $((_h * _w * 2 * _fps)) > dwMinBitRate
		echo $((_h * _w * 2)) > dwMaxVideoFrameBufferSize
		echo 333333 > dwFrameInterval
		echo 333333 > dwDefaultFrameInterval
#666666
#1000000
#5000000
		cd ${CFS}/g1
		mkdir functions/${_func}/streaming/header/h
		cd functions/${_func}/streaming/header/h
		ln -s ../../uncompressed/u
		cd ../../class/fs
		ln -s ../../header/h
		cd ../../class/hs
		ln -s ../../header/h
		cd ../../class/ss
		ln -s ../../header/h
		cd ${CFS}/g1

		echo 1024 > functions/${_func}/streaming_maxpacket
		;;
	"rndis."*)
#		echo $RNDIS_HOST_ADDR > functions/${_func}/host_addr
#		echo $RNDIS_DEV_ADDR > functions/${_func}/dev_addr

		# match MS built-in RNDIS driver
		echo EF > functions/${_func}/class
		echo 04 > functions/${_func}/subclass
		echo 01 > functions/${_func}/protocol
		;;
	"ecm."*)
#		echo $ECM_HOST_ADDR > functions/${_func}/host_addr
#		echo $ECM_DEV_ADDR > functions/${_func}/dev_addr
		;;
	esac
	ln -s functions/${_func} configs/c.1
}

activate()
{
	local _udc

	_udc=$(ls /sys/class/udc/)
	# TODO check $_udc
	echo "$_udc" > UDC
}

teardown()
{
	# TODO: test hid & uvc
	local _ent

	[ -d ${CFS}/g1 ] && cd ${CFS}/g1 || exit 5
	echo "" > UDC
	for _ent in $(ls configs/c.1/); do
		[[ "$_ent" != "MaxPower" ]] || continue
		[[ "$_ent" != "bmAttributes" ]] || continue
		[[ "$_ent" != "strings" ]] || continue
		
		rm -f configs/c.1/$_ent
	done

	for _ent in $(ls functions/); do
		case $_ent in
		"uvc."*)
			rm -rf functions/$_ent/streaming 2>/dev/null
			;;
		esac

		rm -rf functions/$_ent 2>/dev/null
	done

	rmdir configs/c.1/strings/$LANG
	rmdir configs/c.1
	rmdir strings/$LANG
	cd .. && rmdir g1
	echo "toredown"
}

### MAIN ###

[ "$1" != "-d" ] || { teardown; exit 0; }

init
create_gadget
create_config
for func in ${FUNCS[*]}; do
	echo "creating $func ..."
	create_func_single $func
done
activate
echo created

