The default UDC configuration files in /opt/nvidia/l4t-usb-device-mode provide the following.
USB Composite Device:
-Remote NDIS Compatible Device
-CDC NCM
-USB Serial Device
On a Windows 11 machine, all these interfaces enumerate as expected.
Two independent network interfaces are created through a single usb connection. One RNDIS, One CDC NCM.
I have changed the configuration file to create the following:
USB Composite Device:
-(optional) 2 x Remote NDIS Compatible Device
-(optional) 2 x CDC NCM
-USB Serial Device
For systems that only support one ethernet driver, configuring two independent net interfaces is still useful.
Windows 11 does not enumerate both RNDIS devices.
Windows 11 does enumerate both CDC NCM devices but gives them both the same physical mac address.
On a Linux machine both CDC NCM devices also enumerate but with the same physical mac addresses.
I have changed the configuration files to provide unique mac addresses to each interface.
Is dual ethernet functions of the same type not allowed in Linux Gadget Configurations, this particular UDC hardware, or does my configuration file contain errors?
Below is the modified Linux Gadgets configuration file. Using the default nvidia systemd service to trigger execution.
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2017-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e
script_dir="$(cd "$(dirname "$0")" && pwd)"
. "${script_dir}/nv-l4t-usb-device-mode-config.sh"
modprobe libcomposite
for attempt in $(seq 60); do
udc_dev_t210=700d0000.xudc
if [ -e "/sys/class/udc/${udc_dev_t210}" ]; then
udc_dev="${udc_dev_t210}"
break
fi
udc_dev_t186=3550000.xudc
if [ -e "/sys/class/udc/${udc_dev_t186}" ]; then
udc_dev="${udc_dev_t186}"
break
fi
udc_dev_t186=3550000.usb
if [ -e "/sys/class/udc/${udc_dev_t186}" ]; then
udc_dev="${udc_dev_t186}"
break
fi
sleep 1
done
if [ "${udc_dev}" == "" ]; then
echo No known UDC device found
exit 1
fi
macs_file="${script_dir}/mac-addresses"
if [ -f "${macs_file}" ]; then
. "${macs_file}"
if ! [[ "${mac_rndis_h1}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ &&
"${mac_rndis_d1}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ &&
"${mac_rndis_h2}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ &&
"${mac_rndis_d2}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ &&
"${mac_ecm_h1}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ &&
"${mac_ecm_d1}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ &&
"${mac_ecm_h2}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ &&
"${mac_ecm_d2}" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]]; then
rm "${macs_file}"
fi
fi
if ! [ -f "${macs_file}" ]; then
# Generate unique data
if [ -f /proc/device-tree/serial-number ]; then
random="$(md5sum /proc/device-tree/serial-number|cut -c1-12)"
else
random="$(echo "no-serial"|md5sum|cut -c1-12)"
fi
# Extract 6 bytes
b1="$(echo "${random}"|cut -c1-2)"
b2="$(echo "${random}"|cut -c3-4)"
b3="$(echo "${random}"|cut -c5-6)"
b4="$(echo "${random}"|cut -c7-8)"
b5="$(echo "${random}"|cut -c9-10)"
b6="$(echo "${random}"|cut -c11-12)"
# Clear broadcast/multicast, set locally administered bits
b1="$(printf "%02x" "$(("0x${b1}" & 0xfe | 0x02))")"
# Set 4 LSBs to unique value per interface
b6_rndis_h1="$(printf "%02x" "$(("0x${b6}" & 0xf8 | 0x00))")"
b6_rndis_d1="$(printf "%02x" "$(("0x${b6}" & 0xf8 | 0x01))")"
b6_rndis_h2="$(printf "%02x" "$(("0x${b6}" & 0xf8 | 0x02))")"
b6_rndis_d2="$(printf "%02x" "$(("0x${b6}" & 0xf8 | 0x03))")"
b6_ecm_h1="$(printf "%02x" "$(("0x${b6}" & 0xf8 | 0x04))")"
b6_ecm_d1="$(printf "%02x" "$(("0x${b6}" & 0xf8 | 0x05))")"
b6_ecm_h2="$(printf "%02x" "$(("0x${b6}" & 0xf8 | 0x06))")"
b6_ecm_d2="$(printf "%02x" "$(("0x${b6}" & 0xf8 | 0x07))")"
# Construct complete MAC per interface
mac_rndis_h1="${b1}:${b2}:${b3}:${b4}:${b5}:${b6_rndis_h1}"
mac_rndis_d1="${b1}:${b2}:${b3}:${b4}:${b5}:${b6_rndis_d1}"
mac_rndis_h2="${b1}:${b2}:${b3}:${b4}:${b5}:${b6_rndis_h2}"
mac_rndis_d2="${b1}:${b2}:${b3}:${b4}:${b5}:${b6_rndis_d2}"
mac_ecm_h1="${b1}:${b2}:${b3}:${b4}:${b5}:${b6_ecm_h1}"
mac_ecm_d1="${b1}:${b2}:${b3}:${b4}:${b5}:${b6_ecm_d1}"
mac_ecm_h2="${b1}:${b2}:${b3}:${b4}:${b5}:${b6_ecm_h2}"
mac_ecm_d2="${b1}:${b2}:${b3}:${b4}:${b5}:${b6_ecm_d2}"
# Save values for next boot
echo "mac_rndis_h1=${mac_rndis_h1}" > "${macs_file}"
echo "mac_rndis_d1=${mac_rndis_d1}" >> "${macs_file}"
echo "mac_rndis_h2=${mac_rndis_h2}" >> "${macs_file}"
echo "mac_rndis_d2=${mac_rndis_d2}" >> "${macs_file}"
echo "mac_ecm_h1=${mac_ecm_h1}" >> "${macs_file}"
echo "mac_ecm_d1=${mac_ecm_d1}" >> "${macs_file}"
echo "mac_ecm_h2=${mac_ecm_h2}" >> "${macs_file}"
echo "mac_ecm_d2=${mac_ecm_d2}" >> "${macs_file}"
fi
mkdir -p /sys/kernel/config/usb_gadget/l4t
cd /sys/kernel/config/usb_gadget/l4t
echo 0x4422 > idVendor
echo 0x2244 > idProduct
echo 0x0001 > bcdDevice
echo 0xEF > bDeviceClass
echo 0x02 > bDeviceSubClass
echo 0x01 > bDeviceProtocol
mkdir -p strings/0x409
if [ -f /proc/device-tree/serial-number ]; then
serialnumber="$(cat /proc/device-tree/serial-number|tr -d '\000')"
else
serialnumber=no-serial
fi
echo "${serialnumber}" > strings/0x409/serialnumber
echo "MANUFACTURER" > strings/0x409/manufacturer
echo "PRODUCT" > strings/0x409/product
cfg=configs/c.1
mkdir -p "${cfg}"
cfg_str=""
is_remote_wakeup=0
# rndis0
if [ ${enable_rndis1} -eq 1 ]; then
cfg_str="${cfg_str}+RNDIS"
func=functions/rndis.usb0
mkdir -p "${func}"
echo "${mac_rndis_h1}" > "${func}/host_addr"
echo "${mac_rndis_d1}" > "${func}/dev_addr"
ln -sf "${func}" "${cfg}"
echo 1 > os_desc/use
echo 0xcd > os_desc/b_vendor_code
echo MSFT100 > os_desc/qw_sign
echo RNDIS > "${func}/os_desc/interface.rndis/compatible_id"
echo 5162001 > "${func}/os_desc/interface.rndis/sub_compatible_id"
ln -sf "${cfg}" os_desc
is_remote_wakeup=1
fi
# rndis1
if [ ${enable_rndis2} -eq 1 ]; then
cfg_str="${cfg_str}+RNDIS"
func=functions/rndis.usb1
mkdir -p "${func}"
echo "${mac_rndis_h2}" > "${func}/host_addr"
echo "${mac_rndis_d2}" > "${func}/dev_addr"
ln -sf "${func}" "${cfg}"
echo RNDIS > "${func}/os_desc/interface.rndis/compatible_id"
echo 5162001 > "${func}/os_desc/interface.rndis/sub_compatible_id"
is_remote_wakeup=1
fi
# serial
if [ ${enable_acm} -eq 1 ]; then
cfg_str="${cfg_str}+ACM"
func=functions/acm.GS0
mkdir -p "${func}"
ln -sf "${func}" "${cfg}"
fi
# usb0
if [ ${enable_ecm1} -eq 1 ]; then
cfg_str="${cfg_str}+${ecm_ncm_name}"
func=functions/${ecm_ncm}.usb0
mkdir -p "${func}"
echo "${mac_ecm_h1}" > "${func}/host_addr"
echo "${mac_ecm_d1}" > "${func}/dev_addr"
ln -sf "${func}" "${cfg}"
is_remote_wakeup=1
fi
# usb1
if [ ${enable_ecm2} -eq 1 ]; then
cfg_str="${cfg_str}+${ecm_ncm_name}"
func=functions/${ecm_ncm}.usb1
mkdir -p "${func}"
echo "${mac_ecm_h2}" > "${func}/host_addr"
echo "${mac_ecm_d2}" > "${func}/dev_addr"
ln -sf "${func}" "${cfg}"
is_remote_wakeup=1
fi
if [ ${is_remote_wakeup} -eq 1 ]; then
echo "0xe0" > "${cfg}/bmAttributes"
else
echo "0xc0" > "${cfg}/bmAttributes"
fi
mkdir -p "${cfg}/strings/0x409"
echo "${cfg_str:1}" > "${cfg}/strings/0x409/configuration"
echo "${udc_dev}" > UDC
cd - # Out of /sys/kernel/config/usb_gadget
# trigger if device connected before script completed
udevadm trigger -v --action=change --property-match=SUBSYSTEM=android_usb
udevadm trigger -v --action=change --property-match=SUBSYSTEM=usb_role
exit 0