mirror of
https://github.com/dougg3/mac-rom-simm-programmer.git
synced 2024-11-19 01:11:04 +00:00
Continue implementing PC build
The PC build now brings up an empty Qt window. It also runs the programmer firmware in the background in another thread. This also adds a shell script you can use to create the USB gadget.
This commit is contained in:
parent
1397a49921
commit
3fd01f77d8
@ -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
|
||||
|
95
hal/pc/main.cpp
Normal file
95
hal/pc/main.cpp
Normal file
@ -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 <QApplication>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
|
||||
/**
|
||||
* @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();
|
||||
}
|
66
hal/pc/mainwindow.cpp
Normal file
66
hal/pc/mainwindow.cpp
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
49
hal/pc/mainwindow.h
Normal file
49
hal/pc/mainwindow.h
Normal file
@ -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 <QMainWindow>
|
||||
|
||||
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
|
45
hal/pc/mainwindow.ui
Normal file
45
hal/pc/mainwindow.ui
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QPushButton" name="connectButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>20</y>
|
||||
<width>99</width>
|
||||
<height>27</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -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)
|
||||
|
@ -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)
|
||||
|
38
hal/pc/programmerthread.cpp
Normal file
38
hal/pc/programmerthread.cpp
Normal file
@ -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();
|
||||
}
|
40
hal/pc/programmerthread.h
Normal file
40
hal/pc/programmerthread.h
Normal file
@ -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 <QThread>
|
||||
|
||||
class ProgrammerThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProgrammerThread(QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
#endif // PROGRAMMERTHREAD_H
|
170
hal/pc/setup_usb_simulation.sh
Executable file
170
hal/pc/setup_usb_simulation.sh
Executable file
@ -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 <start|stop>" >&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
|
@ -31,9 +31,11 @@
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/limits.h>
|
||||
#include <string.h>
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
@ -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 <stdint.h>
|
||||
@ -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_ */
|
||||
|
179
hal/pc/usbsimulationmanager.cpp
Normal file
179
hal/pc/usbsimulationmanager.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
#include "usbsimulationmanager.h"
|
||||
#include <QDir>
|
||||
|
||||
/// 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<QByteArray> 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;
|
||||
}
|
40
hal/pc/usbsimulationmanager.h
Normal file
40
hal/pc/usbsimulationmanager.h
Normal file
@ -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 <QString>
|
||||
|
||||
class USBSimulationManager
|
||||
{
|
||||
public:
|
||||
static QString configFSPath();
|
||||
static QString localGadgetDir();
|
||||
static QString localGadgetPortDevice();
|
||||
static bool connectGadget();
|
||||
static bool disconnectGadget();
|
||||
};
|
||||
|
||||
#endif // USBSIMULATIONMANAGER_H
|
Loading…
Reference in New Issue
Block a user