1#!/bin/bash 2# Copyright 2021 Google LLC 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16 17# List of options the script accepts. Trailing column means that the option 18# requires an argument. 19ARGUMENT_LIST=( 20 "help" 21 "product-id:" 22 "product-name:" 23 "host-mac:" 24 "bind-device:" 25 "dev-mac:" 26 "dev-type:" 27 "gadget-dir-name:" 28 "iface-name:" 29) 30 31print_usage() { 32 cat <<HELP 33$0 [OPTIONS] [stop|start] 34 Create USB Gadget Configuration 35 --product-id USB Product Id for the gadget. 36 --product-name Product name string (en) for the gadget. 37 --host-mac MAC address of the host part of the connection. Optional. 38 --dev-mac MAC address of the device (gadget) part of the connection. Optional. 39 --dev-type Type of gadget to instantiate. Default: "eem" 40 --bind-device Name of the device to bind, as listed in /sys/class/udc/ 41 --gadget-dir-name Optional base name for gadget directory. Default: iface-name 42 --iface-name name of the network interface. 43 --help Print this help and exit. 44HELP 45} 46 47gadget_start() { 48 # Always provide a basic network configuration 49 mkdir -p /run/systemd/network || return 50 cat >/run/systemd/network/+-bmc-"${IFACE_NAME}".network <<EOF 51[Match] 52Name=${IFACE_NAME} 53EOF 54 55 # Add the gbmcbr configuration if this is a relevant device 56 if (( ID_VENDOR == 0x18d1 && ID_PRODUCT == 0x22b )); then 57 cat >>/run/systemd/network/+-bmc-"${IFACE_NAME}".network <<EOF 58[Network] 59Bridge=gbmcbr 60[Bridge] 61Cost=85 62EOF 63 fi 64 65 # Ignore any failures due to systemd being unavailable at boot 66 networkctl reload || true 67 68 local gadget_dir="${CONFIGFS_HOME}/usb_gadget/${GADGET_DIR_NAME}" 69 mkdir -p "${gadget_dir}" || return 70 echo "${ID_VENDOR}" >"${gadget_dir}/idVendor" || return 71 echo "${ID_PRODUCT}" >"${gadget_dir}/idProduct" || return 72 73 local str_en_dir="${gadget_dir}/strings/0x409" 74 mkdir -p "${str_en_dir}" || return 75 echo "${STR_EN_VENDOR}" >"${str_en_dir}/manufacturer" || return 76 echo "${STR_EN_PRODUCT}" >"${str_en_dir}/product" || return 77 78 local config_dir="${gadget_dir}/configs/c.1" 79 mkdir -p "${config_dir}" || return 80 echo 100 > "${config_dir}/MaxPower" || return 81 mkdir -p "${config_dir}/strings/0x409" || return 82 echo "${DEV_TYPE^^}" > "${config_dir}/strings/0x409/configuration" || return 83 84 local func_dir="${gadget_dir}/functions/${DEV_TYPE}.${IFACE_NAME}" 85 mkdir -p "${func_dir}" || return 86 87 if [[ -n $HOST_MAC_ADDR ]]; then 88 echo "${HOST_MAC_ADDR}" >"${func_dir}"/host_addr || return 89 fi 90 91 if [[ -n $DEV_MAC_ADDR ]]; then 92 echo "${DEV_MAC_ADDR}" >"${func_dir}"/dev_addr || return 93 fi 94 95 ln -s "${func_dir}" "${config_dir}" || return 96 97 # This only works on kernel 5.12+, we have to ignore failures for now 98 echo "$IFACE_NAME" >"${func_dir}"/ifname || true 99 100 echo "${BIND_DEVICE}" >"${gadget_dir}"/UDC || return 101 # Try to reconfigure a few times in case we race with systemd-networkd 102 local start=$SECONDS 103 while (( SECONDS - start < 5 )); do 104 local ifname 105 ifname="$(<"${func_dir}"/ifname)" || return 106 [ "${IFACE_NAME}" = "$ifname" ] && break 107 ip link set dev "$ifname" down && \ 108 ip link set dev "$ifname" name "${IFACE_NAME}" && break 109 sleep 1 110 done 111 ip link set dev "$IFACE_NAME" up || return 112} 113 114gadget_stop() { 115 local gadget_dir="${CONFIGFS_HOME}/usb_gadget/${GADGET_DIR_NAME}" 116 rm -f "${gadget_dir}/configs/c.1/${DEV_TYPE}.${IFACE_NAME}" 117 rmdir "${gadget_dir}/functions/${DEV_TYPE}.${IFACE_NAME}" \ 118 "${gadget_dir}/configs/c.1/strings/0x409" \ 119 "${gadget_dir}/configs/c.1" \ 120 "${gadget_dir}/strings/0x409" \ 121 "${gadget_dir}" || true 122 123 rm -f /run/systemd/network/+-bmc-"${IFACE_NAME}".network 124 networkctl reload || true 125} 126 127opts="$(getopt \ 128 --longoptions "$(printf "%s," "${ARGUMENT_LIST[@]}")" \ 129 --name "$(basename "$0")" \ 130 --options "" \ 131 -- "$@" 132)" 133 134eval set -- "$opts" 135 136CONFIGFS_HOME=${CONFIGFS_HOME:-/sys/kernel/config} 137ID_VENDOR="0x18d1" # Google 138ID_PRODUCT="" 139STR_EN_VENDOR="Google" 140STR_EN_PRODUCT="" 141DEV_MAC_ADDR="" 142DEV_TYPE="eem" 143HOST_MAC_ADDR="" 144BIND_DEVICE="" 145ACTION="start" 146GADGET_DIR_NAME="" 147IFACE_NAME="" 148while [[ $# -gt 0 ]]; do 149 case "$1" in 150 --product-id) 151 ID_PRODUCT=$2 152 shift 2 153 ;; 154 --product-name) 155 STR_EN_PRODUCT=$2 156 shift 2 157 ;; 158 --host-mac) 159 HOST_MAC_ADDR=$2 160 shift 2 161 ;; 162 --dev-mac) 163 DEV_MAC_ADDR=$2 164 shift 2 165 ;; 166 --dev-type) 167 DEV_TYPE=$2 168 shift 2 169 ;; 170 --bind-device) 171 BIND_DEVICE=$2 172 shift 2 173 ;; 174 --gadget-dir-name) 175 GADGET_DIR_NAME=$2 176 shift 2 177 ;; 178 --iface-name) 179 IFACE_NAME=$2 180 shift 2 181 ;; 182 --help) 183 print_usage 184 exit 0 185 ;; 186 start) 187 ACTION="start" 188 shift 1 189 break 190 ;; 191 stop) 192 ACTION="stop" 193 shift 1 194 break 195 ;; 196 --) 197 shift 1 198 ;; 199 *) 200 break 201 ;; 202 esac 203done 204 205if [ -z "$GADGET_DIR_NAME" ]; then 206 GADGET_DIR_NAME="$IFACE_NAME" 207fi 208 209if [[ $ACTION == "stop" ]]; then 210 gadget_stop 211else 212 if [ -z "$ID_PRODUCT" ]; then 213 echo "Product ID is missing" >&2 214 exit 1 215 fi 216 217 if [ -z "$IFACE_NAME" ]; then 218 echo "Interface name is missing" >&2 219 exit 1 220 fi 221 222 if [ -z "$BIND_DEVICE" ]; then 223 echo "Bind device is missing" >&2 224 exit 1 225 fi 226 227 rc=0 228 gadget_start || rc=$? 229 (( rc == 0 )) || gadget_stop || true 230 exit $rc 231fi 232