summaryrefslogtreecommitdiffstats
path: root/src/piklab-prog/cmdline.cpp
blob: 2ec4530a959bddf3f7d4a8ca100eb7b9fa850c38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
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();
}