source: trunk/tools/runonphone/symbianutils/tcftrkdevice.cpp@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 32.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "tcftrkdevice.h"
43#include "json.h"
44
45#include <QtNetwork/QAbstractSocket>
46#include <QtCore/QDebug>
47#include <QtCore/QVector>
48#include <QtCore/QQueue>
49#include <QtCore/QTextStream>
50#include <QtCore/QDateTime>
51#include <QtCore/QFileInfo>
52
53enum { debug = 0 };
54
55static const char messageTerminatorC[] = "\003\001";
56
57namespace tcftrk {
58// ------------- TcfTrkCommandError
59
60TcfTrkCommandError::TcfTrkCommandError() : timeMS(0), code(0), alternativeCode(0)
61{
62}
63
64void TcfTrkCommandError::clear()
65{
66 timeMS = 0;
67 code = alternativeCode = 0;
68 format.clear();
69 alternativeOrganization.clear();
70}
71
72void TcfTrkCommandError::write(QTextStream &str) const
73{
74 if (timeMS) {
75 const QDateTime time(QDate(1970, 1, 1));
76 str << time.addMSecs(timeMS).toString(Qt::ISODate) << ": Error code: " << code
77 << " '" << format << '\'';
78 if (!alternativeOrganization.isEmpty())
79 str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
80 } else{
81 str << "<No error>";
82 }
83}
84
85QString TcfTrkCommandError::toString() const
86{
87 QString rc;
88 QTextStream str(&rc);
89 write(str);
90 return rc;
91}
92
93/* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
94bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
95{
96 // Parse an arbitrary hash (that could as well be a command response)
97 // and check for error elements.
98 unsigned errorKeyCount = 0;
99 clear();
100 do {
101 if (values.isEmpty() || values.front().type() != JsonValue::Object)
102 break;
103 foreach (const JsonValue &c, values.front().children()) {
104 if (c.name() == "Time") {
105 timeMS = c.data().toULongLong();
106 errorKeyCount++;
107 } else if (c.name() == "Code") {
108 code = c.data().toInt();
109 errorKeyCount++;
110 } else if (c.name() == "Format") {
111 format = c.data();
112 errorKeyCount++;
113 } else if (c.name() == "AltCode") {
114 alternativeCode = c.data().toInt();
115 errorKeyCount++;
116 } else if (c.name() == "AltOrg") {
117 alternativeOrganization = c.data();
118 errorKeyCount++;
119 }
120 }
121 } while (false);
122 const bool errorFound = errorKeyCount >= 2u; // Should be at least 'Time', 'Code'.
123 if (!errorFound)
124 clear();
125 if (debug) {
126 qDebug() << "TcfTrkCommandError::parse: Found error: " << errorFound;
127 if (!values.isEmpty())
128 qDebug() << values.front().toString();
129 }
130 return errorFound;
131}
132
133// ------------ TcfTrkCommandResult
134
135TcfTrkCommandResult::TcfTrkCommandResult(Type t) :
136 type(t), service(LocatorService)
137{
138}
139
140TcfTrkCommandResult::TcfTrkCommandResult(char typeChar, Services s,
141 const QByteArray &r,
142 const QVector<JsonValue> &v,
143 const QVariant &ck) :
144 type(FailReply), service(s), request(r), values(v), cookie(ck)
145{
146 switch (typeChar) {
147 case 'N':
148 type = FailReply;
149 break;
150 case 'P':
151 type = ProgressReply;
152 break;
153 case 'R':
154 type = commandError.parse(values) ? CommandErrorReply : SuccessReply;
155 break;
156 default:
157 qWarning("Unknown TCF reply type '%c'", typeChar);
158 }
159}
160
161QString TcfTrkCommandResult::errorString() const
162{
163 QString rc;
164 QTextStream str(&rc);
165
166 switch (type) {
167 case SuccessReply:
168 case ProgressReply:
169 str << "<No error>";
170 return rc;
171 case FailReply:
172 str << "NAK";
173 case CommandErrorReply:
174 commandError.write(str);
175 break;
176 }
177 // Append the failed command for reference
178 str << " (Command was: '";
179 QByteArray printableRequest = request;
180 printableRequest.replace('\0', '|');
181 str << printableRequest << "')";
182 return rc;
183}
184
185QString TcfTrkCommandResult::toString() const
186{
187 QString rc;
188 QTextStream str(&rc);
189 str << "Command answer ";
190 switch (type) {
191 case SuccessReply:
192 str << "[success]";
193 break;
194 case CommandErrorReply:
195 str << "[command error]";
196 break;
197 case FailReply:
198 str << "[fail (NAK)]";
199 break;
200 case ProgressReply:
201 str << "[progress]";
202 break;
203 }
204 str << ", " << values.size() << " values(s) to request: '";
205 QByteArray printableRequest = request;
206 printableRequest.replace('\0', '|');
207 str << printableRequest << "' ";
208 if (cookie.isValid())
209 str << " cookie: " << cookie.toString();
210 str << '\n';
211 for (int i = 0, count = values.size(); i < count; i++)
212 str << '#' << i << ' ' << values.at(i).toString() << '\n';
213 if (type == CommandErrorReply)
214 str << "Error: " << errorString();
215 return rc;
216}
217
218struct TcfTrkSendQueueEntry
219{
220 typedef TcfTrkDevice::MessageType MessageType;
221
222 explicit TcfTrkSendQueueEntry(MessageType mt,
223 int tok,
224 Services s,
225 const QByteArray &d,
226 const TcfTrkCallback &cb= TcfTrkCallback(),
227 const QVariant &ck = QVariant()) :
228 messageType(mt), service(s), data(d), token(tok), cookie(ck), callback(cb) {}
229
230 MessageType messageType;
231 Services service;
232 QByteArray data;
233 int token;
234 QVariant cookie;
235 TcfTrkCallback callback;
236};
237
238struct TcfTrkDevicePrivate {
239 typedef TcfTrkDevice::IODevicePtr IODevicePtr;
240 typedef QHash<int, TcfTrkSendQueueEntry> TokenWrittenMessageMap;
241
242 TcfTrkDevicePrivate();
243
244 const QByteArray m_messageTerminator;
245
246 IODevicePtr m_device;
247 unsigned m_verbose;
248 QByteArray m_readBuffer;
249 int m_token;
250 QQueue<TcfTrkSendQueueEntry> m_sendQueue;
251 TokenWrittenMessageMap m_writtenMessages;
252 QVector<QByteArray> m_registerNames;
253};
254
255TcfTrkDevicePrivate::TcfTrkDevicePrivate() :
256 m_messageTerminator(messageTerminatorC),
257 m_verbose(0), m_token(0)
258{
259}
260
261TcfTrkDevice::TcfTrkDevice(QObject *parent) :
262 QObject(parent), d(new TcfTrkDevicePrivate)
263{
264}
265
266TcfTrkDevice::~TcfTrkDevice()
267{
268 delete d;
269}
270
271QVector<QByteArray> TcfTrkDevice::registerNames() const
272{
273 return d->m_registerNames;
274}
275
276void TcfTrkDevice::setRegisterNames(const QVector<QByteArray>& n)
277{
278 d->m_registerNames = n;
279 if (d->m_verbose) {
280 QString msg;
281 QTextStream str(&msg);
282 const int count = n.size();
283 str << "Registers (" << count << "): ";
284 for (int i = 0; i < count; i++)
285 str << '#' << i << '=' << n.at(i) << ' ';
286 emitLogMessage(msg);
287 }
288}
289
290TcfTrkDevice::IODevicePtr TcfTrkDevice::device() const
291{
292 return d->m_device;
293}
294
295TcfTrkDevice::IODevicePtr TcfTrkDevice::takeDevice()
296{
297 const IODevicePtr old = d->m_device;
298 if (!old.isNull()) {
299 old.data()->disconnect(this);
300 d->m_device = IODevicePtr();
301 }
302 d->m_readBuffer.clear();
303 d->m_token = 0;
304 d->m_sendQueue.clear();
305 return old;
306}
307
308void TcfTrkDevice::setDevice(const IODevicePtr &dp)
309{
310 if (dp.data() == d->m_device.data())
311 return;
312 if (dp.isNull()) {
313 emitLogMessage(QLatin1String("Internal error: Attempt to set NULL device."));
314 return;
315 }
316 takeDevice();
317 d->m_device = dp;
318 connect(dp.data(), SIGNAL(readyRead()), this, SLOT(slotDeviceReadyRead()));
319 if (QAbstractSocket *s = qobject_cast<QAbstractSocket *>(dp.data())) {
320 connect(s, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotDeviceError()));
321 connect(s, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(slotDeviceSocketStateChanged()));
322 }
323}
324
325void TcfTrkDevice::slotDeviceError()
326{
327 const QString message = d->m_device->errorString();
328 emitLogMessage(message);
329 emit error(message);
330}
331
332void TcfTrkDevice::slotDeviceSocketStateChanged()
333{
334 if (const QAbstractSocket *s = qobject_cast<const QAbstractSocket *>(d->m_device.data())) {
335 const QAbstractSocket::SocketState st = s->state();
336 switch (st) {
337 case QAbstractSocket::UnconnectedState:
338 emitLogMessage(QLatin1String("Unconnected"));
339 break;
340 case QAbstractSocket::HostLookupState:
341 emitLogMessage(QLatin1String("HostLookupState"));
342 break;
343 case QAbstractSocket::ConnectingState:
344 emitLogMessage(QLatin1String("Connecting"));
345 break;
346 case QAbstractSocket::ConnectedState:
347 emitLogMessage(QLatin1String("Connected"));
348 break;
349 case QAbstractSocket::ClosingState:
350 emitLogMessage(QLatin1String("Closing"));
351 break;
352 default:
353 emitLogMessage(QString::fromLatin1("State %1").arg(st));
354 break;
355 }
356 }
357}
358
359static inline QString debugMessage(QByteArray message, const char *prefix = 0)
360{
361 message.replace('\0', '|');
362 const QString messageS = QString::fromLatin1(message);
363 return prefix ?
364 (QLatin1String(prefix) + messageS) : messageS;
365}
366
367void TcfTrkDevice::slotDeviceReadyRead()
368{
369 d->m_readBuffer += d->m_device->readAll();
370 // Take complete message off front of readbuffer.
371 do {
372 const int messageEndPos = d->m_readBuffer.indexOf(d->m_messageTerminator);
373 if (messageEndPos == -1)
374 break;
375 const QByteArray message = d->m_readBuffer.left(messageEndPos);
376 if (debug)
377 qDebug("Read:\n%s", qPrintable(formatData(message)));
378 if (const int errorCode = parseMessage(message)) {
379 emitLogMessage(QString::fromLatin1("Parse error %1 for: %2").arg(errorCode).arg(debugMessage(message)));
380 }
381 d->m_readBuffer.remove(0, messageEndPos + d->m_messageTerminator.size());
382 } while (!d->m_readBuffer.isEmpty());
383 checkSendQueue(); // Send off further message
384}
385
386// Split \0-terminated message into tokens, skipping the initial type character
387static inline QVector<QByteArray> splitMessage(const QByteArray &message)
388{
389 QVector<QByteArray> tokens;
390 tokens.reserve(7);
391 const int messageSize = message.size();
392 for (int pos = 2; pos < messageSize; ) {
393 const int nextPos = message.indexOf('\0', pos);
394 if (nextPos == -1)
395 break;
396 tokens.push_back(message.mid(pos, nextPos - pos));
397 pos = nextPos + 1;
398 }
399 return tokens;
400}
401
402int TcfTrkDevice::parseMessage(const QByteArray &message)
403{
404 if (d->m_verbose)
405 emitLogMessage(debugMessage(message, "TCF ->"));
406 // Special JSON parse error message or protocol format error.
407 // The port is usually closed after receiving it.
408 // "\3\2{"Time":1276096098255,"Code":3,"Format": "Protocol format error"}"
409 if (message.startsWith("\003\002")) {
410 QByteArray text = message.mid(2);
411 const QString errorMessage = QString::fromLatin1("Parse error received: %1").arg(QString::fromAscii(text));
412 emit error(errorMessage);
413 return 0;
414 }
415 if (message.size() < 4 || message.at(1) != '\0')
416 return 1;
417 // Split into tokens
418 const char type = message.at(0);
419 const QVector<QByteArray> tokens = splitMessage(message);
420 switch (type) {
421 case 'E':
422 return parseTcfEvent(tokens);
423 case 'R': // Command replies
424 case 'N':
425 case 'P':
426 return parseTcfCommandReply(type, tokens);
427 default:
428 emitLogMessage(QString::fromLatin1("Unhandled message type: %1").arg(debugMessage(message)));
429 return 756;
430 }
431 return 0;
432}
433
434int TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
435{
436 typedef TcfTrkDevicePrivate::TokenWrittenMessageMap::iterator TokenWrittenMessageMapIterator;
437 // Find the corresponding entry in the written messages hash.
438 const int tokenCount = tokens.size();
439 if (tokenCount < 1)
440 return 234;
441 bool tokenOk;
442 const int token = tokens.at(0).toInt(&tokenOk);
443 if (!tokenOk)
444 return 235;
445 const TokenWrittenMessageMapIterator it = d->m_writtenMessages.find(token);
446 if (it == d->m_writtenMessages.end()) {
447 qWarning("TcfTrkDevice: Internal error: token %d not found for '%s'",
448 token, qPrintable(joinByteArrays(tokens)));
449 return 236;
450 }
451 // No callback: remove entry from map, happy
452 if (!it.value().callback) {
453 d->m_writtenMessages.erase(it);
454 return 0;
455 }
456 // Parse values into JSON
457 QVector<JsonValue> values;
458 values.reserve(tokenCount);
459 for (int i = 1; i < tokenCount; i++) {
460 if (!tokens.at(i).isEmpty()) { // Strange: Empty tokens occur.
461 const JsonValue value(tokens.at(i));
462 if (value.isValid()) {
463 values.push_back(value);
464 } else {
465 qWarning("JSON parse error for reply to command token %d: #%d '%s'",
466 token, i, tokens.at(i).constData());
467 d->m_writtenMessages.erase(it);
468 return -1;
469 }
470 }
471 }
472
473 // Construct result and invoke callback, remove entry from map.
474 TcfTrkCallback callback = it.value().callback;
475 TcfTrkCommandResult result(type, it.value().service, it.value().data,
476 values, it.value().cookie);
477 d->m_writtenMessages.erase(it);
478 callback(result);
479 return 0;
480}
481
482static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
483
484int TcfTrkDevice::parseTcfEvent(const QVector<QByteArray> &tokens)
485{
486 // Event: Ignore the periodical heartbeat event, answer 'Hello',
487 // emit signal for the rest
488 if (tokens.size() < 3)
489 return 433;
490 const Services service = serviceFromName(tokens.at(0).constData());
491 if (service == LocatorService && tokens.at(1) == "peerHeartBeat")
492 return 0;
493 QVector<JsonValue> values;
494 for (int i = 2; i < tokens.size(); i++) {
495 const JsonValue value(tokens.at(i));
496 if (!value.isValid())
497 return 434;
498 values.push_back(value);
499 }
500 // Parse known events, emit signals
501 QScopedPointer<TcfTrkEvent> knownEvent(TcfTrkEvent::parseEvent(service, tokens.at(1), values));
502 if (!knownEvent.isNull()) {
503 // Answer hello event.
504 if (knownEvent->type() == TcfTrkEvent::LocatorHello)
505 writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
506 emit tcfEvent(*knownEvent);
507 }
508 emit genericTcfEvent(service, tokens.at(1), values);
509
510 if (debug || d->m_verbose) {
511 QString msg;
512 QTextStream str(&msg);
513 if (knownEvent.isNull()) {
514 str << "Event: " << tokens.at(0) << ' ' << tokens.at(1) << '\n';
515 foreach(const JsonValue &val, values)
516 str << " " << val.toString() << '\n';
517 } else {
518 str << knownEvent->toString();
519 }
520 emitLogMessage(msg);
521 }
522
523 return 0;
524}
525
526unsigned TcfTrkDevice::verbose() const
527{
528 return d->m_verbose;
529}
530
531void TcfTrkDevice::setVerbose(unsigned v)
532{
533 d->m_verbose = v;
534}
535
536void TcfTrkDevice::emitLogMessage(const QString &m)
537{
538 if (debug)
539 qWarning("%s", qPrintable(m));
540 emit logMessage(m);
541}
542
543bool TcfTrkDevice::checkOpen()
544{
545 if (d->m_device.isNull()) {
546 emitLogMessage(QLatin1String("Internal error: No device set on TcfTrkDevice."));
547 return false;
548 }
549 if (!d->m_device->isOpen()) {
550 emitLogMessage(QLatin1String("Internal error: Device not open in TcfTrkDevice."));
551 return false;
552 }
553 return true;
554}
555
556void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
557 const char *commandParameters, int commandParametersLength,
558 const TcfTrkCallback &callBack,
559 const QVariant &cookie)
560
561{
562 if (!checkOpen())
563 return;
564 // Format the message
565 const int token = d->m_token++;
566 QByteArray data;
567 data.reserve(30 + commandParametersLength);
568 data.append('C');
569 data.append('\0');
570 data.append(QByteArray::number(token));
571 data.append('\0');
572 data.append(serviceName(service));
573 data.append('\0');
574 data.append(command);
575 data.append('\0');
576 if (commandParametersLength)
577 data.append(commandParameters, commandParametersLength);
578 const TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie);
579 d->m_sendQueue.enqueue(entry);
580 checkSendQueue();
581}
582
583void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
584 const QByteArray &commandParameters,
585 const TcfTrkCallback &callBack,
586 const QVariant &cookie)
587{
588 sendTcfTrkMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
589 callBack, cookie);
590}
591
592// Enclose in message frame and write.
593void TcfTrkDevice::writeMessage(QByteArray data)
594{
595 if (!checkOpen())
596 return;
597
598 if (d->m_verbose)
599 emitLogMessage(debugMessage(data, "TCF <-"));
600
601 // Ensure \0-termination which easily gets lost in QByteArray CT.
602 if (!data.endsWith('\0'))
603 data.append('\0');
604 data += d->m_messageTerminator;
605
606 if (debug > 1)
607 qDebug("Writing:\n%s", qPrintable(formatData(data)));
608
609 d->m_device->write(data);
610 if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
611 as->flush();
612}
613
614void TcfTrkDevice::checkSendQueue()
615{
616 // Fire off messages or invoke noops until a message with reply is found
617 // and an entry to writtenMessages is made.
618 while (d->m_writtenMessages.empty()) {
619 if (d->m_sendQueue.isEmpty())
620 break;
621 TcfTrkSendQueueEntry entry = d->m_sendQueue.dequeue();
622 switch (entry.messageType) {
623 case MessageWithReply:
624 d->m_writtenMessages.insert(entry.token, entry);
625 writeMessage(entry.data);
626 break;
627 case MessageWithoutReply:
628 writeMessage(entry.data);
629 break;
630 case NoopMessage: // Invoke the noop-callback for synchronization
631 if (entry.callback) {
632 TcfTrkCommandResult noopResult(TcfTrkCommandResult::SuccessReply);
633 noopResult.cookie = entry.cookie;
634 entry.callback(noopResult);
635 }
636 break;
637 }
638 }
639}
640
641// Fix slashes
642static inline QString fixFileName(QString in)
643{
644 in.replace(QLatin1Char('/'), QLatin1Char('\\'));
645 return in;
646}
647
648// Start a process (consisting of a non-reply setSettings and start).
649void TcfTrkDevice::sendProcessStartCommand(const TcfTrkCallback &callBack,
650 const QString &binaryIn,
651 unsigned uid,
652 QStringList arguments,
653 QString workingDirectory,
654 bool debugControl,
655 const QStringList &additionalLibraries,
656 const QVariant &cookie)
657{
658 // Obtain the bin directory, expand by c:/sys/bin if missing
659 const QChar backSlash('\\');
660 int slashPos = binaryIn.lastIndexOf(QLatin1Char('/'));
661 if (slashPos == -1)
662 slashPos = binaryIn.lastIndexOf(backSlash);
663 const QString sysBin = QLatin1String("c:/sys/bin");
664 const QString binaryFileName = slashPos == -1 ? binaryIn : binaryIn.mid(slashPos + 1);
665 const QString binaryDirectory = slashPos == -1 ? sysBin : binaryIn.left(slashPos);
666 const QString binary = fixFileName(binaryDirectory + QLatin1Char('/') + binaryFileName);
667
668 // Fixup: Does argv[0] convention exist on Symbian?
669 arguments.push_front(binary);
670 if (workingDirectory.isEmpty())
671 workingDirectory = sysBin;
672
673 // Format settings with empty dummy parameter
674 QByteArray setData;
675 JsonInputStream setStr(setData);
676 setStr << "" << '\0'
677 << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ']'
678 << '\0' << '['
679 << binary << ','
680 << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
681 << additionalLibraries
682 << ']';
683 sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData);
684
685 QByteArray startData;
686 JsonInputStream startStr(startData);
687 startStr << fixFileName(workingDirectory)
688 << '\0' << binary << '\0' << arguments << '\0'
689 << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
690 << debugControl;
691 sendTcfTrkMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
692}
693
694void TcfTrkDevice::sendProcessTerminateCommand(const TcfTrkCallback &callBack,
695 const QByteArray &id,
696 const QVariant &cookie)
697{
698 QByteArray data;
699 JsonInputStream str(data);
700 str << id;
701 sendTcfTrkMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
702}
703
704void TcfTrkDevice::sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
705 const QByteArray &id,
706 const QVariant &cookie)
707{
708 QByteArray data;
709 JsonInputStream str(data);
710 str << id;
711 sendTcfTrkMessage(MessageWithReply, RunControlService, "terminate", data, callBack, cookie);
712}
713
714// Non-standard: Remove executable from settings
715void TcfTrkDevice::sendSettingsRemoveExecutableCommand(const QString &binaryIn,
716 unsigned uid,
717 const QStringList &additionalLibraries,
718 const QVariant &cookie)
719{
720 QByteArray setData;
721 JsonInputStream setStr(setData);
722 setStr << "" << '\0'
723 << '[' << "removedExecutables" << ',' << "removedLibraries" << ']'
724 << '\0' << '['
725 << '{' << QFileInfo(binaryIn).fileName() << ':' << QString::number(uid, 16) << '}' << ','
726 << additionalLibraries
727 << ']';
728 sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData, TcfTrkCallback(), cookie);
729}
730
731void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
732 const QByteArray &id,
733 RunControlResumeMode mode,
734 unsigned count,
735 quint64 rangeStart,
736 quint64 rangeEnd,
737 const QVariant &cookie)
738{
739 QByteArray resumeData;
740 JsonInputStream str(resumeData);
741 str << id << '\0' << int(mode) << '\0' << count;
742 switch (mode) {
743 case RM_STEP_OVER_RANGE:
744 case RM_STEP_INTO_RANGE:
745 case RM_REVERSE_STEP_OVER_RANGE:
746 case RM_REVERSE_STEP_INTO_RANGE:
747 str << '\0' << '{' << "RANGE_START" << ':' << rangeStart
748 << ',' << "RANGE_END" << ':' << rangeEnd << '}';
749 break;
750 default:
751 break;
752 }
753 sendTcfTrkMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
754}
755
756void TcfTrkDevice::sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
757 const QByteArray &id,
758 const QVariant &cookie)
759{
760 QByteArray data;
761 JsonInputStream str(data);
762 str << id;
763 sendTcfTrkMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
764}
765
766void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
767 const QByteArray &id,
768 const QVariant &cookie)
769{
770 sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
771}
772
773void TcfTrkDevice::sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
774 const Breakpoint &bp,
775 const QVariant &cookie)
776{
777 QByteArray data;
778 JsonInputStream str(data);
779 str << bp;
780 sendTcfTrkMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
781}
782
783void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
784 const QByteArray &id,
785 const QVariant &cookie)
786{
787 sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
788}
789
790void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
791 const QVector<QByteArray> &ids,
792 const QVariant &cookie)
793{
794 QByteArray data;
795 JsonInputStream str(data);
796 str << ids;
797 sendTcfTrkMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
798}
799
800void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
801 const QByteArray &id,
802 bool enable,
803 const QVariant &cookie)
804{
805 sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
806}
807
808void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
809 const QVector<QByteArray> &ids,
810 bool enable,
811 const QVariant &cookie)
812{
813 QByteArray data;
814 JsonInputStream str(data);
815 str << ids;
816 sendTcfTrkMessage(MessageWithReply, BreakpointsService,
817 enable ? "enable" : "disable",
818 data, callBack, cookie);
819}
820
821void TcfTrkDevice::sendMemorySetCommand(const TcfTrkCallback &callBack,
822 const QByteArray &contextId,
823 quint64 start, const QByteArray& data,
824 const QVariant &cookie)
825{
826 QByteArray getData;
827 JsonInputStream str(getData);
828 // start/word size/mode. Mode should ideally be 1 (continue on error?)
829 str << contextId << '\0' << start << '\0' << 1 << '\0' << data.size() << '\0' << 1
830 << '\0' << data.toBase64();
831 sendTcfTrkMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
832}
833
834void TcfTrkDevice::sendMemoryGetCommand(const TcfTrkCallback &callBack,
835 const QByteArray &contextId,
836 quint64 start, quint64 size,
837 const QVariant &cookie)
838{
839 QByteArray data;
840 JsonInputStream str(data);
841 // start/word size/mode. Mode should ideally be 1 (continue on error?)
842 str << contextId << '\0' << start << '\0' << 1 << '\0' << size << '\0' << 1;
843 sendTcfTrkMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
844}
845
846QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
847{
848 if (r.type != TcfTrkCommandResult::SuccessReply || r.values.size() < 1)
849 return QByteArray();
850 const JsonValue &memoryV = r.values.front();
851
852 if (memoryV.type() != JsonValue::String || memoryV.data().size() < 2
853 || !memoryV.data().endsWith('='))
854 return QByteArray();
855 // Catch errors reported as hash:
856 // R.4."TlVMTA==".{"Time":1276786871255,"Code":1,"AltCode":-38,"AltOrg":"POSIX","Format":"BadDescriptor"}
857 // Not sure what to make of it.
858 if (r.values.size() >= 2 && r.values.at(1).type() == JsonValue::Object)
859 qWarning("Error retrieving memory: %s", r.values.at(1).toString(false).constData());
860 // decode
861 const QByteArray memory = QByteArray::fromBase64(memoryV.data());
862 if (memory.isEmpty())
863 qWarning("Base64 decoding of %s failed.", memoryV.data().constData());
864 if (debug)
865 qDebug("TcfTrkDevice::parseMemoryGet: received %d bytes", memory.size());
866 return memory;
867}
868
869void TcfTrkDevice::sendRegistersGetMCommand(const TcfTrkCallback &callBack,
870 const QByteArray &contextId,
871 const QVector<QByteArray> &ids,
872 const QVariant &cookie)
873{
874 // TODO: use "Registers" (which uses base64-encoded values)
875 QByteArray data;
876 JsonInputStream str(data);
877 str << contextId << '\0' << ids;
878 sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "get", data, callBack, cookie);
879}
880
881void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
882 const QByteArray &contextId,
883 unsigned start, unsigned count)
884{
885 const unsigned end = start + count;
886 if (end > (unsigned)d->m_registerNames.size()) {
887 qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", end, d->m_registerNames.size());
888 return;
889 }
890
891 QVector<QByteArray> ids;
892 ids.reserve(count);
893 for (unsigned i = start; i < end; i++)
894 ids.push_back(d->m_registerNames.at(i));
895 sendRegistersGetMCommand(callBack, contextId, ids, QVariant(start));
896}
897
898// Set register
899void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
900 const QByteArray &contextId,
901 const QByteArray &id,
902 unsigned value,
903 const QVariant &cookie)
904{
905 // TODO: use "Registers" (which uses base64-encoded values)
906 QByteArray data;
907 JsonInputStream str(data);
908 str << contextId << '\0' << QVector<QByteArray>(1, id)
909 << '\0' << QVector<QByteArray>(1, QByteArray::number(value, 16));
910 sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "set", data, callBack, cookie);
911}
912
913// Set register
914void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
915 const QByteArray &contextId,
916 unsigned registerNumber,
917 unsigned value,
918 const QVariant &cookie)
919{
920 if (registerNumber >= (unsigned)d->m_registerNames.size()) {
921 qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
922 return;
923 }
924 sendRegistersSetCommand(callBack, contextId,
925 d->m_registerNames[registerNumber],
926 value, cookie);
927}
928
929} // namespace tcftrk
Note: See TracBrowser for help on using the repository browser.