Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add USB Ethernet Gadget toggle #933

Open
wants to merge 12 commits into
base: qml
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,8 @@ set(SOURCES ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc ${QM_FILES})

if (WIN32)
# Adding WIN32 prevents a console window being opened on Windows
add_executable(${PROJECT_NAME} WIN32 ${SOURCES} ${HEADERS} ${DEPENDENCIES})
add_executable(${PROJECT_NAME} WIN32 ${SOURCES} ${HEADERS} ${DEPENDENCIES}
extraFiles.qrc)
else()
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS} ${DEPENDENCIES})
endif()
paulober marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -495,4 +496,4 @@ else()
endif()

include_directories(${CURL_INCLUDE_DIR} ${LibArchive_INCLUDE_DIR} ${LIBLZMA_INCLUDE_DIRS} ${LIBDRM_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${ZSTD_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE ${QT}::Core ${QT}::Quick ${QT}::Svg ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${ZSTD_LIBRARIES} ${ZLIB_LIBRARIES} ${LIBLZMA_LIBRARIES} ${LIBDRM_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS})
target_link_libraries(${PROJECT_NAME} PRIVATE ${QT}::Core ${QT}::Quick ${QT}::Svg ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${ZSTD_LIBRARIES} ${ZLIB_LIBRARIES} ${LIBLZMA_LIBRARIES} ${LIBDRM_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS})
42 changes: 41 additions & 1 deletion src/OptionsPopup.qml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Window {
property string cloudinitrun
property string cloudinitwrite
property string cloudinitnetwork
property bool enableEtherGadget

signal saveSettingsSignal(var settings)

Expand Down Expand Up @@ -355,6 +356,11 @@ Window {
ColumnLayout {
// Remote access tab

ImCheckBox {
id: chkUSBEther
text: qsTr("Enable USB Ethernet Gadget")
}

ImCheckBox {
id: chkSSH
text: qsTr("Enable SSH")
Expand Down Expand Up @@ -826,6 +832,40 @@ Window {

addCmdline("cfg80211.ieee80211_regdom="+fieldWifiCountry.editText)
}
if (chkUSBEther.checked) {
addConfig("dtoverlay=dwc2,dr_mode=peripheral")
// only required if no conf is used to load modules
// g_ether must not be loaded when using manual config
// with sh script
addCmdline("modules-load=dwc2,g_ether")
// TODO: generate mac addresses if there are issues with DHCP
//addCmdline("g_ether.dev_addr=8A:89:6a:8d:14:22")
//addCmdline("g_ether.host_addr=6A:89:6a:8d:14:22")
addCmdline("g_ether.idVendor=0x04b3")
addCmdline("g_ether.idProduct=0x4010")
addCmdline("g_ether.iManufacturer=\"Raspberry Pi\"")
addCmdline("g_ether.bcdDevice=0x0100")
addCmdline("g_ether.iProduct=\"USB Ethernet Gadget\"")
// TODO: maybe set device serial
paulober marked this conversation as resolved.
Show resolved Hide resolved
//addCmdline("g_ether.iSerialNumber=8c1aceb07269b131")

enableEtherGadget = true;

// manual config with this script requires not to load g_ether
//addFirstRun("mv /boot/firmware/etherSet.sh /usr/sbin/configure-usb-ether-gadget-once")
paulober marked this conversation as resolved.
Show resolved Hide resolved
//addFirstRun("chmod +x /usr/sbin/configure-usb-ether-gadget-once")
//addFirstRun("mv /boot/firmware/sysdEth.srv /etc/systemd/system/usb-ether-gadget-once.service")
//addFirstRun("mkdir -p /etc/systemd/system/usb-gadget.target.wants")
//addFirstRun("ln -s /etc/systemd/system/usb-ether-gadget-once.service /etc/systemd/system/usb-gadget.target.wants/usb-ether-gadget-once.service")
addFirstRun("mv /boot/firmware/10usb.net /etc/systemd/network/10-usb.network\n")
// enable stuff
//addFirstRun("systemctl daemon-reload")
//addFirstRun("systemctl enable usb-ether-gadget-once.service")
//addFirstRun("systemctl start usb-ether-gadget-once.service\n")
// enable networkd as I don't have NetworkManager config
addFirstRun("systemctl enable systemd-networkd\n")
}

if (chkLocale.checked) {
var kbdconfig = "XKBMODEL=\"pc105\"\n"
kbdconfig += "XKBLAYOUT=\""+fieldKeyboardLayout.editText+"\"\n"
Expand Down Expand Up @@ -870,7 +910,7 @@ Window {
addCloudInit("runcmd:\n"+cloudinitrun+"\n")
}

imageWriter.setImageCustomization(config, cmdline, firstrun, cloudinit, cloudinitnetwork)
imageWriter.setImageCustomization(config, cmdline, firstrun, cloudinit, cloudinitnetwork, enableEtherGadget)
}

function saveSettings()
Expand Down
4 changes: 2 additions & 2 deletions src/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ int Cli::run()
return 1;
}

_imageWriter->setImageCustomization("", "", "", userData, networkConfig);
_imageWriter->setImageCustomization("", "", "", userData, networkConfig, false);
}
else if (!parser.value("first-run-script").isEmpty())
{
Expand All @@ -208,7 +208,7 @@ int Cli::run()
return 1;
}

_imageWriter->setImageCustomization("", "", firstRunScript, "", "");
_imageWriter->setImageCustomization("", "", firstRunScript, "", "", false);
paulober marked this conversation as resolved.
Show resolved Hide resolved
}

_imageWriter->setDst(args[1]);
Expand Down
22 changes: 21 additions & 1 deletion src/downloadthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,14 +885,15 @@ qint64 DownloadThread::_sectorsWritten()
return -1;
}

void DownloadThread::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudInitNetwork, const QByteArray &initFormat)
void DownloadThread::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudInitNetwork, const QByteArray &initFormat, const bool enableEtherGadget)
{
_config = config;
_cmdline = cmdline;
_firstrun = firstrun;
_cloudinit = cloudinit;
_cloudinitNetwork = cloudInitNetwork;
_initFormat = initFormat;
_enableEtherGadget = enableEtherGadget;
}

