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