diff --git a/CMakeLists.txt b/CMakeLists.txt index d59dc7b..9103054 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,8 +22,9 @@ add_executable(SIMMProgrammer.elf ${SOURCES} ${HWSOURCES}) # Common compiler options target_compile_options(SIMMProgrammer.elf PRIVATE - -Wall -Os -std=gnu99 -ffunction-sections -fdata-sections + -Wall -Os -ffunction-sections -fdata-sections ) +set_property(TARGET SIMMProgrammer.elf PROPERTY C_STANDARD 99) # Common linker options target_link_options(SIMMProgrammer.elf PRIVATE diff --git a/hal/pc/main.cpp b/hal/pc/main.cpp new file mode 100644 index 0000000..6371751 --- /dev/null +++ b/hal/pc/main.cpp @@ -0,0 +1,95 @@ +/* + * main.cpp + * + * Created on: Jul 25, 2021 + * Author: Doug + * + * Copyright (C) 2011-2021 Doug Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mainwindow.h" +#include "programmerthread.h" +#include "usbsimulationmanager.h" +extern "C" +{ +#include "usbcdc_hw.h" +} +#include +#include +#include + +/** + * @brief Performs a bunch of sanity checks to make sure we are empowered to simulate a USB device + * @return True if we are good to go, false if an error occurred. + * + * If there's a problem, show a message informing the user what's wrong. + */ +bool DoSanityChecks(void) +{ + // Make sure configfs is available + if (USBSimulationManager::configFSPath().isEmpty()) + { + QMessageBox::critical(nullptr, "Mountpoint missing", "The configfs filesystem doesn't seem to be mounted on this system. Try running the following command to mount it:\n\n" + "sudo mount -t configfs configfs /sys/kernel/config\n\n"); + return false; + } + + if (USBSimulationManager::localGadgetDir().isEmpty()) + { + QMessageBox::critical(nullptr, "USB gadget not ready", "Could not locate the simulated USB gadget. Have you run the setup_usb_simulation.sh script as root?"); + return false; + } + + if (USBSimulationManager::localGadgetPortDevice().isEmpty()) + { + QMessageBox::critical(nullptr, "USB gadget not ready", "Could not locate the TTY device for the simulated USB gadget. Have you run the setup_usb_simulation.sh script as root?"); + return false; + } + + // We made it, we should be good to go. + return true; +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + // Start by performing a bunch of sanity checks to make sure we actually have a simulated + if (!DoSanityChecks()) + { + return EXIT_FAILURE; + } + + // Start out with the simulated device disconnected + USBSimulationManager::disconnectGadget(); + + // Tell the simulated device which port to open + USBCDC_SetPortName(USBSimulationManager::localGadgetPortDevice().toUtf8().constData()); + + // Run the programmer thread. Note: this is crude -- I just leave it as a dangling object + // with no owner. This seems to be the best way to terminate the thread when closing the + // program though. If I intentionally try to stop the thread, it doesn't work properly. + ProgrammerThread *programmer = new ProgrammerThread(); + programmer->start(); + + // Now we can initialize the system + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/hal/pc/mainwindow.cpp b/hal/pc/mainwindow.cpp new file mode 100644 index 0000000..e7d0b37 --- /dev/null +++ b/hal/pc/mainwindow.cpp @@ -0,0 +1,66 @@ +/* + * mainwindow.cpp + * + * Created on: Jul 25, 2021 + * Author: Doug + * + * Copyright (C) 2011-2021 Doug Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "usbsimulationmanager.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::on_connectButton_clicked() +{ + if (ui->connectButton->text() == "Connect") + { + if (USBSimulationManager::connectGadget()) + { + ui->connectButton->setText("Disconnect"); + } + else + { + // TODO: error + } + } + else + { + if (USBSimulationManager::disconnectGadget()) + { + // TODO: reset simulator state so when we reconnect it's all fresh + ui->connectButton->setText("Connect"); + } + else + { + // TODO: error + } + } +} diff --git a/hal/pc/mainwindow.h b/hal/pc/mainwindow.h new file mode 100644 index 0000000..e9519e1 --- /dev/null +++ b/hal/pc/mainwindow.h @@ -0,0 +1,49 @@ +/* + * mainwindow.h + * + * Created on: Jul 25, 2021 + * Author: Doug + * + * Copyright (C) 2011-2021 Doug Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_connectButton_clicked(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H diff --git a/hal/pc/mainwindow.ui b/hal/pc/mainwindow.ui new file mode 100644 index 0000000..6020df9 --- /dev/null +++ b/hal/pc/mainwindow.ui @@ -0,0 +1,45 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 30 + 20 + 99 + 27 + + + + Connect + + + + + + + 0 + 0 + 800 + 25 + + + + + + + + diff --git a/hal/pc/pc_options.cmake b/hal/pc/pc_options.cmake index 45925ff..90e7b07 100644 --- a/hal/pc/pc_options.cmake +++ b/hal/pc/pc_options.cmake @@ -2,3 +2,12 @@ target_include_directories(SIMMProgrammer.elf PRIVATE hal/pc ) + +# PC-specific compile options +target_compile_definitions(SIMMProgrammer.elf PRIVATE + IS_PC_BUILD=1 +) +set_property(TARGET SIMMProgrammer.elf PROPERTY CMAKE_CXX_STANDARD 11) + +# Link against Qt +target_link_libraries(SIMMProgrammer.elf PRIVATE Qt5::Widgets Qt5::Concurrent) diff --git a/hal/pc/pc_sources.cmake b/hal/pc/pc_sources.cmake index 1380db4..fb58012 100644 --- a/hal/pc/pc_sources.cmake +++ b/hal/pc/pc_sources.cmake @@ -2,7 +2,18 @@ set(HWSOURCES hal/pc/board.c hal/pc/gpio.c + hal/pc/main.cpp + hal/pc/mainwindow.cpp + hal/pc/mainwindow.ui hal/pc/parallel_bus.c + hal/pc/programmerthread.cpp hal/pc/spi.c hal/pc/usbcdc.c + hal/pc/usbsimulationmanager.cpp ) + +# Use Qt on the PC build to create a nice GUI +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) +find_package(Qt5 COMPONENTS Widgets Concurrent REQUIRED) diff --git a/hal/pc/programmerthread.cpp b/hal/pc/programmerthread.cpp new file mode 100644 index 0000000..43bb107 --- /dev/null +++ b/hal/pc/programmerthread.cpp @@ -0,0 +1,38 @@ +/* + * programmerthread.cpp + * + * Created on: Jul 25, 2021 + * Author: Doug + * + * Copyright (C) 2011-2021 Doug Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "programmerthread.h" + +extern "C" int programmer_thread_main(void); + +ProgrammerThread::ProgrammerThread(QObject *parent) : + QThread(parent) +{ + +} + +void ProgrammerThread::run() +{ + programmer_thread_main(); +} diff --git a/hal/pc/programmerthread.h b/hal/pc/programmerthread.h new file mode 100644 index 0000000..c5cb52a --- /dev/null +++ b/hal/pc/programmerthread.h @@ -0,0 +1,40 @@ +/* + * programmerthread.h + * + * Created on: Jul 25, 2021 + * Author: Doug + * + * Copyright (C) 2011-2021 Doug Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PROGRAMMERTHREAD_H +#define PROGRAMMERTHREAD_H + +#include + +class ProgrammerThread : public QThread +{ + Q_OBJECT +public: + ProgrammerThread(QObject *parent = nullptr); + +protected: + virtual void run(); +}; + +#endif // PROGRAMMERTHREAD_H diff --git a/hal/pc/setup_usb_simulation.sh b/hal/pc/setup_usb_simulation.sh new file mode 100755 index 0000000..d957076 --- /dev/null +++ b/hal/pc/setup_usb_simulation.sh @@ -0,0 +1,170 @@ +#!/bin/sh + +F_ACM_MODULE_SYSFS_PATH=/sys/module/usb_f_acm +DUMMY_HCD_MODULE_SYSFS_PATH=/sys/module/dummy_hcd +SIMMPROGRAMMER_VID=0x16d0 +SIMMPROGRAMMER_PID=0x06aa + +find_configfs_dir() { + # Now grab the current configfs location + FOUND_DIR=$(mount | grep -m 1 "^configfs on" | sed "s/^configfs on \(.*\) type .*$/\1/") + if [ ! -d "$FOUND_DIR" ]; then + echo "Error: Something strange happened while trying to locate the configfs mountpoint. You should make sure you have configfs mounted in the usual location (/sys/kernel/config)." >&2 + exit 1 + fi + + echo $FOUND_DIR +} + +start_gadget() { + # If the acm module isn't loaded, try loading it manually + if [ ! -d $F_ACM_MODULE_SYSFS_PATH ]; then + modprobe usb_f_acm + if [ $? -ne 0 ]; then + echo "Error: Unable to load usb_f_acm kernel module." >&2 + exit 1 + fi + fi + + # Same with dummy_hcd + if [ ! -d $DUMMY_HCD_MODULE_SYSFS_PATH ]; then + modprobe dummy_hcd + if [ $? -ne 0 ]; then + echo "Error: Unable to load dummy_hcd kernel module." >&2 + echo "Note: Many Linux distributions don't come with this module by default. You may need to compile it yourself. See the README file for more details." >&2 + exit 1 + fi + fi + + # Make sure we have a dummy_udc to use + if [ ! -d /sys/class/udc/dummy_udc.0 ]; then + echo "Error: Can't find a dummy UDC device to use in /sys/class/udc" >&2 + exit 1 + fi + + # locate configfs, mount it if it's not already loaded + CONFIGFS_LINE_COUNT=$(mount | grep -c "^configfs on ") + if [ $CONFIGFS_LINE_COUNT -lt 1 ]; then + # Try to mount it ourselves if we couldn't + mount -t configfs configfs /sys/kernel/config + fi + + # Check one more time + CONFIGFS_LINE_COUNT=$(mount | grep -c "^configfs on ") + if [ $CONFIGFS_LINE_COUNT -lt 1 ]; then + echo "Error: Unable to find configfs. Is your kernel configured with configfs support?" >&2 + exit 1 + fi + + # Now grab the current configfs location + CONFIGFS_DIR=$(find_configfs_dir) + + # Check if we have the usb_gadget subdir + if [ ! -d "$CONFIGFS_DIR/usb_gadget" ]; then + echo "Error: The configfs filesystem doesn't seem to contain a usb_gadget subdirectory. Make sure your kernel is compiled with configfs support for USB gadgets." >&2 + exit 1 + fi + + # Make sure we can't find an existing gadget that's set up as us + cd "$CONFIGFS_DIR/usb_gadget" + for g in */; do + if [ -d "$g" ]; then + VID=$(cat "$g/idVendor") + PID=$(cat "$g/idProduct") + if [ $VID = $SIMMPROGRAMMER_VID -a $PID = $SIMMPROGRAMMER_PID ]; then + echo "Error: The gadget is already started" >&2 + exit 1 + fi + fi + done + + # Find the first empty gadget slot to use + COUNTER=1 + while [ -d "$CONFIGFS_DIR/usb_gadget/g$COUNTER" ]; do + COUNTER=$((COUNTER+1)) + done + NEWGADGET="g$COUNTER" + + # Create the gadget and configure it + mkdir "$CONFIGFS_DIR/usb_gadget/$NEWGADGET" + cd "$CONFIGFS_DIR/usb_gadget/$NEWGADGET" + echo 0x16d0 > idVendor + echo 0x06aa > idProduct + mkdir strings/0x0409 + echo "Doug Brown" > strings/0x0409/manufacturer + echo "Mac ROM SIMM Programmer" > strings/0x0409/product + echo "0" > strings/0x0409/serialnumber + mkdir functions/acm.GS0 + echo 0 > functions/acm.GS0/console + mkdir configs/c.1 + ln -s functions/acm.GS0 configs/c.1/ + + # Set up permissions so any user can connect/disconnect the USB device. + # This will allow us to hotplug directly inside the PC build while not root. + chmod 666 UDC + + # Don't plug it in yet; use connect_gadget for that + echo "Created dummy SIMM programmer gadget" +} + +stop_gadget() { + FOUND_ANY=false + + # Now grab the current configfs location + CONFIGFS_DIR=$(find_configfs_dir) + + # Check if we have the usb_gadget subdir + if [ -d "$CONFIGFS_DIR/usb_gadget" ]; then + # Find any existing gadget that is set up as us, and destroy it + cd "$CONFIGFS_DIR/usb_gadget" + for g in */; do + if [ -d "$g" ]; then + VID=$(cat "$g/idVendor") + PID=$(cat "$g/idProduct") + if [ $VID = $SIMMPROGRAMMER_VID -a $PID = $SIMMPROGRAMMER_PID ]; then + # Disconnect the gadget if it's already connected to a UDC + CURUDC=$(cat $g/UDC) + if [ x$CURUDC != x ]; then + echo "" > $g/UDC + fi + # Then destroy the rest of it + rm $g/configs/c.1/acm.GS0 + rmdir $g/configs/c.1 + rmdir $g/functions/acm.GS0 + rmdir $g/strings/0x0409 + rmdir $g + FOUND_ANY=true + fi + fi + done + fi + + if $FOUND_ANY; then + echo "Deleted dummy SIMM programmer gadget" + else + echo "Error: Couldn't find a dummy SIMM programmer gadget to delete." >&2 + exit 1 + fi +} + +# Make sure they supplied a command +if [ $# -ne 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# Make sure we are root +if [ $(whoami) != "root" ]; then + echo "This script has to be run as root." >&2 + exit 1 +fi + +# Parse the command and run the function for it +if [ $1 = "start" ]; then + start_gadget +elif [ $1 = "stop" ]; then + stop_gadget +else + echo "Invalid command: $1" >&2 + exit 1 +fi diff --git a/hal/pc/usbcdc.c b/hal/pc/usbcdc.c index 1f83873..2bdc10d 100644 --- a/hal/pc/usbcdc.c +++ b/hal/pc/usbcdc.c @@ -31,9 +31,11 @@ #include #include #include +#include +#include /// The emulated USB CDC serial port associated with the faked USB device -static const char *gadgetPort = "/dev/ttyGS0"; +static char gadgetPort[PATH_MAX]; /// File descriptor for the port static int gadgetfd; @@ -42,6 +44,13 @@ static int gadgetfd; */ void USBCDC_Init(void) { + // Bail if we haven't initialized the gadgetPort variable yet. + // It's the responsibility of the simulation to set that up ahead of time. + if (gadgetPort[0] == 0) + { + return; + } + // Attempt to open the device end of the USB CDC gadget. gadgetfd = open(gadgetPort, O_RDWR | O_CLOEXEC | O_NOCTTY); if (gadgetfd < 0) @@ -151,3 +160,13 @@ void USBCDC_Flush(void) { tcflush(gadgetfd, TCOFLUSH); } + +/** + * @brief Sets the port name to use for the simulated CDC serial port + * @param portName The port name (typically /dev/ttyGS0) + */ +void USBCDC_SetPortName(const char *portName) +{ + strncpy(gadgetPort, portName, sizeof(gadgetPort)); + gadgetPort[sizeof(gadgetPort) - 1] = 0; +} diff --git a/hal/pc/usbcdc_hw.h b/hal/pc/usbcdc_hw.h index bdcaf0c..89fc485 100644 --- a/hal/pc/usbcdc_hw.h +++ b/hal/pc/usbcdc_hw.h @@ -22,8 +22,8 @@ * */ -#ifndef HAL_AT90USB646_USBCDC_HW_H_ -#define HAL_AT90USB646_USBCDC_HW_H_ +#ifndef HAL_PC_USBCDC_HW_H_ +#define HAL_PC_USBCDC_HW_H_ #include "../../util.h" #include @@ -34,5 +34,6 @@ bool USBCDC_SendData(uint8_t const *data, uint16_t len); int16_t USBCDC_ReadByte(void); uint8_t USBCDC_ReadByteBlocking(void); void USBCDC_Flush(void); +void USBCDC_SetPortName(const char *portName); #endif /* HAL_AT90USB646_USBCDC_HW_H_ */ diff --git a/hal/pc/usbsimulationmanager.cpp b/hal/pc/usbsimulationmanager.cpp new file mode 100644 index 0000000..21ded62 --- /dev/null +++ b/hal/pc/usbsimulationmanager.cpp @@ -0,0 +1,179 @@ +#include "usbsimulationmanager.h" +#include + +/// File in proc to find current mountpoints +#define MOUNTS_FILE "/proc/mounts" + +/// File type to look for in mounts for configfs +#define CONFIGFS_FS_TYPE "configfs" + +/// Subdirectory in configfs for usb gadget setup +#define CONFIGFS_USB_GADGET_SUBDIR "usb_gadget" + +/// The vendor ID of the SIMM programmer, in string format matching configfs idVendor +#define SIMMPROGRAMMER_VID_STRING "0x16d0" + +/// The product ID of the SIMM programmer, in string format matching configfs idProduct +#define SIMMPROGRAMMER_PID_STRING "0x06aa" + +/** + * @brief Determines the path to configfs (if it's mounted) + * @return The path to it, or an empty string if it's not mounted + */ +QString USBSimulationManager::configFSPath() +{ + // Open up /proc/mounts + QFile mounts(MOUNTS_FILE); + if (!mounts.open(QFile::ReadOnly)) + { + return QString(); + } + + // Search for configfs as a mountpoint + QByteArray line("l"); + while (!line.isEmpty()) + { + line = mounts.readLine(); + QList components = line.split(' '); + if (components.count() >= 2 && components[0] == CONFIGFS_FS_TYPE) + { + // We found it! The second component is the mountpoint. + return QString::fromUtf8(components[1]); + } + } + + // No need to close the file, it will automatically be closed when we return. + // Return an empty string if we didn't find it + return QString(); +} + +/** + * @brief Gets the path to the gadget directory in configfs for the SIMM programmer, if it exists + * @return The path to the gadget directory, or an empty string if we can't locate it + */ +QString USBSimulationManager::localGadgetDir() +{ + QString configFSDirPath = configFSPath(); + if (!configFSDirPath.isEmpty()) + { + QDir dir(configFSDirPath); + if (dir.exists() && dir.cd(CONFIGFS_USB_GADGET_SUBDIR)) + { + foreach (QString const &g, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) + { + QByteArray vendor, product; + + QDir gDir = dir; + gDir.cd(g); + + QFile f(gDir.absoluteFilePath("idVendor")); + if (f.open(QFile::ReadOnly)) + { + vendor = f.readAll().trimmed(); + f.close(); + } + f.setFileName(gDir.absoluteFilePath("idProduct")); + if (f.open(QFile::ReadOnly)) + { + product = f.readAll().trimmed(); + f.close(); + } + + // If it matches product and vendor ID, we can assume we got it. + if (vendor == SIMMPROGRAMMER_VID_STRING && + product == SIMMPROGRAMMER_PID_STRING) + { + return gDir.absolutePath(); + } + } + } + } + // If we can't find it, bail + return QString(); +} +/* + * usbsimulationmanager.cpp + * + * Created on: Jul 25, 2021 + * Author: Doug + * + * Copyright (C) 2011-2021 Doug Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/** + * @brief Gets the path to the TTY device representing the simulated port + * @return The path, or an empty string if we can't figure it out + */ +QString USBSimulationManager::localGadgetPortDevice() +{ + QString g = localGadgetDir(); + if (!g.isEmpty()) + { + QDir gDir(g); + if (gDir.cd("functions/acm.GS0")) + { + QFile f(gDir.absoluteFilePath("port_num")); + if (f.open(QFile::ReadOnly)) + { + QByteArray number = f.readAll().trimmed(); + f.close(); + QString path = "/dev/ttyGS" + QString::fromUtf8(number); + if (QFile::exists(path)) + { + return path; + } + } + } + } + // If we can't find it, bail + return QString(); +} + +/** + * @brief Attempts to connect the simulated USB device to the host + * @return True on success, false on failure + */ +bool USBSimulationManager::connectGadget() +{ + bool success = false; + QDir gd(localGadgetDir()); + QFile controlFile(gd.absoluteFilePath("UDC")); + if (controlFile.exists() && controlFile.open(QFile::WriteOnly)) + { + success = controlFile.write("dummy_udc.0\n") > 0; + controlFile.close(); + } + return success; +} + +/** + * @brief Attempts to connect the simulated USB device to the host + * @return True on success, false on failure + */ +bool USBSimulationManager::disconnectGadget() +{ + bool success = false; + QDir gd(localGadgetDir()); + QFile controlFile(gd.absoluteFilePath("UDC")); + if (controlFile.exists() && controlFile.open(QFile::WriteOnly)) + { + success = controlFile.write("\n") > 0; + controlFile.close(); + } + return success; +} diff --git a/hal/pc/usbsimulationmanager.h b/hal/pc/usbsimulationmanager.h new file mode 100644 index 0000000..4121bc3 --- /dev/null +++ b/hal/pc/usbsimulationmanager.h @@ -0,0 +1,40 @@ +/* + * usbsimulationmanager.h + * + * Created on: Jul 25, 2021 + * Author: Doug + * + * Copyright (C) 2011-2021 Doug Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef USBSIMULATIONMANAGER_H +#define USBSIMULATIONMANAGER_H + +#include + +class USBSimulationManager +{ +public: + static QString configFSPath(); + static QString localGadgetDir(); + static QString localGadgetPortDevice(); + static bool connectGadget(); + static bool disconnectGadget(); +}; + +#endif // USBSIMULATIONMANAGER_H diff --git a/main.c b/main.c index dc85acc..1142de3 100644 --- a/main.c +++ b/main.c @@ -42,7 +42,11 @@ * * @return Never; the main loop is an infinite loop. */ +#if !IS_PC_BUILD int main(void) +#else +int programmer_thread_main(void) +#endif { DisableInterrupts(); Board_Init();