bool DownloadThread::_customizeImage()
Expand Down Expand Up @@ -974,6 +975,25 @@ bool DownloadThread::_customizeImage()
_cmdline += " systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target";
}

if (_enableEtherGadget) {
// load files from disk and write
QByteArray networkConfig = _fileGetContentsTrimmed("://extraFiles/10-usb.network");
fat->writeFile("10usb.net", networkConfig);
// little optimization for memory constraint systems - add if more files are loaded in this scope
//networkConfig.clear();

// only needed for manual config without g_ether
paulober marked this conversation as resolved.
Show resolved Hide resolved
//QByteArray script = _fileGetContentsTrimmed("://extraFiles/configure-usb-ether-gadget-once.sh");
//fat->writeFile("etherSet.sh", script);
// little optimization for memory constraint systems - add if more files are loaded in this scope
//script.clear();

//QByteArray serviceFile = _fileGetContentsTrimmed("://extraFiles/usb-ether-gadget-once.service");
//fat->writeFile("sysdEth.srv", serviceFile);
// not needed anymore, because auto cleanup after out of scope
//serviceFile.clear();
}

if (!_cloudinit.isEmpty() && _initFormat == "cloudinit")
{
_cloudinit = "#cloud-config\n"+_cloudinit;
Expand Down
3 changes: 2 additions & 1 deletion src/downloadthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class DownloadThread : public QThread
/*
* Enable image customization
*/
void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork, const QByteArray &initFormat);
void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork, const QByteArray &initFormat, const bool enableEtherGadget);

