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