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