/*
* Thread safe download progress query functions
Expand Down Expand Up @@ -171,6 +171,7 @@ class DownloadThread : public QThread
std::uint64_t _lastFailureOffset;
qint64 _sectorsStart;
QByteArray _url, _useragent, _buf, _filename, _lastError, _expectedHash, _config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat;
bool _enableEtherGadget;
char *_firstBlock;
size_t _firstBlockSize;
static QByteArray _proxy;
Expand Down
7 changes: 7 additions & 0 deletions src/extraFiles.qrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/">
<file>extraFiles/10-usb.network</file>
<file>extraFiles/configure-usb-ether-gadget-once.sh</file>
<file>extraFiles/usb-ether-gadget-once.service</file>
</qresource>
</RCC>
33 changes: 33 additions & 0 deletions src/extraFiles/10-usb.network
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[Match]
Name=usb*

[Link]
RequiredForOnline=no

[Network]
# Configure Subnet for USB Ethernet Gadget
# - IP Range: 10.12.194.1 to 10.12.194.14
# - Total IPs: 16
# - Usable IPs: 14
# - Network Address: 10.12.194.0
# - Broadcast Address: 10.12.194.15
# - Subnet Mask: 255.255.255.240 (/28)
# | 10 - private
# | 12 - 2012 founding of Raspberry Pi Ltd.
# | 194 - address of Raspberry Pi Ltd. in the Science Park, Cambridge
# | 1 - first device
# TODO: maybe only static hostname to not conflict when multiple devices are connected
# to the same computer
Address=10.12.194.1/28
DHCPServer=yes

[DHCPServer]
# Configure DHCP settings
PoolSize=16 # Number of IP addresses available for lease
DefaultLeaseTimeSec=60 # Default lease time for DHCP clients
MaxLeaseTimeSec=60 # Maximum lease time for DHCP clients

# Network isolation settings (no internet)
EmitDNS=no # Do not provide DNS information
EmitNTP=no # Do not provide NTP information
EmitRouter=no # Do not provide router information
115 changes: 115 additions & 0 deletions src/extraFiles/configure-usb-ether-gadget-once.sh
paulober marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/bin/bash

######################
## Config variables ##
######################
# required for RNDIS (Windows compatibility)
# Acer Incorporated. - Other hardware - Acer USB Ethernet/RNDIS Gadget
# did not work correctly
#VID="0x0502"
#PID="0x3230"
# Did work correctly with IBM driver
VID="0x04b3"
PID="0x4010"
SERIAL=$(grep Serial /proc/cpuinfo | awk '{print $3}')
# Compute the SHA256 hash of the serial number
sha=$(echo -n "$SERIAL" | sha256sum | cut -d ' ' -f 1)
config_path=g_ether
##########################
## END Config variables ##
##########################

###############################
## Setup basic configuration ##
###############################
mkdir -p ${config_path}
cd ${config_path} || exit 1

echo "${VID}" > idVendor # Custom Vendor ID
echo "${PID}" > idProduct # Custom Product ID
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2
# setup appearance
mkdir -p strings/0x409
echo "${SERIAL}" > strings/0x409/serialnumber
echo "Raspberry Pi" > strings/0x409/manufacturer
echo "USB Ethernet Gadget" > strings/0x409/product
###################################
## END Setup basic configuration ##
###################################

####################################################
## ECM (Ethernet Control Model) for Mac and Linux ##
####################################################
mkdir -p configs/c.1/strings/0x409
mkdir -p functions/ecm.usb0

# max possible stable USB 2.0 power
echo 250 > configs/c.1/MaxPower
echo "ECM" > configs/c.1/strings/0x409/configuration
########################################################
## END ECM (Ethernet Control Model) for Mac and Linux ##
########################################################

#####################################
## RNDIS for Windows compatibility ##
#####################################
mkdir -p configs/c.2/strings/0x409
mkdir -p functions/rndis.usb0
# not allowed
#mkdir -p os_desc/interface.rndis

echo "RNDIS" > configs/c.2/strings/0x409/configuration
#echo "RNDIS" > os_desc/interface.rndis/compatibility_id
#echo "5162001" > os_desc/interface.rndis/sub_compatibility_id
#########################################
## END RNDIS for Windows compatibility ##
#########################################

#########################
## Setup MAC addresses ##
#########################
# Generate MAC addresses using slices of the hash
# TODO: maybe change prefix
mac0=6A:${sha:2:2}:${sha:4:2}:${sha:6:2}:${sha:8:2}:${sha:10:2}
mac1=7A:${sha:2:2}:${sha:4:2}:${sha:6:2}:${sha:8:2}:${sha:10:2}
mac2=8A:${sha:2:2}:${sha:4:2}:${sha:6:2}:${sha:8:2}:${sha:10:2}
mac3=9A:${sha:2:2}:${sha:4:2}:${sha:6:2}:${sha:8:2}:${sha:10:2}

echo "${mac1}" > functions/ecm.usb0/host_addr
echo "${mac3}" > functions/ecm.usb0/dev_addr

echo "${mac0}" > functions/rndis.usb0/host_addr
echo "${mac2}" > functions/rndis.usb0/dev_addr
#############################
## END Setup MAC addresses ##
#############################

######################################
## Link functions to configurations ##
######################################
ln -s functions/ecm.usb0 configs/c.1/
ln -s functions/rndis.usb0 configs/c.2/
##########################################

######################################
## Link the UDC to start the gadget ##
######################################
count=0
while [ ${count} -lt 15 ]; do
udc="$(ls /sys/class/udc)"
if [ -n "${udc}" ]; then
echo "Found UDC ${udc}"
echo "${udc}" > UDC
break
fi
count=$((count + 1))
sleep 1
done
##########################################
## END Link the UDC to start the gadget ##
##########################################

systemctl restart getty@ttyGS0

echo "Ethernet USB OTG gadget init complete - $(cat UDC)"
17 changes: 17 additions & 0 deletions src/extraFiles/usb-ether-gadget-once.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[Unit]
Description=Configure USB Ethernet Gadget
Requires=sys-kernel-config.mount
After=sys-kernel-config.mount
Requires=systemd-modules-load.service
After=systemd-modules-load.service

[Service]
Type=oneshot
WorkingDirectory=/sys/kernel/config/usb_gadget
ExecStart=/usr/sbin/configure-usb-ether-gadget-once
StandardOutput=journal+console
# TODO: remove
User=root

[Install]
WantedBy=usb-gadget.target
6 changes: 4 additions & 2 deletions src/imagewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ void ImageWriter::startWrite()
connect(_thread, SIGNAL(preparationStatusUpdate(QString)), SLOT(onPreparationStatusUpdate(QString)));
_thread->setVerifyEnabled(_verifyEnabled);
_thread->setUserAgent(QString("Mozilla/5.0 rpi-imager/%1").arg(constantVersion()).toUtf8());
_thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat);
_thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat, _enableEtherGadget);

if (!_expectedHash.isEmpty() && _cachedFileHash != _expectedHash && _cachingEnabled)
{
Expand Down Expand Up @@ -1184,18 +1184,20 @@ void ImageWriter::setSetting(const QString &key, const QVariant &value)
_settings.sync();
}

void ImageWriter::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork)
void ImageWriter::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork, const bool enableEtherGadget)
{
_config = config;
_cmdline = cmdline;
_firstrun = firstrun;
_cloudinit = cloudinit;
_cloudinitNetwork = cloudinitNetwork;
_enableEtherGadget = enableEtherGadget;

qDebug() << "Custom config.txt entries:" << config;
qDebug() << "Custom cmdline.txt entries:" << cmdline;
qDebug() << "Custom firstuse.sh:" << firstrun;
qDebug() << "Cloudinit:" << cloudinit;
qDebug() << "Enable USB Ethernet gadget:" << enableEtherGadget;
}

QString ImageWriter::crypt(const QByteArray &password)
Expand Down
5 changes: 3 additions & 2 deletions src/imagewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ class ImageWriter : public QObject
Q_INVOKABLE QString getPSK();

Q_INVOKABLE bool getBoolSetting(const QString &key);
Q_INVOKABLE void setSetting(const QString &key, const QVariant &value);
Q_INVOKABLE void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork);
Q_INVOKABLE void setSetting(const QString &key, const QVariant &value);//, const QVariantList
paulober marked this conversation as resolved.
Show resolved Hide resolved
Q_INVOKABLE void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork, const bool enableEtherGadget);
paulober marked this conversation as resolved.
Show resolved Hide resolved
Q_INVOKABLE void setSavedCustomizationSettings(const QVariantMap &map);
Q_INVOKABLE QVariantMap getSavedCustomizationSettings();
Q_INVOKABLE void clearSavedCustomizationSettings();
Expand Down Expand Up @@ -191,6 +191,7 @@ protected slots:
QUrl _src, _repo;
QString _dst, _cacheFileName, _parentCategory, _osName, _currentLang, _currentLangcode, _currentKeyboard;
QByteArray _expectedHash, _cachedFileHash, _cmdline, _config, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat;
bool _enableEtherGadget;
quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow;
DriveListModel _drivelist;
QQmlApplicationEngine *_engine;
Expand Down