Initial commit

This commit is contained in:
Your Name
2026-04-23 17:07:55 +08:00
commit b7e39e063b
16725 changed files with 1625565 additions and 0 deletions
@@ -0,0 +1,31 @@
divert(-1)
define(`HOST_MAC_ARG', `ifelse($1, `invalid', `',
ifelse($1, `', `',
` --host-mac "$1"'))')
define(`DEV_MAC_ARG', `ifelse($1, `invalid', `',
ifelse($1, `', `',
` --dev-mac "$1"'))')
divert(0)dnl
dnl
[Unit]
Description=USB Gadget
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=M_SCRIPT_INSTALL_DIR/usb_network.sh \
--product-id "M_BMC_USB_PRODUCT_ID" \
--product-name "M_BMC_USB_PRODUCT_NAME" \
--dev-type "M_BMC_USB_TYPE" \
HOST_MAC_ARG(M_BMC_USB_HOST_MAC) \
DEV_MAC_ARG(M_BMC_USB_DEV_MAC) \
--iface-name "M_BMC_USB_IFACE" \
--bind-device "M_BMC_USB_BIND_DEV"
ExecStop=M_SCRIPT_INSTALL_DIR/usb_network.sh stop \
--dev-type "M_BMC_USB_TYPE" \
--iface-name "M_BMC_USB_IFACE"
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,231 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# List of options the script accepts. Trailing column means that the option
# requires an argument.
ARGUMENT_LIST=(
"help"
"product-id:"
"product-name:"
"host-mac:"
"bind-device:"
"dev-mac:"
"dev-type:"
"gadget-dir-name:"
"iface-name:"
)
print_usage() {
cat <<HELP
$0 [OPTIONS] [stop|start]
Create USB Gadget Configuration
--product-id USB Product Id for the gadget.
--product-name Product name string (en) for the gadget.
--host-mac MAC address of the host part of the connection. Optional.
--dev-mac MAC address of the device (gadget) part of the connection. Optional.
--dev-type Type of gadget to instantiate. Default: "eem"
--bind-device Name of the device to bind, as listed in /sys/class/udc/
--gadget-dir-name Optional base name for gadget directory. Default: iface-name
--iface-name name of the network interface.
--help Print this help and exit.
HELP
}
gadget_start() {
# Always provide a basic network configuration
mkdir -p /run/systemd/network || return
cat >/run/systemd/network/+-bmc-"${IFACE_NAME}".network <<EOF
[Match]
Name=${IFACE_NAME}
EOF
# Add the gbmcbr configuration if this is a relevant device
if (( ID_VENDOR == 0x18d1 && ID_PRODUCT == 0x22b )); then
cat >>/run/systemd/network/+-bmc-"${IFACE_NAME}".network <<EOF
[Network]
Bridge=gbmcbr
[Bridge]
Cost=85
EOF
fi
# Ignore any failures due to systemd being unavailable at boot
networkctl reload || true
local gadget_dir="${CONFIGFS_HOME}/usb_gadget/${GADGET_DIR_NAME}"
mkdir -p "${gadget_dir}" || return
echo "${ID_VENDOR}" >"${gadget_dir}/idVendor" || return
echo "${ID_PRODUCT}" >"${gadget_dir}/idProduct" || return
local str_en_dir="${gadget_dir}/strings/0x409"
mkdir -p "${str_en_dir}" || return
echo "${STR_EN_VENDOR}" >"${str_en_dir}/manufacturer" || return
echo "${STR_EN_PRODUCT}" >"${str_en_dir}/product" || return
local config_dir="${gadget_dir}/configs/c.1"
mkdir -p "${config_dir}" || return
echo 100 > "${config_dir}/MaxPower" || return
mkdir -p "${config_dir}/strings/0x409" || return
echo "${DEV_TYPE^^}" > "${config_dir}/strings/0x409/configuration" || return
local func_dir="${gadget_dir}/functions/${DEV_TYPE}.${IFACE_NAME}"
mkdir -p "${func_dir}" || return
if [[ -n $HOST_MAC_ADDR ]]; then
echo "${HOST_MAC_ADDR}" >"${func_dir}"/host_addr || return
fi
if [[ -n $DEV_MAC_ADDR ]]; then
echo "${DEV_MAC_ADDR}" >"${func_dir}"/dev_addr || return
fi
ln -s "${func_dir}" "${config_dir}" || return
# This only works on kernel 5.12+, we have to ignore failures for now
echo "$IFACE_NAME" >"${func_dir}"/ifname || true
echo "${BIND_DEVICE}" >"${gadget_dir}"/UDC || return
# Try to reconfigure a few times in case we race with systemd-networkd
local start=$SECONDS
while (( SECONDS - start < 5 )); do
local ifname
ifname="$(<"${func_dir}"/ifname)" || return
[ "${IFACE_NAME}" = "$ifname" ] && break
ip link set dev "$ifname" down && \
ip link set dev "$ifname" name "${IFACE_NAME}" && break
sleep 1
done
ip link set dev "$IFACE_NAME" up || return
}
gadget_stop() {
local gadget_dir="${CONFIGFS_HOME}/usb_gadget/${GADGET_DIR_NAME}"
rm -f "${gadget_dir}/configs/c.1/${DEV_TYPE}.${IFACE_NAME}"
rmdir "${gadget_dir}/functions/${DEV_TYPE}.${IFACE_NAME}" \
"${gadget_dir}/configs/c.1/strings/0x409" \
"${gadget_dir}/configs/c.1" \
"${gadget_dir}/strings/0x409" \
"${gadget_dir}" || true
rm -f /run/systemd/network/+-bmc-"${IFACE_NAME}".network
networkctl reload || true
}
opts="$(getopt \
--longoptions "$(printf "%s," "${ARGUMENT_LIST[@]}")" \
--name "$(basename "$0")" \
--options "" \
-- "$@"
)"
eval set -- "$opts"
CONFIGFS_HOME=${CONFIGFS_HOME:-/sys/kernel/config}
ID_VENDOR="0x18d1" # Google
ID_PRODUCT=""
STR_EN_VENDOR="Google"
STR_EN_PRODUCT=""
DEV_MAC_ADDR=""
DEV_TYPE="eem"
HOST_MAC_ADDR=""
BIND_DEVICE=""
ACTION="start"
GADGET_DIR_NAME=""
IFACE_NAME=""
while [[ $# -gt 0 ]]; do
case "$1" in
--product-id)
ID_PRODUCT=$2
shift 2
;;
--product-name)
STR_EN_PRODUCT=$2
shift 2
;;
--host-mac)
HOST_MAC_ADDR=$2
shift 2
;;
--dev-mac)
DEV_MAC_ADDR=$2
shift 2
;;
--dev-type)
DEV_TYPE=$2
shift 2
;;
--bind-device)
BIND_DEVICE=$2
shift 2
;;
--gadget-dir-name)
GADGET_DIR_NAME=$2
shift 2
;;
--iface-name)
IFACE_NAME=$2
shift 2
;;
--help)
print_usage
exit 0
;;
start)
ACTION="start"
shift 1
break
;;
stop)
ACTION="stop"
shift 1
break
;;
--)
shift 1
;;
*)
break
;;
esac
done
if [ -z "$GADGET_DIR_NAME" ]; then
GADGET_DIR_NAME="$IFACE_NAME"
fi
if [[ $ACTION == "stop" ]]; then
gadget_stop
else
if [ -z "$ID_PRODUCT" ]; then
echo "Product ID is missing" >&2
exit 1
fi
if [ -z "$IFACE_NAME" ]; then
echo "Interface name is missing" >&2
exit 1
fi
if [ -z "$BIND_DEVICE" ]; then
echo "Bind device is missing" >&2
exit 1
fi
rc=0
gadget_start || rc=$?
(( rc == 0 )) || gadget_stop || true
exit $rc
fi
@@ -0,0 +1,290 @@
#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
TEMPDIRS=()
# Script under test
SUT=$PWD/usb_network.sh
TEST_STATUS="OK"
test_setup() {
echo -n "Testing $1 ..."
FAKE_CONFIGFS="$(mktemp -d)"
TEMPDIRS+=("${FAKE_CONFIGFS}")
FAKE_GADGETFS="$FAKE_CONFIGFS"/usb_gadget
mkdir -p "$FAKE_GADGETFS"
}
test_teardown() {
echo ${TEST_STATUS}
rm -rf -- "${TEMPDIRS[@]}"
TEMPDIRS=()
}
test_fail() {
echo -n " $* " >&2
TEST_STATUS=FAIL
test_teardown
exit 1
}
check_file_content() {
local filename="$1"
local expected_content="$2"
if [[ ! -f ${filename} ]]; then
test_fail "File ${filename} does not exist!"
fi
local actual_content
actual_content=$(<"${filename}")
if [[ $expected_content != "$actual_content" ]]; then
test_fail "Expected ${expected_content}, got ${actual_content}"
fi
}
test_gadget_creation_with_defaults() {
local extra_args=()
local gadget_dir="$1"
if [[ $gadget_dir == "" ]]; then
gadget_dir="g1";
else
extra_args+=(--gadget-dir-name "${gadget_dir}")
fi
local product_name="Souvenier BMC"
local product_id="0xcafe"
local host_mac="ab:cd:ef:10:11:12"
local dev_mac="12:11:10:ef:cd:ab"
local bind_device="f80002000.udc"
if ! CONFIGFS_HOME="${FAKE_CONFIGFS}" "${SUT}" --product-id "${product_id}" \
--product-name "${product_name}" \
--host-mac "${host_mac}" \
--dev-mac "${dev_mac}" \
--bind-device "${bind_device}" \
"${extra_args[@]}"; then
test_fail "${SUT} failed"
fi
if [[ ! -d ${FAKE_GADGETFS}/${gadget_dir} ]]; then
test_fail "Gadget was not created!"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idVendor" "0x18d1"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idProduct" "${product_id}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/manufacturer" "Google"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/product" "${product_name}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/MaxPower" "100"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/strings/0x409/configuration" "EEM"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0/dev_addr" "${dev_mac}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0/host_addr" "${host_mac}"
if [[ ! -d ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0 ]]; then
test_fail "Function directory was not created"
fi
local func_link="${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/ee.usb0"
if [[ ! -L ${func_link} ]]; then
test_fail "Symlink to the function was not created in the config"
fi
local link_dest
link_dest="$(realpath "${func_link}")"
if [[ $link_dest != "${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0" ]]; then
test_fail "Symlink points to the wrong file/dir"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/UDC" "${bind_device}"
}
test_gadget_creation_with_override() {
mkdir -p "${FAKE_GADGETFS}"/g1/{strings,configs,functions}
touch "${FAKE_GADGETFS}"/g1/{idVendor,idProduct}
test_gadget_creation_with_defaults
}
test_gadget_stopping() {
local extra_args=()
local gadget_dir="$1"
local iface_name="$2"
if [[ $gadget_dir == "" ]]; then
gadget_dir="g1";
else
extra_args+=(--gadget-dir-name "${gadget_dir}")
fi
if [[ $iface_name == "" ]]; then
iface_name="usb0";
else
extra_args+=(--iface-name "${iface_name}")
fi
CONFIGFS_HOME=${FAKE_CONFIGFS} ${SUT} "${extra_args[@]}" stop
if test -d "${FAKE_GADGETFS}/${gadget_dir}"; then
test_fail "Gadget was not removed!"
fi
}
test_gadget_creation_no_macs() {
local gadget_dir="g1";
local product_name="Souvenier BMC"
local product_id="0xcafe"
local bind_device="f80002000.udc"
CONFIGFS_HOME=${FAKE_CONFIGFS} ${SUT} --product-id "${product_id}" \
--product-name "${product_name}" \
--bind-device "${bind_device}"
if test $? -ne 0; then
test_fail "${SUT} failed"
fi
if ! test -d "${FAKE_GADGETFS}/${gadget_dir}"; then
test_fail "Gadget was not created!"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idVendor" "0x18d1"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idProduct" "${product_id}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/manufacturer" "Google"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/product" "${product_name}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/MaxPower" "100"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/strings/0x409/configuration" "EEM"
if [[ -e ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0/dev_addr ]]; then
test_fail "dev_addr should not be set"
fi
if [[ -e ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0/host_addr ]]; then
test_fail "host_addr should not be set"
fi
local func_link="${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/eem.usb0"
if [[ ! -L ${func_link} ]]; then
test_fail "Symlink to the function was not created in the config"
fi
local link_dest
link_dest="$(realpath "${func_link}")"
if [[ $link_dest != ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.usb0 ]]; then
test_fail "Symlink points to the wrong file/dir"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/UDC" "${bind_device}"
}
test_gadget_creation_alt_iface() {
local gadget_dir="g1";
local product_name="Souvenier BMC"
local product_id="0xcafe"
local bind_device="f80002000.udc"
local iface_name="iface0"
if ! CONFIGFS_HOME=${FAKE_CONFIGFS} ${SUT} --product-id "${product_id}" \
--product-name "${product_name}" \
--bind-device "${bind_device}" \
--iface-name "${iface_name}"; then
test_fail "${SUT} failed"
fi
if [[ ! -d "${FAKE_GADGETFS}/${gadget_dir}" ]]; then
test_fail "Gadget was not created!"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idVendor" "0x18d1"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/idProduct" "${product_id}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/manufacturer" "Google"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/strings/0x409/product" "${product_name}"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/MaxPower" "100"
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/strings/0x409/configuration" "EEM"
if [[ ! -d ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.${iface_name} ]]; then
test_fail "Function directory was not created"
fi
if [[ -e ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.${iface_name}/dev_addr ]]; then
test_fail "dev_addr should not be set"
fi
if [[ -e ${FAKE_GADGETFS}/${gadget_dir}/functions/eem.${iface_name}/host_addr ]]; then
test_fail "host_addr should not be set"
fi
local func_link="${FAKE_GADGETFS}/${gadget_dir}/configs/c.1/eem.${iface_name}"
if [[ ! -L ${func_link} ]]; then
test_fail "Symlink to the function was not created in the config"
fi
local link_dest
link_dest="$(realpath "${func_link}")"
if [[ $link_dest != "${FAKE_GADGETFS}/${gadget_dir}/functions/eem.${iface_name}" ]]; then
test_fail "Symlink points to the wrong file/dir"
fi
check_file_content "${FAKE_GADGETFS}/${gadget_dir}/UDC" "${bind_device}"
}
# -------------------------------------------------------------------
test_setup "Device Creation"
test_gadget_creation_with_defaults
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Device Creation With Override"
test_gadget_creation_with_override
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Test Device Stop"
test_gadget_creation_with_defaults
test_gadget_stopping
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Device Creation/Stopping, Alternative Name"
test_gadget_creation_with_defaults "gAlt"
test_gadget_stopping "gAlt"
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Device Creation without MAC Addrs"
test_gadget_creation_no_macs
test_teardown
# -------------------------------------------------------------------
# -------------------------------------------------------------------
test_setup "Device Creation/Stopping, Alternative Interface"
test_gadget_creation_alt_iface
test_teardown
# -------------------------------------------------------------------
echo "SUCCESS!"