xref: /OK3568_Linux_fs/debian/overlay/usr/bin/usbdevice (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1#!/bin/sh -e
2#
3# Usage:
4# usbdevice [start|update|stop]
5#
6# Hookable stages:
7# usb_<pre|post>_<init|prepare|start|stop|restart>_hook
8# <usb function>_<pre|post>_<prepare|start|stop>_hook
9#
10# Example hook:
11# root@RK3588:/# more /etc/usbdevice.d/usb_init.sh
12# #!/bin/sh
13# usb_pre_init_hook()
14# {
15#	echo "usb pre-init hook"
16# }
17#
18# root@RK3588:/# more /etc/usbdevice.d/uvc.sh
19# #!/bin/sh
20# UVC_INSTANCES="uvc.gs1 uvc.gs2"
21
22# Uncomment below to see more logs
23# set -x
24
25# Load default env variables from profiles
26. /etc/profile
27
28LOG_FILE=/tmp/usbdevice.log
29USB_FUNCS_FILE=/tmp/.usbdevice
30
31alias usb_enable='touch $USB_FUNCS_FILE'
32alias usb_disable='rm -f $USB_FUNCS_FILE'
33alias usb_is_enabled='[ -f $USB_FUNCS_FILE ]'
34alias usb_set_started='echo $USB_FUNCS > $USB_FUNCS_FILE'
35usb_get_started()
36{
37	usb_is_enabled || return 0
38	cat $USB_FUNCS_FILE
39}
40
41CONFIGFS_DIR=/sys/kernel/config
42USB_GROUP=rockchip
43USB_STRINGS_ATTR=strings/0x409
44USB_GADGET_DIR=$CONFIGFS_DIR/usb_gadget/$USB_GROUP
45USB_GADGET_STRINGS_DIR=$USB_GADGET_DIR/$USB_STRINGS_ATTR
46USB_FUNCTIONS_DIR=$USB_GADGET_DIR/functions
47USB_CONFIGS_DIR=$USB_GADGET_DIR/configs/b.1
48USB_CONFIGS_STRINGS_DIR=$USB_CONFIGS_DIR/$USB_STRINGS_ATTR
49
50# Make sure that we own this session (pid equals sid)
51if ! ps x -o pid,sid | grep -wq "$$$"; then
52	setsid $0 $@
53	exit $?
54fi
55
56# ---- helper functions
57usb_msg()
58{
59	logger -t "$(basename "$0")" "[$$]: $@"
60	echo "[$(date +"%F %T")] $@"
61}
62
63usb_pid()
64{
65	case "$(echo $USB_FUNCS | xargs -n 1 | sort | paste -sd'-')" in
66		ums)		echo 0x0000;;
67		mtp)		echo 0x0001;;
68		uvc)		echo 0x0005;;
69		adb)		echo 0x0006;;
70		adb-mtp)	echo 0x0011;;
71		adb-ums)	echo 0x0018;;
72		adb-uvc)	echo 0x0015;;
73		ntb-uvc)	echo 0x0017;;
74		acm)		echo 0x1005;;
75		*)		echo 0x0019;;
76	esac
77}
78
79usb_instances()
80{
81	for func in $@; do
82		VAR=$(echo $func | tr 'a-z' 'A-Z')_INSTANCES
83		eval echo "\${$VAR:-$func.gs0}"
84	done
85}
86
87usb_run_stage()
88{
89	for f in $1_pre_$2_hook $1_$2 $1_post_$2_hook; do
90		type $f >/dev/null 2>/dev/null || continue
91
92		usb_msg "Run stage: $f"
93		eval $f || break
94	done
95}
96
97usb_wait_files()
98{
99	for i in `seq 200`;do
100		fuser -s $@ 2>/dev/null && break
101		sleep .01
102	done
103}
104
105usb_release_files()
106{
107	for i in `seq 200`;do
108		fuser -s -k $@ 2>/dev/null || break
109		sleep .01
110	done
111}
112
113# usage: usb_mount <src> <mountpoint> <options>
114usb_mount()
115{
116	mkdir -p $2
117	mountpoint -q $2 || mount $@
118}
119
120usb_umount()
121{
122	mountpoint -q $1 || return 0
123	usb_release_files -m $1
124	umount $1
125}
126
127usb_symlink()
128{
129	mkdir -p $1
130	[ -e $2 ] || ln -s $1 $2
131}
132
133usb_try_symlink()
134{
135	usb_symlink $@ &>/dev/null || true
136}
137
138usb_write()
139{
140	if echo "x$1" | grep -q "^x-"; then
141		OPTS=$1
142		shift
143	fi
144
145	FILE=$1
146	shift
147
148	if [ -r $FILE ] && [ "$(cat $FILE)" = "$@" ]; then
149		return 0
150	fi
151
152	echo $OPTS "$@" > $FILE
153}
154
155usb_try_write()
156{
157	usb_write $@ &>/dev/null || true
158}
159
160usb_start_daemon()
161{
162	NAME=$(echo $1 | sed "s#^[^ ]*/\([^ ]*\).*#\1#")
163	TAG_FILE=/tmp/.usb_$NAME
164
165	# Enable spawn
166	touch $TAG_FILE
167
168	# Already started
169	[ -z "$(usb_get_started)" ] || return 0
170
171	# Start and spawn background daemon
172	{
173		exec 3<&-
174
175		cd /
176		while usb_is_enabled; do
177			# Don't spawn after stopped
178			[ ! -f $TAG_FILE ] ||
179				start-stop-daemon -Sqx $@ || true
180			sleep .5
181		done
182	}&
183}
184
185usb_stop_daemon()
186{
187	NAME=$(echo $1 | sed "s#^[^ ]*/\([^ ]*\).*#\1#")
188	TAG_FILE=/tmp/.usb_$NAME
189
190	# Stop and disable spawn
191	rm -f $TAG_FILE
192	start-stop-daemon -Kqox $@
193}
194
195usb_load_config()
196{
197	USB_CONFIG_FILE=$(find /etc/ -name .usb_config | head -n 1)
198	[ -n "$USB_CONFIG_FILE" -a -r "$USB_CONFIG_FILE" ] || return 0
199
200	ums_parse()
201	{
202		grep "\<$1=" "$USB_CONFIG_FILE" | cut -d'=' -f2
203	}
204	UMS_FILE="$(ums_parse ums_block)"
205	UMS_SIZE=$(ums_parse ums_block_size || echo 0)M
206	UMS_FSTYPE=$(ums_parse ums_block_type)
207	UMS_MOUNT=$([ "$(ums_parse ums_block_auto_mount)" != on ]; echo $?)
208	UMS_RO=$([ "$(ums_parse ums_block_ro)" != on ]; echo $?)
209
210	USB_FUNCS="$(sed -n "s/usb_\(.*\)_en/\1/p" "$USB_CONFIG_FILE" | xargs)"
211}
212
213# ---- adb
214ADB_INSTANCES=${ADB_INSTANCES:-ffs.adb}
215
216adb_prepare()
217{
218	usb_mount adb /dev/usb-ffs/adb -o uid=2000,gid=2000 -t functionfs
219	usb_start_daemon /usr/bin/adbd
220	usb_wait_files -m /dev/usb-ffs/adb
221}
222
223adb_stop()
224{
225	usb_stop_daemon /usr/bin/adbd
226}
227
228# ---- ntb
229NTB_INSTANCES=${NTB_INSTANCES:-ffs.ntb}
230
231ntb_prepare()
232{
233	usb_mount ntb /dev/usb-ffs/ntb -o uid=2000,gid=2000 -t functionfs
234}
235
236# ---- uac1
237uac1_prepare()
238{
239	for f in $(find . -name "*_feature_unit"); do
240		echo 1 >$f
241	done
242}
243
244# ---- uac2
245uac2_prepare()
246{
247	uac1_prepare
248}
249
250# ---- mtp
251mtp_prepare()
252{
253	echo "MTP" > os_desc/interface.MTP/compatible_id
254	echo 1 > $USB_GADGET_DIR/os_desc/use
255}
256
257mtp_start()
258{
259	usb_start_daemon /usr/bin/mtp-server
260	usb_wait_files /dev/mtp_usb
261}
262
263mtp_stop()
264{
265	usb_stop_daemon /usr/bin/mtp-server
266	usb_release_files /dev/mtp_usb
267
268	echo 0 > $USB_GADGET_DIR/os_desc/use
269}
270
271# ---- acm
272ACM_INSTANCES=${ACM_INSTANCES:-acm.gs6}
273
274# ---- rndis
275# Nothing special
276
277# ---- uvc
278UVC_INSTANCES=${UVC_INSTANCES:-uvc.gs6}
279
280uvc_add_yuyv()
281{
282	WIDTH=$(echo $1 | cut -d'x' -f1)
283	HEIGHT=$(echo $1 | cut -d'x' -f2)
284	DIR=${HEIGHT}p
285
286	[ ! -d $DIR ] || return 0
287
288	mkdir -p $DIR
289	echo $WIDTH > $DIR/wWidth
290	echo $HEIGHT > $DIR/wHeight
291	echo 333333 > $DIR/dwDefaultFrameInterval
292	echo $((WIDTH * HEIGHT * 20)) > $DIR/dwMinBitRate
293	echo $((WIDTH * HEIGHT * 20)) > $DIR/dwMaxBitRate
294	echo $((WIDTH * HEIGHT * 2)) > $DIR/dwMaxVideoFrameBufferSize
295	echo -e "333333\n666666\n1000000\n2000000" > $DIR/dwFrameInterval
296}
297
298uvc_add_mjpeg()
299{
300	WIDTH=$(echo $1 | cut -d'x' -f1)
301	HEIGHT=$(echo $1 | cut -d'x' -f2)
302	DIR=${HEIGHT}p
303
304	[ ! -d $DIR ] || return 0
305
306	mkdir -p $DIR
307	echo $WIDTH > $DIR/wWidth
308	echo $HEIGHT > $DIR/wHeight
309	echo 333333 > $DIR/dwDefaultFrameInterval
310	echo $((WIDTH * HEIGHT * 20)) > $DIR/dwMinBitRate
311	echo $((WIDTH * HEIGHT * 20)) > $DIR/dwMaxBitRate
312	echo $((WIDTH * HEIGHT * 2)) > $DIR/dwMaxVideoFrameBufferSize
313	echo -e "333333\n666666\n1000000\n2000000" > $DIR/dwFrameInterval
314}
315
316uvc_add_h264()
317{
318	WIDTH=$(echo $1 | cut -d'x' -f1)
319	HEIGHT=$(echo $1 | cut -d'x' -f2)
320	DIR=${HEIGHT}p
321
322	[ ! -d $DIR ] || return 0
323
324	mkdir -p $DIR
325	echo $WIDTH > $DIR/wWidth
326	echo $HEIGHT > $DIR/wHeight
327	echo 333333 > $DIR/dwDefaultFrameInterval
328	echo $((WIDTH * HEIGHT * 10)) > $DIR/dwMinBitRate
329	echo $((WIDTH * HEIGHT * 10)) > $DIR/dwMaxBitRate
330	echo -e "333333\n666666\n1000000\n2000000" > $DIR/dwFrameInterval
331}
332
333uvc_support_resolutions()
334{
335	case ${1:-yuyv} in
336		yuyv)	echo "640x480 1280x720";;
337		mjpeg)	echo "640x480 1280x720 1920x1080 2560x1440 2592x1944";;
338		h264)	echo "640x480 1280x720 1920x1080";;
339	esac
340}
341
342uvc_prepare()
343{
344	UVC_DIR=$(pwd)
345
346	usb_symlink $UVC_DIR/control/header/h $UVC_DIR/control/class/fs/h
347	usb_symlink $UVC_DIR/control/header/h $UVC_DIR/control/class/ss/h
348
349	usb_symlink $UVC_DIR/streaming/header/h $UVC_DIR/streaming/class/fs/h
350	usb_symlink $UVC_DIR/streaming/header/h $UVC_DIR/streaming/class/hs/h
351	usb_symlink $UVC_DIR/streaming/header/h $UVC_DIR/streaming/class/ss/h
352
353	UVC_YUYV_RES=$(uvc_support_resolutions yuyv)
354	if [ -n "$UVC_YUYV_RES" ]; then
355		usb_try_symlink $UVC_DIR/streaming/uncompressed/u \
356			$UVC_DIR/streaming/header/h/u
357		cd $UVC_DIR/streaming/uncompressed/u
358
359		for res in $UVC_YUYV_RES; do
360			uvc_add_yuyv $res
361		done
362	fi
363
364	UVC_MJPEG_RES=$(uvc_support_resolutions mjpeg)
365	if [ -n "$UVC_MJPEG_RES" ]; then
366		usb_try_symlink $UVC_DIR/streaming/mjpeg/m \
367			$UVC_DIR/streaming/header/h/m
368		cd $UVC_DIR/streaming/mjpeg/m
369
370		for res in $UVC_MJPEG_RES; do
371			uvc_add_mjpeg $res
372		done
373	fi
374
375	UVC_H264_RES=$(uvc_support_resolutions h264)
376	if [ -n "$UVC_H264_RES" ]; then
377		usb_try_symlink $UVC_DIR/streaming/framebased/f \
378			$UVC_DIR/streaming/header/h/f
379		cd $UVC_DIR/streaming/framebased/f
380
381		for res in $UVC_H264_RES; do
382			uvc_add_h264 $res
383		done
384
385		usb_try_write -ne guidFormat "\\x48\\x32\\x36\\x34\\x00\\x00\\x10\\x00\\x80\\x00\\x00\\xaa\\x00\\x38\\x9b\\x71"
386	fi
387}
388
389# TODO: Start UVC daemon in uvc_start
390# TODO: Stop UVC daemon in uvc_stop
391
392# ---- hid
393HID_INSTANCES=${HID_INSTANCES:-hid.usb0}
394
395hid_prepare()
396{
397	echo 1 > protocol
398	echo 1 > subclass
399	echo 8 > report_length
400	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" \
401		> report_desc
402}
403
404# ---- ums
405UMS_INSTANCES=${UMS_INSTANCES:-mass_storage.0}
406
407ums_prepare()
408{
409	if [ ! -f "$UMS_FILE" ]; then
410		usb_msg "Formating $UMS_FILE($UMS_SIZE) to $UMS_FSTYPE"
411		truncate -s $UMS_SIZE "$UMS_FILE"
412		mkfs.$UMS_FSTYPE "$UMS_FILE" || \
413			usb_msg "Failed to format $UMS_FILE to $UMS_FSTYPE"
414	fi
415}
416
417ums_stop()
418{
419	echo > lun.0/file
420	usb_umount "$UMS_MOUNTPOINT"
421
422	[ "$UMS_MOUNT" -eq 1 ] || return 0
423
424	# Try auto fstype firstly
425	usb_mount "$UMS_FILE" "$UMS_MOUNTPOINT" -o sync 2>/dev/null || \
426		usb_mount "$UMS_FILE" "$UMS_MOUNTPOINT" -o sync -t $UMS_FSTYPE
427}
428
429ums_start()
430{
431	case "$USB_STATE" in
432		CONFIGURED)
433			if [ "$(cat lun.0/ro)" != "$UMS_RO" ]; then
434				echo > lun.0/file
435				echo $UMS_RO > lun.0/ro
436			fi
437
438			if ! grep -wq "$UMS_FILE" lun.0/file; then
439				usb_umount "$UMS_MOUNTPOINT"
440				echo "$UMS_FILE" > lun.0/file
441			fi
442			;;
443		DISCONNECTED)
444			ums_stop
445			;;
446	esac
447}
448
449# ---- global
450usb_init()
451{
452	usb_msg "Initializing"
453
454	echo 0x2207 > idVendor
455	echo 0x0310 > bcdDevice
456	echo 0x0200 > bcdUSB
457
458	mkdir -p $USB_GADGET_STRINGS_DIR
459	SERIAL=$(grep Serial /proc/cpuinfo | cut -d':' -f2)
460	echo ${SERIAL:-0123456789ABCDEF} > $USB_GADGET_STRINGS_DIR/serialnumber
461	echo $USB_GROUP  > $USB_GADGET_STRINGS_DIR/manufacturer
462	echo "rk3xxx"  > $USB_GADGET_STRINGS_DIR/product
463
464	mkdir -p $USB_CONFIGS_DIR
465	echo 500 > $USB_CONFIGS_DIR/MaxPower
466
467	echo 0x1 > os_desc/b_vendor_code
468	echo MSFT100 > os_desc/qw_sign
469	ln -s $USB_CONFIGS_DIR os_desc/
470
471	mkdir -p $USB_CONFIGS_STRINGS_DIR
472}
473
474usb_funcs_grep()
475{
476	echo $USB_FUNCS | xargs -n 1 | sort | uniq | grep $@ || true
477}
478
479usb_funcs_sort()
480{
481	{
482		for func in $@; do
483			usb_funcs_grep -E $func
484		done
485		usb_funcs_grep -vE $(echo $@ | tr ' ' '|')
486	} | uniq | xargs
487}
488
489usb_prepare()
490{
491	usb_load_config
492
493	# Allow function/variable overriding
494	[ -d /etc/usbdevice.d ] && . /etc/usbdevice.d/*
495
496	UMS_FILE=${UMS_FILE:-/userdata/ums_shared.img}
497	UMS_SIZE=${UMS_SIZE:-256M}
498	UMS_FSTYPE=${UMS_FSTYPE:-vfat}
499	UMS_MOUNT=${UMS_MOUNT:-0}
500	UMS_MOUNTPOINT="${UMS_MOUNTPOINT:-/mnt/ums}"
501	UMS_RO=${UMS_RO:-0}
502	USB_FUNCS=${USB_FUNCS:-adb}
503
504	# Orders required by kernel
505	USB_FUNCS="$(usb_funcs_sort rndis uac uvc adb ntb ums mtp acm)"
506	USB_CONFIG="$(echo "$USB_FUNCS" | tr ' ' '_')"
507
508	if [ ! -d $USB_GADGET_DIR ]; then
509		mountpoint -q $CONFIGFS_DIR || \
510			mount -t configfs none $CONFIGFS_DIR
511
512		mkdir -p $USB_GADGET_DIR
513		cd $USB_GADGET_DIR
514
515		# Global initialize
516		usb_run_stage usb init
517	fi
518
519	USB_STATE=$(cat /sys/class/android_usb/android0/state)
520	USB_UDC=$(ls /sys/class/udc/ | head -n 1)
521
522	# Parse started USB functions
523	OLD_FUNCS=$(usb_get_started)
524
525	# Stop old USB functions when USB functions changed
526	if [ -n "$OLD_FUNCS" ] && [ "$OLD_FUNCS" != "$USB_FUNCS" ]; then
527		usb_msg "Functions changed $OLD_FUNCS -> $USB_FUNCS"
528		usb_stop
529	fi
530
531	# Update USB PID
532	usb_pid > $USB_GADGET_DIR/idProduct
533}
534
535usb_start()
536{
537	usb_msg "Starting functions: $USB_FUNCS"
538
539	echo "$USB_CONFIG" > $USB_CONFIGS_STRINGS_DIR/configuration
540
541	for func in $USB_FUNCS; do
542		for instance in $(usb_instances $func); do
543			usb_msg "Preparing instance: $instance"
544
545			if ! mkdir -p $USB_FUNCTIONS_DIR/$instance 2>/dev/null; then
546				usb_msg "Failed to create instance: $instance"
547				continue
548			fi
549
550			cd $USB_FUNCTIONS_DIR/$instance &>/dev/null || continue
551
552			usb_run_stage $func prepare
553
554			# Make symlink after prepared (required by UVC)
555			usb_symlink $USB_FUNCTIONS_DIR/$instance \
556				$USB_CONFIGS_DIR/f-$instance
557		done
558	done
559
560	usb_write $USB_GADGET_DIR/UDC $USB_UDC
561
562	for func in $USB_FUNCS; do
563		for instance in $(usb_instances $func); do
564			cd $USB_FUNCTIONS_DIR/$instance &>/dev/null || continue
565
566			usb_msg "Starting instance: $instance"
567			usb_run_stage $func start
568		done
569	done
570
571	# Store started functions
572	usb_set_started
573}
574
575usb_stop()
576{
577	if [ -n "$OLD_FUNCS" ]; then
578		usb_msg "Stopping functions: $OLD_FUNCS"
579	fi
580
581	usb_write $USB_GADGET_DIR/UDC ""
582
583	for func in $USB_FUNCS; do
584		for instance in $(usb_instances $func); do
585			cd $USB_FUNCTIONS_DIR/$instance &>/dev/null || continue
586
587			usb_msg "Stopping instance: $instance"
588			usb_run_stage $func stop
589		done
590	done
591
592	rm -f $USB_CONFIGS_DIR/f-*
593
594	# Clear functions to avoid stopping them again
595	unset OLD_FUNCS
596}
597
598usb_restart()
599{
600	usb_run_stage usb stop
601	usb_run_stage usb start
602}
603
604ACTION=${1:-update}
605if [ "$ACTION" = update ]; then
606	usb_is_enabled || exit 0
607fi
608
609# Lock it
610exec 3<$0
611flock -x 3
612
613echo "Starting $0 ${ACTION}, log saved to $LOG_FILE"
614
615# Redirect outputs to log file
616exec >>$LOG_FILE 2>&1
617
618usb_msg "Handling ${ACTION} request"
619
620usb_run_stage usb prepare
621
622case "$ACTION" in
623	start|update)
624		usb_enable
625		usb_run_stage usb start
626		;;
627	stop)
628		usb_disable
629		usb_run_stage usb stop
630		;;
631	restart)
632		usb_enable
633		usb_run_stage usb restart
634		;;
635	*)
636		echo "Usage: usbdevice [start|stop|restart|update]" >&2
637		;;
638esac
639
640usb_msg "Done $ACTION request"
641echo
642
643# Unlock it
644flock -u 3
645