1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "codaclientapplication.h"
37 # include <qextserialport/qextserialport.h>
40 #include "tcftrkdevice.h"
41 #include <QtNetwork/QTcpSocket>
42 #include <QtCore/QFile>
43 #include <QtCore/QFileInfo>
47 static const char usageC[] =
48 "\n%1 v0.1 built "__DATE__"\n\n"
49 "Test client for Symbian CODA\n\n"
51 "%1 ping connection Note: For serial connections ONLY.\n"
52 "%1 launch [-d] connection binary uid [--] [arguments]\n"
53 "%1 install[-s] connection remote-sis-file [targetdrive]\n"
54 "%1 put [c size] connection local-file remote-file\n"
55 "%1 stat connection remote-file\n\n"
56 "'connection': address[:port] or serial-port\n\n"
58 "-d Launch: Launch under Debug control (wait for termination)\n"
59 "-c [size] Put: Chunk size in KB (default %2KB)\n"
60 "-s Install: Silent installation\n\n"
62 "UIDs take the form '0xfdaa278'. The target directory for sis-files on a\n"
63 "device typically is 'c:\\data'. CODA's default port is %3.\n\n"
65 "%1 put 192.168.0.42 test.sis c:\\data\\test.sis\n"
66 "%1 stat 192.168.0.42 c:\\data\\test.sis\n"
67 "%1 install 192.168.0.42 c:\\data\\test.sis c:\n"
68 "%1 launch 192.168.0.42 c:\\sys\\bin\\test.exe 0x34f2b\n"
69 "%1 ping /dev/ttyUSB1\n";
71 static const unsigned short defaultPort = 65029;
72 static const quint64 defaultChunkSize = 10240;
74 static inline bool isSerialPort(const QString &address)
76 return address.startsWith(QLatin1String("/dev"))
77 || address.startsWith(QLatin1String("com"), Qt::CaseInsensitive)
78 || address.startsWith(QLatin1String("tty"), Qt::CaseInsensitive)
79 || address.startsWith(QLatin1Char('\\'));
82 static inline QString fixSlashes(QString s)
84 s.replace(QLatin1Char('/'), QLatin1Char('\\'));
88 CodaClientApplication::CodaClientApplication(int &argc, char **argv) :
89 QCoreApplication(argc, argv),
94 m_installTargetDrive(QLatin1String("C:")),
95 m_installSilently(false),
98 m_putLastChunkSize(0),
99 m_putChunkSize(defaultChunkSize),
102 setApplicationName(QLatin1String("codaclient"));
105 CodaClientApplication::~CodaClientApplication()
109 QString CodaClientApplication::usage()
111 return QString::fromLatin1(usageC)
112 .arg(QCoreApplication::applicationName())
113 .arg(defaultChunkSize / 1024).arg(defaultPort);
116 static inline CodaClientApplication::Mode modeArg(const QString &a)
118 if (a == QLatin1String("launch"))
119 return CodaClientApplication::Launch;
120 if (a == QLatin1String("ping"))
121 return CodaClientApplication::Ping;
122 if (a == QLatin1String("install"))
123 return CodaClientApplication::Install;
124 if (a == QLatin1String("put"))
125 return CodaClientApplication::Put;
126 if (a == QLatin1String("stat"))
127 return CodaClientApplication::Stat;
128 return CodaClientApplication::Invalid;
131 bool CodaClientApplication::parseArgument(const QString &a, int argNumber, QString *errorMessage)
136 if (m_mode == Invalid) {
137 *errorMessage = QLatin1String("Invalid mode");
141 case 2: { // Address/port
143 const int colonPos = m_address.indexOf(':');
144 if (colonPos != -1) {
145 m_port = m_address.mid(colonPos + 1).toUInt();
146 m_address.truncate(colonPos);
153 m_launchBinary = fixSlashes(a);
156 m_installSisFile = a;
162 m_statRemoteFile = fixSlashes(a);
171 m_launchUID = a.toUInt(0, 0);
173 *errorMessage = QLatin1String("Invalid UID");
178 m_installTargetDrive = a;
181 m_putRemoteFile = fixSlashes(a);
189 if (m_mode == Launch)
190 m_launchArgs.push_back(a);
196 CodaClientApplication::ParseArgsResult CodaClientApplication::parseArguments(QString *errorMessage)
199 const QStringList args = QCoreApplication::arguments();
200 const QStringList::const_iterator cend = args.constEnd();
201 QStringList::const_iterator it = args.constBegin();
202 bool optionsEnd = false;
203 for (++it; it != cend; ++it) {
204 if (!optionsEnd && *it == QLatin1String("--")) {
208 if (!optionsEnd && it->startsWith(QLatin1Char('-')) && it->size() == 2) {
209 switch (it->at(1).toAscii()) {
214 m_launchDebug = true;
217 m_installSilently = true;
221 *errorMessage = QString::fromLatin1("Parameter missing for -c");
222 return ParseArgsError;
224 m_putChunkSize = it->toULongLong() * 1024;
225 if (!m_putChunkSize) {
226 *errorMessage = QString::fromLatin1("Invalid chunk size.");
227 return ParseArgsError;
231 *errorMessage = QString::fromLatin1("Invalid option %1").arg(*it);
232 return ParseArgsError;
235 if (!parseArgument(*it, argNumber++, errorMessage))
236 return ParseArgsError;
239 // Basic Check & init
242 return ParseArgsError;
244 if (m_address.isEmpty() || !m_launchUID || m_launchBinary.isEmpty()) {
245 *errorMessage = QString::fromLatin1("Not enough parameters for launch.");
246 return ParseInitError;
250 if (m_address.isEmpty()) {
251 *errorMessage = QString::fromLatin1("Not enough parameters for ping.");
252 return ParseInitError;
254 if (!isSerialPort(m_address)) {
255 *errorMessage = QString::fromLatin1("'ping' not supported for TCP/IP.");
256 return ParseInitError;
260 if (m_address.isEmpty() || m_installSisFile.isEmpty()) {
261 *errorMessage = QString::fromLatin1("Not enough parameters for install.");
262 return ParseInitError;
266 if (m_address.isEmpty() || m_putLocalFile.isEmpty() || m_putRemoteFile.isEmpty()) {
267 *errorMessage = QString::fromLatin1("Not enough parameters for put.");
268 return ParseInitError;
270 const QFileInfo fi(m_putLocalFile);
271 if (!fi.isFile() || !fi.isReadable()) {
272 *errorMessage = QString::fromLatin1("Local file '%1' not readable.").arg(m_putLocalFile);
273 return ParseInitError;
283 bool CodaClientApplication::start()
290 const QString args = m_launchArgs.join(QString(QLatin1Char(' ')));
291 std::printf("Launching 0x%x '%s '%s' (debug: %d)\n",
292 m_launchUID, qPrintable(m_launchBinary),
293 qPrintable(args), m_launchDebug);
297 std::printf("Installing '%s' to '%s'\n",
298 qPrintable(m_installSisFile), qPrintable(m_installTargetDrive));
301 std::printf("Copying '%s' to '%s' in chunks of %lluKB\n",
302 qPrintable(m_putLocalFile), qPrintable(m_putRemoteFile),
303 m_putChunkSize / 1024);
306 std::printf("Retrieving attributes of '%s'\n", qPrintable(m_statRemoteFile));
312 m_trkDevice.reset(new tcftrk::TcfTrkDevice);
313 m_trkDevice->setVerbose(m_verbose);
314 connect(m_trkDevice.data(), SIGNAL(error(QString)),
315 this, SLOT(slotError(QString)));
316 connect(m_trkDevice.data(), SIGNAL(logMessage(QString)),
317 this, SLOT(slotTrkLogMessage(QString)));
318 connect(m_trkDevice.data(), SIGNAL(tcfEvent(tcftrk::TcfTrkEvent)),
319 this, SLOT(slotTcftrkEvent(tcftrk::TcfTrkEvent)));
320 connect(m_trkDevice.data(), SIGNAL(serialPong(QString)),
321 this, SLOT(slotSerialPong(QString)));
322 if (isSerialPort(m_address)) {
323 #ifdef HAS_SERIALPORT
326 const QString fullPort = QextSerialPort::fullPortNameWin(m_address);
328 const QString fullPort = m_address;
330 const QSharedPointer<QextSerialPort>
331 serialPort(new QextSerialPort(fullPort, QextSerialPort::EventDriven));
332 std::printf("Opening port %s...\n", qPrintable(fullPort));
334 // Magic USB serial parameters
335 serialPort->setTimeout(2000);
336 serialPort->setBaudRate(BAUD115200);
337 serialPort->setFlowControl(FLOW_OFF);
338 serialPort->setParity(PAR_NONE);
339 serialPort->setDataBits(DATA_8);
340 serialPort->setStopBits(STOP_1);
342 m_trkDevice->setSerialFrame(true);
343 m_trkDevice->setDevice(serialPort); // Grab all data from start
344 if (!serialPort->open(QIODevice::ReadWrite|QIODevice::Unbuffered)) {
345 std::fprintf(stderr, "Cannot open port: %s", qPrintable(serialPort->errorString()));
348 // Initiate communication
349 m_trkDevice->sendSerialPing(m_mode == Ping);
352 std::fprintf(stderr, "Not implemented\n");
357 const QSharedPointer<QTcpSocket> tcfTrkSocket(new QTcpSocket);
358 m_trkDevice->setDevice(tcfTrkSocket);
359 tcfTrkSocket->connectToHost(m_address, m_port);
360 std::printf("Connecting to %s:%hu...\n", qPrintable(m_address), m_port);
365 void CodaClientApplication::slotError(const QString &e)
367 std::fprintf(stderr, "Error: %s\n", qPrintable(e));
371 void CodaClientApplication::slotTrkLogMessage(const QString &m)
374 std::printf("%s\n", qPrintable(m));
377 void CodaClientApplication::handleCreateProcess(const tcftrk::TcfTrkCommandResult &result)
379 const bool ok = result.type == tcftrk::TcfTrkCommandResult::SuccessReply;
382 std::printf("Launch succeeded: %s\n", qPrintable(result.toString()));
386 std::fprintf(stderr, "Launch failed: %s\n", qPrintable(result.toString()));
391 void CodaClientApplication::handleFileSystemOpen(const tcftrk::TcfTrkCommandResult &result)
393 if (result.type != tcftrk::TcfTrkCommandResult::SuccessReply) {
394 std::fprintf(stderr, "Open remote file failed: %s\n", qPrintable(result.toString()));
399 if (result.values.size() < 1 || result.values.at(0).data().isEmpty()) {
400 std::fprintf(stderr, "Internal error: No filehandle obtained\n");
405 m_remoteFileHandle = result.values.at(0).data();
407 if (m_mode == Stat) {
408 m_trkDevice->sendFileSystemFstatCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemFStat),
413 m_putFile.reset(new QFile(m_putLocalFile));
414 if (!m_putFile->open(QIODevice::ReadOnly)) { // Should not fail, was checked before
415 std::fprintf(stderr, "Open local file failed: %s\n", qPrintable(m_putFile->errorString()));
422 void CodaClientApplication::putSendNextChunk()
424 // Read and send off next chunk
425 const quint64 pos = m_putFile->pos();
426 const QByteArray data = m_putFile->read(m_putChunkSize);
427 if (data.isEmpty()) {
431 m_putLastChunkSize = data.size();
433 std::printf("Writing %llu bytes to remote file '%s' at %llu\n",
435 m_remoteFileHandle.constData(), pos);
436 m_trkDevice->sendFileSystemWriteCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemWrite),
437 m_remoteFileHandle, data, unsigned(pos));
441 void CodaClientApplication::closeRemoteFile()
443 m_trkDevice->sendFileSystemCloseCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemClose),
447 void CodaClientApplication::handleFileSystemWrite(const tcftrk::TcfTrkCommandResult &result)
449 // Close remote file even if copy fails
450 m_putWriteOk = result;
452 std::fprintf(stderr, "Writing data failed: %s\n", qPrintable(result.toString()));
453 if (!m_putWriteOk || m_putLastChunkSize < m_putChunkSize) {
460 void CodaClientApplication::handleFileSystemFStat(const tcftrk::TcfTrkCommandResult &result)
462 m_statFstatOk = result.type == tcftrk::TcfTrkCommandResult::SuccessReply;
463 // Close remote file even if copy fails
465 const tcftrk::TcfTrkStatResponse statr = tcftrk::TcfTrkDevice::parseStat(result);
467 std::printf("File: %s\nSize: %llu bytes\nAccessed: %s\nModified: %s\n",
468 qPrintable(m_statRemoteFile), statr.size,
469 qPrintable(statr.accessTime.toString(Qt::LocalDate)),
470 qPrintable(statr.modTime.toString(Qt::LocalDate)));
472 std::fprintf(stderr, "FStat failed: %s\n", qPrintable(result.toString()));
477 void CodaClientApplication::handleFileSystemClose(const tcftrk::TcfTrkCommandResult &result)
479 if (result.type == tcftrk::TcfTrkCommandResult::SuccessReply) {
481 std::printf("File closed.\n");
482 const bool ok = m_mode == Put ? m_putWriteOk : m_statFstatOk;
485 std::fprintf(stderr, "File close failed: %s\n", qPrintable(result.toString()));
490 void CodaClientApplication::handleSymbianInstall(const tcftrk::TcfTrkCommandResult &result)
492 if (result.type == tcftrk::TcfTrkCommandResult::SuccessReply) {
494 std::printf("Installation succeeded\n.");
497 std::fprintf(stderr, "Installation failed: %s\n", qPrintable(result.toString()));
502 void CodaClientApplication::slotTcftrkEvent (const tcftrk::TcfTrkEvent &ev)
505 std::printf("Event: %s\n", qPrintable(ev.toString()));
507 case tcftrk::TcfTrkEvent::LocatorHello: // Commands accepted now
512 m_trkDevice->sendProcessStartCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleCreateProcess),
513 m_launchBinary, m_launchUID, m_launchArgs, QString(), m_launchDebug);
516 if (m_installSilently) {
517 m_trkDevice->sendSymbianInstallSilentInstallCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleSymbianInstall),
518 m_installSisFile.toAscii(), m_installTargetDrive.toAscii());
520 m_trkDevice->sendSymbianInstallUIInstallCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleSymbianInstall),
521 m_installSisFile.toAscii());
525 const unsigned flags =
526 tcftrk::TcfTrkDevice::FileSystem_TCF_O_WRITE
527 |tcftrk::TcfTrkDevice::FileSystem_TCF_O_CREAT
528 |tcftrk::TcfTrkDevice::FileSystem_TCF_O_TRUNC;
529 m_putWriteOk = false;
530 m_trkDevice->sendFileSystemOpenCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemOpen),
531 m_putRemoteFile.toAscii(), flags);
535 const unsigned flags = tcftrk::TcfTrkDevice::FileSystem_TCF_O_READ;
536 m_statFstatOk = false;
537 m_trkDevice->sendFileSystemOpenCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemOpen),
538 m_statRemoteFile.toAscii(), flags);
545 case tcftrk::TcfTrkEvent::RunControlModuleLoadSuspended: {
546 // Debug mode start: Continue:
547 const tcftrk::TcfTrkRunControlModuleLoadContextSuspendedEvent &me =
548 static_cast<const tcftrk::TcfTrkRunControlModuleLoadContextSuspendedEvent &>(ev);
549 if (me.info().requireResume) {
551 std::printf("Continuing...\n");
552 m_trkDevice->sendRunControlResumeCommand(tcftrk::TcfTrkCallback(), me.id());
556 case tcftrk::TcfTrkEvent::RunControlContextRemoved: // App terminated in debug mode
564 void CodaClientApplication::slotSerialPong(const QString &v)
567 std::printf("Pong from '%s'\n", qPrintable(v));
572 void CodaClientApplication::doExit(int ex)
574 if (!m_trkDevice.isNull()) {
575 const QSharedPointer<QIODevice> dev = m_trkDevice->device();
577 if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(dev.data())) {
578 if (socket->state() == QAbstractSocket::ConnectedState)
579 socket->disconnectFromHost();
587 std::printf("Exiting (%d)\n", ex);
591 void CodaClientApplication::printTimeStamp()
593 const int elapsedMS = m_startTime.elapsed();
594 const int secs = elapsedMS / 1000;
595 const int msecs = elapsedMS % 1000;
596 std::printf("%4d.%03ds: ", secs, msecs);