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