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 2 commits
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
8 changes: 5 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,11 @@ 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})
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS} ${DEPENDENCIES}
extraFiles.qrc)
endif()

set_property(TARGET ${PROJECT_NAME} PROPERTY AUTOMOC ON)
Expand Down Expand Up @@ -495,4 +497,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})
35 changes: 34 additions & 1 deletion src/OptionsPopup.qml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Window {
property string cloudinitrun
property string cloudinitwrite
property string cloudinitnetwork
property bool deviceUsbOtgSupport
property bool enableEtherGadget

signal saveSettingsSignal(var settings)

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

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

ImCheckBox {
id: chkSSH
text: qsTr("Enable SSH")
Expand Down Expand Up @@ -629,6 +637,18 @@ Window {
}
}

var hwFilterList = imageWriter.getHWFilterList()
var hwFilterIsModelZero = imageWriter.getHWFilterIsModelZero()

if (hwFilterList) {
var targetTags = ["pi5-64bit", "pi4-64bit", "pi5-32bit", "pi4-32bit"]
paulober marked this conversation as resolved.
Show resolved Hide resolved
deviceUsbOtgSupport = targetTags.some(tag => hwFilterList.includes(tag)) || hwFilterIsModelZero
if (!deviceUsbOtgSupport) {
// make sure it isn't disabled and selected
chkUSBEther = false;
}
}

//open()
show()
raise()
Expand Down Expand Up @@ -826,6 +846,19 @@ Window {

addCmdline("cfg80211.ieee80211_regdom="+fieldWifiCountry.editText)
}
if (chkUSBEther.checked) {
addConfig("dtoverlay=dwc2,dr_mode=peripheral")

enableEtherGadget = true;

addFirstRun("\nmv /boot/firmware/10usb.net /etc/systemd/network/10-usb.network")
addFirstRun("mv /boot/firmware/geth.cnf /etc/modprobe.d/g_ether.conf")
addFirstRun("mv /boot/firmware/gemod.cnf /etc/modules-load.d/usb-ether-gadget.conf\n")
addFirstRun("SERIAL=$(grep Serial /proc/cpuinfo | awk '{print $3}')")
addFirstRun("sed -i \"s/<serial>/$SERIAL/g\" /etc/modprobe.d/g_ether.conf")
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 +903,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
paulober marked this conversation as resolved.
Show resolved Hide resolved
networkConfig.clear();

// only needed for manual config without g_ether
paulober marked this conversation as resolved.
Show resolved Hide resolved
QByteArray modprobeConf = _fileGetContentsTrimmed("://extraFiles/g_ether.conf");
fat->writeFile("geth.cnf", modprobeConf);
// little optimization for memory constraint systems
modprobeConf.clear();

QByteArray modulesConf = _fileGetContentsTrimmed("://extraFiles/usb-ether-gadget.conf");
fat->writeFile("gemod.cnf", modulesConf);
// not needed anymore, because auto cleanup after out of scope
//modulesConf.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/g_ether.conf</file>
<file>extraFiles/usb-ether-gadget.conf</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
1 change: 1 addition & 0 deletions src/extraFiles/g_ether.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
options g_ether idVendor=0x04b3 idProduct=0x4010 iManufacturer="Raspberry Pi" bcdDevice=0x0100 iProduct="USB Ethernet Gadget" iSerialNumber=<serial>
paulober marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions src/extraFiles/usb-ether-gadget.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dwc2
g_ether
21 changes: 18 additions & 3 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 @@ -477,10 +477,23 @@ namespace {
} // namespace anonymous


void ImageWriter::setHWFilterList(const QByteArray &json, const bool &inclusive) {
void ImageWriter::setHWFilterList(const QByteArray &json, const bool &inclusive, const bool &isModelZero) {
QJsonDocument json_document = QJsonDocument::fromJson(json);
_deviceFilter = json_document.array();
_deviceFilterIsInclusive = inclusive;
_isModelZero = isModelZero;
}

QJsonArray ImageWriter::getHWFilterList() {
return _deviceFilter;
}

bool ImageWriter::getHWFilterListInclusive() {
return _deviceFilterIsInclusive;
}

bool ImageWriter::getHWFilterIsModelZero() {
return _isModelZero;
}

void ImageWriter::handleNetworkRequestFinished(QNetworkReply *data) {
Expand Down Expand Up @@ -1184,18 +1197,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
16 changes: 14 additions & 2 deletions src/imagewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,16 @@ class ImageWriter : public QObject
Q_INVOKABLE void beginOSListFetch();

/** Set the HW filter, for a filtered view of the OS list */
Q_INVOKABLE void setHWFilterList(const QByteArray &json, const bool &inclusive);
Q_INVOKABLE void setHWFilterList(const QByteArray &json, const bool &inclusive, const bool &isModelZero);

/* Get the HW filter list */
Q_INVOKABLE QJsonArray getHWFilterList();

/* Get if the HW filter is in inclusive mode */
Q_INVOKABLE bool getHWFilterListInclusive();

/* Get if HW filter tags are from a Pi Zero model */
Q_INVOKABLE bool getHWFilterIsModelZero();

/* Set custom cache file */
void setCustomCacheFile(const QString &cacheFile, const QByteArray &sha256);
Expand Down Expand Up @@ -123,7 +132,7 @@ class ImageWriter : public QObject

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 setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork, const bool enableEtherGadget);
Q_INVOKABLE void setSavedCustomizationSettings(const QVariantMap &map);
Q_INVOKABLE QVariantMap getSavedCustomizationSettings();
Q_INVOKABLE void clearSavedCustomizationSettings();
Expand Down Expand Up @@ -186,11 +195,14 @@ protected slots:
QJsonDocument _completeOsList;
QJsonArray _deviceFilter;
bool _deviceFilterIsInclusive;
/* As there is no distinction between normal pi models and zeros (in the tags), this flag can be used to differenciate */
paulober marked this conversation as resolved.
Show resolved Hide resolved
bool _isModelZero;

protected:
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
2 changes: 1 addition & 1 deletion src/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -1614,7 +1614,7 @@ ApplicationWindow {
}
}

imageWriter.setHWFilterList(hwmodel.tags, inclusive)
imageWriter.setHWFilterList(hwmodel.tags, inclusive, hwmodel.name.toLowerCase().includes("zero"))
paulober marked this conversation as resolved.
Show resolved Hide resolved

/* Reload list */
var oslist_json = imageWriter.getFilteredOSlist();
Expand Down