summaryrefslogtreecommitdiffstats
path: root/src/piklab-prog/cmdline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/piklab-prog/cmdline.cpp')
-rw-r--r--src/piklab-prog/cmdline.cpp457
1 files changed, 457 insertions, 0 deletions
diff --git a/src/piklab-prog/cmdline.cpp b/src/piklab-prog/cmdline.cpp
new file mode 100644
index 0000000..2ec4530
--- /dev/null
+++ b/src/piklab-prog/cmdline.cpp
@@ -0,0 +1,457 @@
+/***************************************************************************
+ * Copyright (C) 2005-2007 Nicolas Hadacek <hadacek@kde.org> *
+ * *
+ * 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. *
+ ***************************************************************************/
+#include "cmdline.h"
+
+#if defined(HAVE_READLINE)
+# include <readline/readline.h>
+# include <readline/history.h>
+#else
+# include <stdio.h>
+#endif
+#include <signal.h>
+#include <qtimer.h>
+
+#include "devices/list/device_list.h"
+#include "devices/base/device_group.h"
+#include "common/global/about.h"
+#include "progs/base/prog_config.h"
+#include "progs/base/hardware_config.h"
+#include "devices/pic/pic/pic_memory.h"
+#include "devices/pic/prog/pic_prog.h"
+#include "progs/list/prog_list.h"
+#include "common/cli/cli_log.h"
+#include "cli_prog_manager.h"
+#include "cli_debug_manager.h"
+
+//-----------------------------------------------------------------------------
+const CLI::CommandData CLI::NORMAL_COMMAND_DATA[] = {
+ { "connect", NeedProgrammer | NeedDevice,
+ I18N_NOOP("Connect programmer.") },
+ { "run", NeedProgrammer | NeedDevice,
+ I18N_NOOP("Run device (release reset).") },
+ { "stop", NeedProgrammer | NeedDevice,
+ I18N_NOOP("Stop device (hold reset).") },
+ { "program", NeedProgrammer | InputHex | NeedDevice,
+ I18N_NOOP("Program device memory: \"program <hexfilename>\".") },
+ { "verify", NeedProgrammer | InputHex | NeedDevice,
+ I18N_NOOP("Verify device memory: \"verify <hexfilename>\".") },
+ { "read", NeedProgrammer | OutputHex | NeedDevice,
+ I18N_NOOP("Read device memory: \"read <hexfilename>\".") },
+ { "erase", NeedProgrammer | NeedDevice,
+ I18N_NOOP("Erase device memory.") },
+ { "blank_check", NeedProgrammer | NeedDevice,
+ I18N_NOOP("Blank check device memory.") },
+ { "upload_firmware", NeedProgrammer | InputHex,
+ I18N_NOOP("Upload firmware to programmer: \"upload_firmware <hexfilename>\".") },
+ { 0, NoCommandProperty, 0 }
+};
+
+const KCmdLineOptions CLI::OPTIONS[] = {
+ { "r", 0, 0 },
+ { "range <name>", I18N_NOOP("Memory range to operate on."), 0 },
+ { "range-list", I18N_NOOP("Return the list of memory ranges."), 0 },
+ KCmdLineLastOption
+};
+
+//-----------------------------------------------------------------------------
+const Programmer::Group *CLI::_progGroup = 0;
+const Device::Data *CLI::_device = 0;
+HexBuffer::Format CLI::_format = HexBuffer::IHX32;
+QString CLI::_port, CLI::_targetSelfPowered, CLI::_hardware;
+PURL::Directory CLI::_firmwareDir;
+PURL::Url CLI::_hexUrl, CLI::_coffUrl;
+Device::Memory *CLI::_memory = 0;
+CLI::Interactive *CLI::_interactive = 0;
+
+//-----------------------------------------------------------------------------
+CLI::ExitCode CLI::Main::formatList()
+{
+ log(Log::LineType::Normal, i18n("Supported hex file formats:"));
+ for (uint i=0; i<HexBuffer::Nb_Formats; i++)
+ log(Log::LineType::Normal, QString(" ") + HexBuffer::FORMATS[i]);
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::programmerList()
+{
+ log(Log::LineType::Normal, i18n("Supported programmers:"));
+ Programmer::Lister::ConstIterator it;
+ for (it=Programmer::lister().begin(); it!=Programmer::lister().end(); it++)
+ log(Log::LineType::Normal, " " + QString(it.data()->name()));
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::hardwareList()
+{
+ log(Log::LineType::Normal, i18n("Supported hardware configuration for programmers:"));
+ Programmer::Lister::ConstIterator it;
+ for (it=Programmer::lister().begin(); it!=Programmer::lister().end(); it++) {
+ ::Hardware::Config *config = it.data()->hardwareConfig();
+ if ( config==0 ) continue;
+ FOR_EACH(PortType, type) {
+ if ( !it.data()->isPortSupported(type) ) continue;
+ log(Log::LineType::Normal, "-" + QString(it.data()->name()) + " [" + type.label() + "]:");
+ QStringList list = config->hardwareNames(type);
+ for (uint k=0; k<uint(list.count()); k++) log(Log::LineType::Normal, " " + list[k]);
+ }
+ delete config;
+ }
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::deviceList()
+{
+ QValueVector<QString> devices;
+ if ( _progGroup==0 ) {
+ log(Log::LineType::Normal, i18n("Supported devices:"));
+ devices = Programmer::lister().supportedDevices();
+ } else {
+ log(Log::LineType::Normal, i18n("Supported devices for \"%1\":").arg(_progGroup->label()));
+ devices = _progGroup->supportedDevices();
+ }
+ qHeapSort(devices);
+ QString s;
+ for (uint i=0; i<uint(devices.count()); i++) s += " " + devices[i];
+ log(Log::LineType::Normal, s + "\n");
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::portList()
+{
+ if (_progGroup) log(Log::LineType::Normal, i18n("Detected ports supported by \"%1\":").arg(_progGroup->label()));
+ else log(Log::LineType::Normal, i18n("Detected ports:"));
+ FOR_EACH(PortType, type) {
+ if ( _progGroup && !_progGroup->isPortSupported(type) ) continue;
+ QString s = "- " + type.label() + ":";
+ if ( !Port::isAvailable(type) ) {
+ log(Log::LineType::Normal, s + i18n(" support disabled."));
+ continue;
+ }
+ QStringList list = Port::probedDeviceList(type);
+ if ( list.count()==0 ) log(Log::LineType::Normal, s + i18n(" no port detected."));
+ else {
+ log(Log::LineType::Normal, s);
+ for (uint k=0; k<uint(list.count()); k++) log(Log::LineType::Normal, " " + list[k]);
+ }
+ }
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::rangeList()
+{
+ log(Log::LineType::Normal, i18n("Memory ranges for PIC/dsPIC devices:"));
+ FOR_EACH(Pic::MemoryRangeType, type) log(Log::LineType::Normal, QString(" %1").arg(type.key()));
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::prepareCommand(const QString &command)
+{
+ const CommandData *data = findCommandData(command);
+ CommandProperties properties = static_cast<CommandProperties>(data->properties);
+ if ( _device==0 && (properties & NeedDevice) ) return errorExit(i18n("Device not specified."), ARG_ERROR);
+ if ( _progGroup==0 && (properties & NeedProgrammer) ) return errorExit(i18n("Programmer not specified."), ARG_ERROR);
+
+ if ( (properties & InputHex) || (properties & OutputHex) ) {
+ if ( _hexUrl.isEmpty() ) return errorExit(i18n("Hex filename not specified."), ARG_ERROR);
+ //if ( !_filename.isLocalFile() ) return errorExit(i18n("Only local files are supported."), ARG_ERROR);
+ PURL::File file(_hexUrl, *_view);
+ delete _memory;
+ _memory = 0;
+ if ( properties & NeedDevice ) _memory = _device->group().createMemory(*_device);
+ if ( properties & InputHex ) {
+ if (_memory) {
+ if ( !file.openForRead() ) return FILE_ERROR;
+ QStringList errors, warnings;
+ Device::Memory::WarningTypes warningTypes;
+ if ( !_memory->load(file.stream(), errors, warningTypes, warnings) )
+ return errorExit(i18n("Could not load hex file \"%1\".").arg(errors[0]), FILE_ERROR);
+ if ( warningTypes!=Device::Memory::NoWarning )
+ log(Log::LineType::Warning, i18n("Hex file seems incompatible with device \"%1\".").arg(warnings.join(" ")));
+ }
+ } else if ( properties & OutputHex ) {
+ if ( !_force && _hexUrl.exists() ) return errorExit(i18n("Output hex filename already exists."), FILE_ERROR);
+ }
+ }
+
+ return OK;
+}
+
+CLI::Main::Main()
+ : MainBase(HasForce | HasInteractiveMode)
+{
+ _range = new Device::MemoryRange;
+ Programmer::manager = new Programmer::CliManager(this);
+ Programmer::manager->setView(_view);
+ Debugger::manager = new Debugger::CliManager;
+}
+
+CLI::Main::~Main()
+{
+ delete _range;
+}
+
+CLI::ExitCode CLI::Main::list(const QString &command)
+{
+ if ( MainBase::list(command)==OK ) return OK;
+ if ( command=="format-list" ) return formatList();
+ if ( command=="programmer-list" ) return programmerList();
+ if ( command=="hardware-list" ) return hardwareList();
+ if ( command=="port-list" ) return portList();
+ if ( command=="device-list" ) return deviceList();
+ if ( command=="range-list" ) return rangeList();
+ Q_ASSERT(false);
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::prepareRun(bool &interactive)
+{
+ // argument
+ if ( _args->count()>1 ) return errorExit(i18n("Too many arguments."), ARG_ERROR);
+ if ( _args->count()==1 ) {
+ PURL::Url url(_args->url(0));
+ ExitCode code = OK;
+ if ( url.fileType()==PURL::Hex ) code = executeSetCommand("hex", url.filepath());
+ else if ( url.fileType()==PURL::Coff ) code = executeSetCommand("coff", url.filepath());
+ else return errorExit(i18n("Argument file type not recognized."), ARG_ERROR);
+ if ( code!=OK ) return code;
+ }
+
+ interactive = _args->isSet("cli");
+ if (interactive) {
+ _interactive = new Interactive(this);
+ log(Log::LineType::Normal, i18n("Interactive mode: type help for help"));
+ log(Log::LineType::Normal, QString::null);
+#if QT_VERSION<0x040000
+ return ExitCode(qApp->exec());
+#else
+ return ExitCode(QCoreApplication::exec());
+#endif
+ }
+
+ // range
+ if ( _args->isSet("range-list") ) return list("range-list");
+ ExitCode code = extractRange(_args->getOption("range"));
+ if ( code!=OK ) return code;
+
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::extractRange(const QString &range)
+{
+ delete _range;
+ _range = 0;
+ if ( !range.isEmpty() ) {
+ if ( _device==0 ) return errorExit(i18n("Cannot specify range without specifying device."), ARG_ERROR);
+ if ( _device->group().name()=="pic" ) {
+ FOR_EACH(Pic::MemoryRangeType, type) {
+ if ( range!=type.key() ) continue;
+ if ( !static_cast<const Pic::Data *>(_device)->isReadable(type) ) return errorExit(i18n("Memory range not present on this device."), ARG_ERROR);
+ _range = new Pic::MemoryRange(type);
+ break;
+ }
+ if ( _range==0 ) return errorExit(i18n("Memory range not recognized."), ARG_ERROR);
+ } else return errorExit(i18n("Memory ranges are not supported for the specified device."), ARG_ERROR);
+ } else _range = new Device::MemoryRange;
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::executeCommand(const QString &command)
+{
+ Programmer::Base *programmer = Programmer::manager->programmer();
+ if ( command=="connect" ) return (Programmer::manager->connectDevice() ? OK : EXEC_ERROR);
+ if ( command=="disconnect" ) {
+ if ( programmer==0 || programmer->state()==Programmer::NotConnected )
+ return okExit(i18n("Programmer is already disconnected."));
+ return (Programmer::manager->disconnectDevice() ? OK : EXEC_ERROR);
+ }
+ if ( command=="run" ) {
+ if ( programmer && programmer->state()==Programmer::Running ) return okExit(i18n("Programmer is already running."));
+ return (Programmer::manager->run() ? OK : EXEC_ERROR);
+ }
+ if ( command=="stop" ) {
+ if ( programmer && programmer->state()!=Programmer::Running ) return okExit(i18n("Programmer is already stopped."));
+ return (Programmer::manager->halt() ? OK : EXEC_ERROR);
+ }
+ if ( command=="step" ) {
+ if ( !_progGroup->isDebugger() ) return errorExit(i18n("Debugging is not supported for specified programmer."), NOT_SUPPORTED_ERROR);
+ if ( programmer && programmer->state()==Programmer::Running ) return (Programmer::manager->halt() ? OK : EXEC_ERROR);
+ return (Programmer::manager->step() ? OK : EXEC_ERROR);
+ }
+ if ( command=="start" ) {
+ if ( !_progGroup->isDebugger() ) return errorExit(i18n("Debugging is not supported for specified programmer."), NOT_SUPPORTED_ERROR);
+ return (Programmer::manager->restart() ? OK : EXEC_ERROR);
+ }
+ if ( command=="program" ) {
+ if ( _progGroup->isSoftware() ) return errorExit(i18n("Reading device memory not supported for specified programmer."), NOT_SUPPORTED_ERROR);
+ return (Programmer::manager->program(*_memory, *_range) ? OK : EXEC_ERROR);
+ }
+ if ( command=="verify" ) {
+ if ( _progGroup->isSoftware() )
+ return errorExit(i18n("Reading device memory not supported for specified programmer."), NOT_SUPPORTED_ERROR);
+ return (Programmer::manager->verify(*_memory, *_range) ? OK : EXEC_ERROR);
+ }
+ if ( command=="read" ) {
+ if ( _progGroup->isSoftware() )
+ return errorExit(i18n("Reading device memory not supported for specified programmer."), NOT_SUPPORTED_ERROR);
+ if ( !Programmer::manager->read(*_memory, *_range) ) return EXEC_ERROR;
+ PURL::File file(_hexUrl, *_view);
+ if ( !file.openForWrite() ) return FILE_ERROR;
+ if ( !_memory->save(file.stream(), _format) )
+ return errorExit(i18n("Error while writing file \"%1\".").arg(_hexUrl.pretty()), FILE_ERROR);
+ return OK;
+ }
+ if ( command=="erase" ) {
+ if ( _progGroup->isSoftware() )
+ return errorExit(i18n("Erasing device memory not supported for specified programmer."), NOT_SUPPORTED_ERROR);
+ return (Programmer::manager->erase(*_range) ? OK : EXEC_ERROR);
+ }
+ if ( command=="blank_check" ) {
+ if ( _progGroup->isSoftware() )
+ return errorExit(i18n("Blank-checking device memory not supported for specified programmer."), NOT_SUPPORTED_ERROR);
+ return (Programmer::manager->blankCheck(*_range) ? OK : EXEC_ERROR);
+ }
+ if ( command=="upload_firmware" ) {
+ if ( !(_progGroup->properties() & ::Programmer::CanUploadFirmware) )
+ return errorExit(i18n("Uploading firmware is not supported for the specified programmer."), NOT_SUPPORTED_ERROR);
+ if ( Programmer::manager->programmer()==0 ) Programmer::manager->createProgrammer(0); // no device specified
+ return (Programmer::manager->programmer()->uploadFirmware(_hexUrl) ? OK : EXEC_ERROR);
+ }
+ Q_ASSERT(false);
+ return EXEC_ERROR;
+}
+
+CLI::ExitCode CLI::Main::checkProgrammer()
+{
+ if ( _progGroup==0 ) return OK;
+ if ( _progGroup->isSoftware() && _progGroup->supportedDevices().isEmpty() )
+ return errorExit(i18n("Please check installation of selected software debugger."), NOT_SUPPORTED_ERROR);
+ if ( _device && !_progGroup->isSupported(_device->name()) )
+ return errorExit(i18n("The selected device \"%1\" is not supported by the selected programmer.").arg(_device->name()), NOT_SUPPORTED_ERROR);
+ if ( !_hardware.isEmpty() ) {
+ ::Hardware::Config *config = _progGroup->hardwareConfig();
+ Port::Description pd = static_cast<Programmer::CliManager *>(Programmer::manager)->portDescription();
+ bool ok = (config==0 || config->hardwareNames(pd.type).contains(_hardware));
+ delete config;
+ if ( !ok ) return errorExit(i18n("The selected programmer does not supported the specified hardware configuration (\"%1\").").arg(_hardware), NOT_SUPPORTED_ERROR);
+ }
+ return OK;
+}
+
+CLI::ExitCode CLI::Main::executeSetCommand(const QString &property, const QString &value)
+{
+ if ( property=="programmer" ) {
+ _progGroup = 0;
+ if ( value.isEmpty() ) return OK;
+ _progGroup = Programmer::lister().group(value.lower());
+ if (_progGroup) return checkProgrammer();
+ return errorExit(i18n("Unknown programmer \"%1\".").arg(value.lower()), ARG_ERROR);
+ }
+ if ( property=="hardware" ) { _hardware = value; return OK; }
+ if ( property=="device" || property=="processor" ) {
+ if ( value.isEmpty() ) {
+ _device = 0;
+ return OK;
+ }
+ QString s = value.upper();
+ _device = Device::lister().data(s);
+ Debugger::manager->updateDevice();
+ if ( _device==0 ) return errorExit(i18n("Unknown device \"%1\".").arg(s), ARG_ERROR);
+ Debugger::manager->init();
+ return checkProgrammer();
+ }
+ if ( property=="format" ) {
+ if ( value.isEmpty() ) {
+ _format = HexBuffer::IHX32;
+ return OK;
+ }
+ QString s = value.lower();
+ for (uint i=0; i<HexBuffer::Nb_Formats; i++)
+ if ( s==HexBuffer::FORMATS[i] ) {
+ _format = HexBuffer::Format(i);
+ return OK;
+ }
+ return errorExit(i18n("Unknown hex file format \"%1\".").arg(s), ARG_ERROR);
+ }
+ if ( property=="port" ) { _port = value; return OK; }
+ if ( property=="firmware-dir" ) { _firmwareDir = value; return OK; }
+ if ( property=="target-self-powered" ) { _targetSelfPowered = value.lower(); return OK; }
+ if ( property=="hex" ) {
+ PURL::Url url = PURL::Url::fromPathOrUrl(value);
+ if ( url.isRelative() ) _hexUrl = PURL::Url(runDirectory(), value);
+ else _hexUrl = url;
+ return OK;
+ }
+ if ( property=="coff" ) {
+ PURL::Url url = PURL::Url::fromPathOrUrl(value);
+ if ( url.isRelative() ) _coffUrl = PURL::Url(runDirectory(), value);
+ else _coffUrl = url;
+ if ( _device && !Debugger::manager->init() ) return ARG_ERROR;
+ return OK;
+ }
+ return errorExit(i18n("Unknown property \"%1\"").arg(property), ARG_ERROR);
+}
+
+QString CLI::Main::executeGetCommand(const QString &property)
+{
+ if ( property=="programmer" ) {
+ if ( _progGroup==0 ) return i18n("<not set>");
+ return _progGroup->name();
+ }
+ if ( property=="hardware" ) {
+ if ( !_hardware.isEmpty() ) return _hardware;
+ if ( _progGroup==0 ) return i18n("<not set>");
+ Port::Description pd = static_cast<Programmer::CliManager *>(Programmer::manager)->portDescription();
+ ::Hardware::Config *config = _progGroup->hardwareConfig();
+ if (config) return config->currentHardware(pd.type) + " " + i18n("<from config>");
+ delete config;
+ return i18n("<not set>");
+ }
+ if ( property=="device" || property=="processor" ) {
+ if ( _device==0 ) return i18n("<not set>");
+ return _device->name();
+ }
+ if ( property=="format" ) return HexBuffer::FORMATS[_format];
+ if ( property=="port" ) {
+ if ( !_port.isEmpty() ) return _port;
+ if ( _progGroup==0 ) return i18n("<not set>");
+ Port::Description pd = Programmer::GroupConfig::portDescription(*_progGroup);
+ QString s = pd.type.key();
+ if (pd.type.data().withDevice) s += " (" + pd.device + ")";
+ return s + " " + i18n("<from config>");
+ }
+ if ( property=="firmware-dir" ) {
+ if ( !_firmwareDir.isEmpty() ) return _firmwareDir.pretty();
+ if ( _progGroup==0 ) return i18n("<not set>");
+ return Programmer::GroupConfig::firmwareDirectory(*_progGroup) + " " + i18n("<from config>");
+ }
+ if ( property=="target-self-powered" ) {
+ if ( !_targetSelfPowered.isEmpty() ) return _targetSelfPowered;
+ return QString(readConfigEntry(Programmer::Config::TargetSelfPowered).toBool() ? "true" : "false") + " " + i18n("<from config>");
+ }
+ if ( property=="hex" ) {
+ if ( !_hexUrl.isEmpty() ) return _hexUrl.pretty();
+ return i18n("<not set>");
+ }
+ if ( property=="coff" ) {
+ if ( !_coffUrl.isEmpty() ) return _coffUrl.pretty();
+ return i18n("<not set>");
+ }
+ log(Log::LineType::SoftError, i18n("Unknown property \"%1\"").arg(property));
+ return QString::null;
+}
+
+//-----------------------------------------------------------------------------
+int main(int argc, char **argv)
+{
+ CLI::Main main;
+ Piklab::AboutData *about = new Piklab::AboutData("piklab-prog", I18N_NOOP("Piklab Programmer Utility"), I18N_NOOP("Command-line programmer/debugger."));
+ CLI::OptionList list = main.optionList(I18N_NOOP("Hex filename for programming."));
+ Piklab::init(about, argc, argv, false, list.ptr());
+ return main.doRun();
+}