xref: /openbmc/openbmc/meta-google/recipes-google/networking/google-usb-network/usb_network.sh (revision a8a08aaa5117fc6b7a295b75d5d9117dbe370ba6)
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