You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
piklab/src/piklab-prog/cmdline.cpp

454 lines
19 KiB

/***************************************************************************
* 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 <tqtimer.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;
TQString 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, TQString(" ") + 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, " " + TQString(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, "-" + TQString(it.data()->name()) + " [" + type.label() + "]:");
TQStringList 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()
{
TQValueVector<TQString> 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\":").tqarg(_progGroup->label()));
devices = _progGroup->supportedDevices();
}
qHeapSort(devices);
TQString 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\":").tqarg(_progGroup->label()));
else log(Log::LineType::Normal, i18n("Detected ports:"));
FOR_EACH(PortType, type) {
if ( _progGroup && !_progGroup->isPortSupported(type) ) continue;
TQString s = "- " + type.label() + ":";
if ( !Port::isAvailable(type) ) {
log(Log::LineType::Normal, s + i18n(" support disabled."));
continue;
}
TQStringList 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, TQString(" %1").tqarg(type.key()));
return OK;
}
CLI::ExitCode CLI::Main::prepareCommand(const TQString &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;
TQStringList errors, warnings;
Device::Memory::WarningTypes warningTypes;
if ( !_memory->load(file.stream(), errors, warningTypes, warnings) )
return errorExit(i18n("Could not load hex file \"%1\".").tqarg(errors[0]), FILE_ERROR);
if ( warningTypes!=Device::Memory::NoWarning )
log(Log::LineType::Warning, i18n("Hex file seems incompatible with device \"%1\".").tqarg(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 TQString &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, TQString());
return ExitCode(tqApp->exec());
}
// 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 TQString &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 TQString &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\".").tqarg(_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.").tqarg(_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\").").tqarg(_hardware), NOT_SUPPORTED_ERROR);
}
return OK;
}
CLI::ExitCode CLI::Main::executeSetCommand(const TQString &property, const TQString &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\".").tqarg(value.lower()), ARG_ERROR);
}
if ( property=="hardware" ) { _hardware = value; return OK; }
if ( property=="device" || property=="processor" ) {
if ( value.isEmpty() ) {
_device = 0;
return OK;
}
TQString s = value.upper();
_device = Device::lister().data(s);
Debugger::manager->updateDevice();
if ( _device==0 ) return errorExit(i18n("Unknown device \"%1\".").tqarg(s), ARG_ERROR);
Debugger::manager->init();
return checkProgrammer();
}
if ( property=="format" ) {
if ( value.isEmpty() ) {
_format = HexBuffer::IHX32;
return OK;
}
TQString 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\".").tqarg(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\"").tqarg(property), ARG_ERROR);
}
TQString CLI::Main::executeGetCommand(const TQString &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);
TQString 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 TQString(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\"").tqarg(property));
return TQString();
}
//-----------------------------------------------------------------------------
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();
}