/*************************************************************************** * Copyright (C) 2005-2007 Nicolas Hadacek * * * * 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 # include #else # include #endif #include #include #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 \".") }, { "verify", NeedProgrammer | InputHex | NeedDevice, I18N_NOOP("Verify device memory: \"verify \".") }, { "read", NeedProgrammer | OutputHex | NeedDevice, I18N_NOOP("Read device memory: \"read \".") }, { "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 \".") }, { 0, NoCommandProperty, 0 } }; const KCmdLineOptions CLI::OPTIONS[] = { { "r", 0, 0 }, { "range ", 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; iname())); 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 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; ilabel())); 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(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(_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::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; iinit() ) 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(""); return _progGroup->name(); } if ( property=="hardware" ) { if ( !_hardware.isEmpty() ) return _hardware; if ( _progGroup==0 ) return i18n(""); Port::Description pd = static_cast(Programmer::manager)->portDescription(); ::Hardware::Config *config = _progGroup->hardwareConfig(); if (config) return config->currentHardware(pd.type) + " " + i18n(""); delete config; return i18n(""); } if ( property=="device" || property=="processor" ) { if ( _device==0 ) return i18n(""); return _device->name(); } if ( property=="format" ) return HexBuffer::FORMATS[_format]; if ( property=="port" ) { if ( !_port.isEmpty() ) return _port; if ( _progGroup==0 ) return i18n(""); Port::Description pd = Programmer::GroupConfig::portDescription(*_progGroup); QString s = pd.type.key(); if (pd.type.data().withDevice) s += " (" + pd.device + ")"; return s + " " + i18n(""); } if ( property=="firmware-dir" ) { if ( !_firmwareDir.isEmpty() ) return _firmwareDir.pretty(); if ( _progGroup==0 ) return i18n(""); return Programmer::GroupConfig::firmwareDirectory(*_progGroup) + " " + i18n(""); } if ( property=="target-self-powered" ) { if ( !_targetSelfPowered.isEmpty() ) return _targetSelfPowered; return QString(readConfigEntry(Programmer::Config::TargetSelfPowered).toBool() ? "true" : "false") + " " + i18n(""); } if ( property=="hex" ) { if ( !_hexUrl.isEmpty() ) return _hexUrl.pretty(); return i18n(""); } if ( property=="coff" ) { if ( !_coffUrl.isEmpty() ) return _coffUrl.pretty(); return i18n(""); } 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(); }