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