diff --git a/src/plugins/coreplugin/eventfilteringmainwindow.cpp b/src/plugins/coreplugin/eventfilteringmainwindow.cpp index 12a4e820dee67c5a378263fd3c422f894e64b82b..3b75faef2ba20cc9dd047b82bdc9226ae0c5f023 100644 --- a/src/plugins/coreplugin/eventfilteringmainwindow.cpp +++ b/src/plugins/coreplugin/eventfilteringmainwindow.cpp @@ -33,21 +33,50 @@ #include <windows.h> #endif -#include <QtDebug> +#include <QtCore/QtDebug> +#include <QtCore/QEvent> +#include <QtCore/QCoreApplication> -using namespace Core::Internal; +namespace Core { +namespace Internal { -EventFilteringMainWindow::EventFilteringMainWindow() +/* The notification signal is delayed by using a custom event + * as otherwise device removal is not detected properly + * (devices are still present in the registry. */ + +class DeviceNotifyEvent : public QEvent { +public: + explicit DeviceNotifyEvent(int id) : QEvent(static_cast<QEvent::Type>(id)) {} +}; + +EventFilteringMainWindow::EventFilteringMainWindow() : + m_deviceEventId(QEvent::registerEventType(QEvent::User + 2)) { } #ifdef Q_OS_WIN +bool EventFilteringMainWindow::event(QEvent *event) +{ + if (event->type() == m_deviceEventId) { + event->accept(); + emit deviceChange(); + return true; + } + return QMainWindow::event(event); +} + bool EventFilteringMainWindow::winEvent(MSG *msg, long *result) { if (msg->message == WM_DEVICECHANGE) { - emit deviceChange(); - *result = TRUE; + if (msg->wParam & 0x7 /* DBT_DEVNODES_CHANGED */) { + *result = TRUE; + QCoreApplication::postEvent(this, new DeviceNotifyEvent(m_deviceEventId)); + } } return false; } #endif + +} // namespace Internal +} // namespace Core + diff --git a/src/plugins/coreplugin/eventfilteringmainwindow.h b/src/plugins/coreplugin/eventfilteringmainwindow.h index f43eb7596ea66b4851e88df333543d5cecf14a79..c3b8f18418aceec4a92e66724182ed9b36c97d23 100644 --- a/src/plugins/coreplugin/eventfilteringmainwindow.h +++ b/src/plugins/coreplugin/eventfilteringmainwindow.h @@ -32,6 +32,10 @@ #include <QtGui/QMainWindow> +QT_BEGIN_NAMESPACE + +QT_END_NAMESPACE + namespace Core { namespace Internal { @@ -46,14 +50,17 @@ class EventFilteringMainWindow : public QMainWindow public: EventFilteringMainWindow(); +signals: + void deviceChange(); + #ifdef Q_OS_WIN protected: - bool winEvent(MSG *message, long *result); + virtual bool winEvent(MSG *message, long *result); + virtual bool event(QEvent *event); #endif -signals: - void deviceChange(); - +private: + const int m_deviceEventId; }; } // Internal diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index 68274489468bbdd2f9bcdba24a6e604f281f793d..f44f5761f4badcaf993f5717d6827ab27a5a0cdb 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -224,7 +224,6 @@ const char *DebuggerManager::stateName(int s) DebuggerStartParameters::DebuggerStartParameters() : attachPID(-1), useTerminal(false), - remoteChannelType(-1), toolChainType(ProjectExplorer::ToolChain::UNKNOWN), startMode(NoStartMode) {} diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index 7a884976a2eb9c69c5e2bd016e425a3a1096564d..782dec318f3e024c1094f9d5e8fc3015498f00b3 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -118,7 +118,6 @@ public: QString crashParameter; // for AttachCrashedExternal // for remote debugging QString remoteChannel; - int remoteChannelType; QString remoteArchitecture; QString symbolFileName; QString serverStartScript; diff --git a/src/plugins/debugger/gdb/s60debuggerbluetoothstarter.cpp b/src/plugins/debugger/gdb/s60debuggerbluetoothstarter.cpp index 41856d8014fc8a8eb74a6a5838f1f44be2d233a5..043f8a1aac35221449c5b6ae29eb30c1c2f95e43 100644 --- a/src/plugins/debugger/gdb/s60debuggerbluetoothstarter.cpp +++ b/src/plugins/debugger/gdb/s60debuggerbluetoothstarter.cpp @@ -31,6 +31,7 @@ #include "bluetoothlistener.h" #include "debuggermanager.h" #include "trkoptions.h" +#include "trkdevice.h" namespace Debugger { namespace Internal { @@ -51,18 +52,16 @@ trk::BluetoothListener *S60DebuggerBluetoothStarter::createListener() trk::PromptStartCommunicationResult S60DebuggerBluetoothStarter::startCommunication(const TrkDevicePtr &trkDevice, - int communicationType, QWidget *msgBoxParent, QString *errorMessage) { // Bluetooth? - if (communicationType == TrkOptions::BlueTooth) { - S60DebuggerBluetoothStarter bluetoothStarter(trkDevice); - return trk::promptStartBluetooth(bluetoothStarter, msgBoxParent, errorMessage); + if (trkDevice->serialFrame()) { + BaseCommunicationStarter serialStarter(trkDevice); + return trk::promptStartSerial(serialStarter, msgBoxParent, errorMessage); } - // Serial - BaseCommunicationStarter serialStarter(trkDevice); - return trk::promptStartSerial(serialStarter, msgBoxParent, errorMessage); + S60DebuggerBluetoothStarter bluetoothStarter(trkDevice); + return trk::promptStartBluetooth(bluetoothStarter, msgBoxParent, errorMessage); } } // namespace Internal diff --git a/src/plugins/debugger/gdb/s60debuggerbluetoothstarter.h b/src/plugins/debugger/gdb/s60debuggerbluetoothstarter.h index c97088c2452107fa6dc1ebc9e4aed100a8e38595..6a7d2cdb08ff4c3385ea0ebfcbe10f3c1cb3f5d3 100644 --- a/src/plugins/debugger/gdb/s60debuggerbluetoothstarter.h +++ b/src/plugins/debugger/gdb/s60debuggerbluetoothstarter.h @@ -47,7 +47,6 @@ class S60DebuggerBluetoothStarter : public trk::AbstractBluetoothStarter public: static trk::PromptStartCommunicationResult startCommunication(const TrkDevicePtr &trkDevice, - int communicationType, QWidget *msgBoxParent, QString *errorMessage); diff --git a/src/plugins/debugger/gdb/trkgdbadapter.cpp b/src/plugins/debugger/gdb/trkgdbadapter.cpp index 74a714599630725004f8dc329a9b7e52cfb9096f..2d2db0e15d1630acee266d10ad773b79b63ccf74 100644 --- a/src/plugins/debugger/gdb/trkgdbadapter.cpp +++ b/src/plugins/debugger/gdb/trkgdbadapter.cpp @@ -32,6 +32,7 @@ #include "launcher.h" #include "trkoptions.h" #include "trkoptionspage.h" +#include "symbiandevicemanager.h" #include "s60debuggerbluetoothstarter.h" #include "bluetoothlistener_gui.h" @@ -239,9 +240,8 @@ void Snapshot::insertMemory(const MemoryRange &range, const QByteArray &ba) TrkGdbAdapter::TrkGdbAdapter(GdbEngine *engine, const TrkOptionsPtr &options) : AbstractGdbAdapter(engine), m_options(options), - m_overrideTrkDeviceType(-1), m_running(false), - m_trkDevice(new trk::TrkDevice), + m_deviceFromSymbianDeviceManager(false), m_gdbAckMode(true), m_verbose(0) { @@ -259,16 +259,8 @@ TrkGdbAdapter::TrkGdbAdapter(GdbEngine *engine, const TrkOptionsPtr &options) : #endif m_gdbServerName = _("127.0.0.1:%1").arg(2222 + portOffset); - connect(m_trkDevice.data(), SIGNAL(messageReceived(trk::TrkResult)), - this, SLOT(handleTrkResult(trk::TrkResult))); - connect(m_trkDevice.data(), SIGNAL(error(QString)), - this, SLOT(handleTrkError(QString))); - setVerbose(theDebuggerBoolSetting(VerboseLog)); - m_trkDevice->setSerialFrame(effectiveTrkDeviceType() != TrkOptions::BlueTooth); - connect(m_trkDevice.data(), SIGNAL(logMessage(QString)), - this, SLOT(trkLogMessage(QString))); connect(theDebuggerAction(VerboseLog), SIGNAL(valueChanged(QVariant)), this, SLOT(setVerbose(QVariant))); } @@ -287,23 +279,8 @@ void TrkGdbAdapter::setVerbose(const QVariant &value) void TrkGdbAdapter::setVerbose(int verbose) { m_verbose = verbose; - m_trkDevice->setVerbose(m_verbose); -} - -QString TrkGdbAdapter::effectiveTrkDevice() const -{ - if (!m_overrideTrkDevice.isEmpty()) - return m_overrideTrkDevice; - if (m_options->mode == TrkOptions::BlueTooth) - return m_options->blueToothDevice; - return m_options->serialPort; -} - -int TrkGdbAdapter::effectiveTrkDeviceType() const -{ - if (m_overrideTrkDeviceType >= 0) - return m_overrideTrkDeviceType; - return m_options->mode; + if (!m_trkDevice.isNull()) + m_trkDevice->setVerbose(m_verbose); } void TrkGdbAdapter::trkLogMessage(const QString &msg) @@ -1726,14 +1703,66 @@ void TrkGdbAdapter::interruptInferior() sendTrkMessage(0x1a, TrkCallback(), trkInterruptMessage(), "Interrupting..."); } +void TrkGdbAdapter::trkDeviceRemoved(const SymbianUtils::SymbianDevice &dev) +{ + if (state() != DebuggerNotReady && m_deviceFromSymbianDeviceManager + && !m_trkDevice.isNull() && m_trkDevice->port() == dev.portName()) { + const QString message = QString::fromLatin1("Device '%1' has been disconnected.").arg(dev.friendlyName()); + logMessage(message); + emit adapterCrashed(message); + } +} + +bool TrkGdbAdapter::initializeDevice(const QString &remoteChannel, QString *errorMessage) +{ + m_deviceFromSymbianDeviceManager = false; + if (remoteChannel.isEmpty()) { + // Obtain device from settings page + m_trkDevice = TrkDevicePtr(new TrkDevice); + m_trkDevice->setPort(m_options->mode == TrkOptions::BlueTooth ? + m_options->blueToothDevice : m_options->serialPort); + m_trkDevice->setSerialFrame(m_options->mode != TrkOptions::BlueTooth); + } else { + // Run config: Acquire from device manager. + m_trkDevice = SymbianUtils::SymbianDeviceManager::instance()->acquireDevice(remoteChannel); + if (m_trkDevice.isNull()) { + *errorMessage = tr("Unable to acquire a device on '%1'. It appears to be in use.").arg(remoteChannel); + return false; + } + connect(SymbianUtils::SymbianDeviceManager::instance(), SIGNAL(deviceRemoved(const SymbianUtils::SymbianDevice)), + this, SLOT(trkDeviceRemoved(SymbianUtils::SymbianDevice))); + m_deviceFromSymbianDeviceManager = true; + } + connect(m_trkDevice.data(), SIGNAL(messageReceived(trk::TrkResult)), + this, SLOT(handleTrkResult(trk::TrkResult))); + connect(m_trkDevice.data(), SIGNAL(error(QString)), + this, SLOT(handleTrkError(QString))); + connect(m_trkDevice.data(), SIGNAL(logMessage(QString)), + this, SLOT(trkLogMessage(QString))); + m_trkDevice->setVerbose(m_verbose); + + // Prompt the user to start communication + const trk::PromptStartCommunicationResult src = + S60DebuggerBluetoothStarter::startCommunication(m_trkDevice, + 0, errorMessage); + switch (src) { + case trk::PromptStartCommunicationConnected: + break; + case trk::PromptStartCommunicationCanceled: + *errorMessage = tr("Canceled"); + return false; + case trk::PromptStartCommunicationError: + return false; + } + return true; +} + void TrkGdbAdapter::startAdapter() { m_snapshot.fullReset(); // Retrieve parameters const DebuggerStartParameters ¶meters = startParameters(); - m_overrideTrkDevice = parameters.remoteChannel; - m_overrideTrkDeviceType = parameters.remoteChannelType; m_remoteExecutable = parameters.executable; m_remoteArguments = parameters.processArgs; m_symbolFile = parameters.symbolFileName; @@ -1750,23 +1779,16 @@ void TrkGdbAdapter::startAdapter() setState(AdapterStarting); debugMessage(_("TRYING TO START ADAPTER")); logMessage(QLatin1String("### Starting TrkGdbAdapter")); - m_trkDevice->setPort(effectiveTrkDevice()); - m_trkDevice->setSerialFrame(effectiveTrkDeviceType() != TrkOptions::BlueTooth); + // Prompt the user to start communication QString message; - - const trk::PromptStartCommunicationResult src = - S60DebuggerBluetoothStarter::startCommunication(m_trkDevice, - effectiveTrkDeviceType(), - 0, &message); - switch (src) { - case trk::PromptStartCommunicationConnected: - break; - case trk::PromptStartCommunicationCanceled: - emit adapterStartFailed(message, QString()); - return; - case trk::PromptStartCommunicationError: - emit adapterStartFailed(message, TrkOptionsPage::settingsId()); + if (!initializeDevice(parameters.remoteChannel, &message)) { + if (message.isEmpty()) { + emit adapterStartFailed(QString(), QString()); + } else { + logMessage(message); + emit adapterStartFailed(message, TrkOptionsPage::settingsId()); + } return; } @@ -2099,7 +2121,16 @@ void TrkGdbAdapter::handleDirectStep3(const TrkResult &result) void TrkGdbAdapter::cleanup() { - m_trkDevice->close(); + if (!m_trkDevice.isNull()) { + m_trkDevice->close(); + if (m_deviceFromSymbianDeviceManager) { + m_trkDevice->disconnect(this); + SymbianUtils::SymbianDeviceManager::instance()->releaseDevice(m_trkDevice->port()); + m_deviceFromSymbianDeviceManager = false; + } + m_trkDevice = TrkDevicePtr(); + } + delete m_gdbServer; m_gdbServer = 0; } diff --git a/src/plugins/debugger/gdb/trkgdbadapter.h b/src/plugins/debugger/gdb/trkgdbadapter.h index 222955aaf0cc207a4a857b2c6b5a693c5d6aee2d..1bfe0b91170f971765678273312f9e0438f06dc1 100644 --- a/src/plugins/debugger/gdb/trkgdbadapter.h +++ b/src/plugins/debugger/gdb/trkgdbadapter.h @@ -47,6 +47,9 @@ #include <QtNetwork/QTcpServer> #include <QtNetwork/QTcpSocket> +namespace SymbianUtils { +class SymbianDevice; +} namespace Debugger { namespace Internal { @@ -160,8 +163,6 @@ signals: private: const TrkOptionsPtr m_options; - QString m_overrideTrkDevice; - int m_overrideTrkDeviceType; QString m_gdbServerName; // 127.0.0.1:(2222+uid) @@ -180,6 +181,7 @@ public: private: void startAdapter(); + bool initializeDevice(const QString &remoteChannel, QString *errorMessage); void startInferior(); void startInferiorPhase2(); void interruptInferior(); @@ -258,8 +260,10 @@ private: QByteArray trkStepRangeMessage(); QByteArray trkDeleteProcessMessage(); QByteArray trkInterruptMessage(); + Q_SLOT void trkDeviceRemoved(const SymbianUtils::SymbianDevice &); QSharedPointer<trk::TrkDevice> m_trkDevice; + bool m_deviceFromSymbianDeviceManager; QString m_adapterFailMessage; // @@ -299,9 +303,6 @@ private: QHash<int, GdbCommand> m_gdbCookieForToken; - QString effectiveTrkDevice() const; - int effectiveTrkDeviceType() const; - // Debuggee state trk::Session m_session; // global-ish data (process id, target information) Snapshot m_snapshot; // local-ish data (memory and registers) diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp index 582576b1fcb5bc6f8613103e84c6fce4ee7036ae..5493e50b3d8216e3828fd99da328ec8f6882450f 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp @@ -112,10 +112,8 @@ S60DeviceRunConfiguration::S60DeviceRunConfiguration(Target *parent, const QStri m_cachedTargetInformationValid(false), #ifdef Q_OS_WIN m_serialPortName(QLatin1String("COM5")), - m_communicationType(SymbianUtils::SerialPortCommunication), #else m_serialPortName(QLatin1String(SymbianUtils::SymbianDeviceManager::linuxBlueToothDeviceRootC) + QLatin1Char('0')), - m_communicationType(SymbianUtils::BlueToothCommunication), #endif m_signingMode(SignSelf) { @@ -127,7 +125,6 @@ S60DeviceRunConfiguration::S60DeviceRunConfiguration(Target *target, S60DeviceRu m_proFilePath(source->m_proFilePath), m_cachedTargetInformationValid(false), m_serialPortName(source->m_serialPortName), - m_communicationType(source->m_communicationType), m_signingMode(source->m_signingMode), m_customSignaturePath(source->m_customSignaturePath), m_customKeyPath(source->m_customKeyPath) @@ -202,7 +199,6 @@ QVariantMap S60DeviceRunConfiguration::toMap() const map.insert(QLatin1String(CUSTOM_SIGNATURE_PATH_KEY), m_customSignaturePath); map.insert(QLatin1String(CUSTOM_KEY_PATH_KEY), m_customKeyPath); map.insert(QLatin1String(SERIAL_PORT_NAME_KEY), m_serialPortName); - map.insert(QLatin1String(COMMUNICATION_TYPE_KEY), m_communicationType); map.insert(QLatin1String(COMMAND_LINE_ARGUMENTS_KEY), m_commandLineArguments); return map; @@ -217,7 +213,6 @@ bool S60DeviceRunConfiguration::fromMap(const QVariantMap &map) m_customSignaturePath = map.value(QLatin1String(CUSTOM_SIGNATURE_PATH_KEY)).toString(); m_customKeyPath = map.value(QLatin1String(CUSTOM_KEY_PATH_KEY)).toString(); m_serialPortName = map.value(QLatin1String(SERIAL_PORT_NAME_KEY)).toString().trimmed(); - m_communicationType = map.value(QLatin1String(COMMUNICATION_TYPE_KEY)).toInt(); m_commandLineArguments = map.value(QLatin1String(COMMAND_LINE_ARGUMENTS_KEY)).toStringList(); return RunConfiguration::fromMap(map); @@ -237,16 +232,6 @@ void S60DeviceRunConfiguration::setSerialPortName(const QString &name) emit serialPortNameChanged(); } -int S60DeviceRunConfiguration::communicationType() const -{ - return m_communicationType; -} - -void S60DeviceRunConfiguration::setCommunicationType(int t) -{ - m_communicationType = t; -} - QString S60DeviceRunConfiguration::targetName() const { const_cast<S60DeviceRunConfiguration *>(this)->updateTarget(); @@ -485,6 +470,8 @@ S60DeviceRunControlBase::S60DeviceRunControlBase(RunConfiguration *runConfigurat m_toolChain(ProjectExplorer::ToolChain::INVALID), m_makesis(new QProcess(this)), m_signsis(0), + m_releaseDeviceAfterLauncherFinish(false), + m_handleDeviceRemoval(true), m_launcher(0) { // connect for automatically reporting the "finished deploy" state to the progress manager @@ -507,7 +494,6 @@ S60DeviceRunControlBase::S60DeviceRunControlBase(RunConfiguration *runConfigurat m_toolChain = s60runConfig->toolChainType(); m_serialPortName = s60runConfig->serialPortName(); m_serialPortFriendlyName = SymbianUtils::SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName); - m_communicationType = s60runConfig->communicationType(); m_targetName = s60runConfig->targetName(); m_baseFileName = s60runConfig->basePackageFilePath(); m_commandLineArguments = s60runConfig->commandLineArguments(); @@ -553,7 +539,7 @@ S60DeviceRunControlBase::S60DeviceRunControlBase(RunConfiguration *runConfigurat m_packageFile = QFileInfo(m_packageFilePath).fileName(); if (debug) qDebug() << "S60DeviceRunControlBase" << m_targetName << ProjectExplorer::ToolChain::toolChainName(m_toolChain) - << m_serialPortName << m_communicationType << m_workingDirectory; + << m_serialPortName << m_workingDirectory; } S60DeviceRunControlBase::~S60DeviceRunControlBase() @@ -564,6 +550,11 @@ S60DeviceRunControlBase::~S60DeviceRunControlBase() } } +void S60DeviceRunControlBase::setReleaseDeviceAfterLauncherFinish(bool v) +{ + m_releaseDeviceAfterLauncherFinish = v; +} + void S60DeviceRunControlBase::start() { m_deployProgress = new QFutureInterface<void>; @@ -739,55 +730,59 @@ void S60DeviceRunControlBase::signsisProcessFinished() } } + void S60DeviceRunControlBase::startDeployment() { - m_launcher = new trk::Launcher(); - connect(m_launcher, SIGNAL(finished()), this, SLOT(launcherFinished())); - connect(m_launcher, SIGNAL(canNotConnect(QString)), this, SLOT(printConnectFailed(QString))); - connect(m_launcher, SIGNAL(copyingStarted()), this, SLOT(printCopyingNotice())); - connect(m_launcher, SIGNAL(canNotCreateFile(QString,QString)), this, SLOT(printCreateFileFailed(QString,QString))); - connect(m_launcher, SIGNAL(canNotWriteFile(QString,QString)), this, SLOT(printWriteFileFailed(QString,QString))); - connect(m_launcher, SIGNAL(canNotCloseFile(QString,QString)), this, SLOT(printCloseFileFailed(QString,QString))); - connect(m_launcher, SIGNAL(installingStarted()), this, SLOT(printInstallingNotice())); - connect(m_launcher, SIGNAL(canNotInstall(QString,QString)), this, SLOT(printInstallFailed(QString,QString))); - connect(m_launcher, SIGNAL(installingFinished()), this, SLOT(printInstallingFinished())); - connect(m_launcher, SIGNAL(copyProgress(int)), this, SLOT(printCopyProgress(int))); - connect(m_launcher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int))); - connect(m_launcher, SIGNAL(processStopped(uint,uint,uint,QString)), - this, SLOT(processStopped(uint,uint,uint,QString))); - - //TODO sisx destination and file path user definable - m_launcher->setTrkServerName(m_serialPortName); - m_launcher->setSerialFrame(m_communicationType == SymbianUtils::SerialPortCommunication); - if (!m_commandLineArguments.isEmpty()) - m_launcher->setCommandLineArgs(m_commandLineArguments); - const QString copySrc(m_baseFileName + ".sisx"); - const QString copyDst = QString("C:\\Data\\%1.sisx").arg(QFileInfo(m_baseFileName).fileName()); - const QString runFileName = QString("C:\\sys\\bin\\%1.exe").arg(m_targetName); - m_launcher->setCopyFileName(copySrc, copyDst); - m_launcher->setInstallFileName(copyDst); - initLauncher(runFileName, m_launcher); - emit addToOutputWindow(this, tr("Package: %1\nDeploying application to '%2'...").arg(lsFile(copySrc), m_serialPortFriendlyName)); QString errorMessage; - // Prompt the user to start up the Blue tooth connection - const trk::PromptStartCommunicationResult src = + bool success = false; + do { + connect(SymbianUtils::SymbianDeviceManager::instance(), SIGNAL(deviceRemoved(const SymbianUtils::SymbianDevice)), + this, SLOT(deviceRemoved(SymbianUtils::SymbianDevice))); + m_launcher = trk::Launcher::acquireFromDeviceManager(m_serialPortName, 0, &errorMessage); + if (!m_launcher) + break; + + connect(m_launcher, SIGNAL(finished()), this, SLOT(launcherFinished())); + connect(m_launcher, SIGNAL(canNotConnect(QString)), this, SLOT(printConnectFailed(QString))); + connect(m_launcher, SIGNAL(copyingStarted()), this, SLOT(printCopyingNotice())); + connect(m_launcher, SIGNAL(canNotCreateFile(QString,QString)), this, SLOT(printCreateFileFailed(QString,QString))); + connect(m_launcher, SIGNAL(canNotWriteFile(QString,QString)), this, SLOT(printWriteFileFailed(QString,QString))); + connect(m_launcher, SIGNAL(canNotCloseFile(QString,QString)), this, SLOT(printCloseFileFailed(QString,QString))); + connect(m_launcher, SIGNAL(installingStarted()), this, SLOT(printInstallingNotice())); + connect(m_launcher, SIGNAL(canNotInstall(QString,QString)), this, SLOT(printInstallFailed(QString,QString))); + connect(m_launcher, SIGNAL(installingFinished()), this, SLOT(printInstallingFinished())); + connect(m_launcher, SIGNAL(copyProgress(int)), this, SLOT(printCopyProgress(int))); + connect(m_launcher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int))); + connect(m_launcher, SIGNAL(processStopped(uint,uint,uint,QString)), + this, SLOT(processStopped(uint,uint,uint,QString))); + + //TODO sisx destination and file path user definable + if (!m_commandLineArguments.isEmpty()) + m_launcher->setCommandLineArgs(m_commandLineArguments); + const QString copySrc(m_baseFileName + QLatin1String(".sisx")); + const QString copyDst = QString::fromLatin1("C:\\Data\\%1.sisx").arg(QFileInfo(m_baseFileName).fileName()); + const QString runFileName = QString::fromLatin1("C:\\sys\\bin\\%1.exe").arg(m_targetName); + m_launcher->setCopyFileName(copySrc, copyDst); + m_launcher->setInstallFileName(copyDst); + initLauncher(runFileName, m_launcher); + emit addToOutputWindow(this, tr("Package: %1\nDeploying application to '%2'...").arg(lsFile(copySrc), m_serialPortFriendlyName)); + // Prompt the user to start up the Blue tooth connection + const trk::PromptStartCommunicationResult src = S60RunConfigBluetoothStarter::startCommunication(m_launcher->trkDevice(), - m_communicationType, 0, - &errorMessage); - switch (src) { - case trk::PromptStartCommunicationConnected: - break; - case trk::PromptStartCommunicationCanceled: - case trk::PromptStartCommunicationError: - error(this, errorMessage); - stop(); - emit finished(); - return; - }; + 0, &errorMessage); + if (src != trk::PromptStartCommunicationConnected) + break; + if (!m_launcher->startServer(&errorMessage)) { + errorMessage = tr("Could not connect to phone on port '%1': %2\n" + "Check if the phone is connected and App TRK is running.").arg(m_serialPortName, errorMessage); + break; + } + success = true; + } while (false); - if (!m_launcher->startServer(&errorMessage)) { - error(this, tr("Could not connect to phone on port '%1': %2\n" - "Check if the phone is connected and App TRK is running.").arg(m_serialPortName, errorMessage)); + if (!success) { + if (!errorMessage.isEmpty()) + error(this, errorMessage); stop(); emit finished(); } @@ -845,6 +840,10 @@ void S60DeviceRunControlBase::printInstallFailed(const QString &filename, const void S60DeviceRunControlBase::launcherFinished() { + if (m_releaseDeviceAfterLauncherFinish) { + m_handleDeviceRemoval = false; + trk::Launcher::releaseToDeviceManager(m_launcher); + } m_launcher->deleteLater(); m_launcher = 0; handleLauncherFinished(); @@ -919,6 +918,14 @@ void S60DeviceRunControlBase::printApplicationOutput(const QString &output) emit addToOutputWindowInline(this, output); } +void S60DeviceRunControlBase::deviceRemoved(const SymbianUtils::SymbianDevice &d) +{ + if (m_handleDeviceRemoval && d.portName() == m_serialPortName) { + error(this, tr("The device '%1' has been disconnected").arg(d.friendlyName())); + emit finished(); + } +} + bool S60DeviceRunControlBase::checkConfiguration(QString * /* errorMessage */, QString * /* settingsCategory */, QString * /* settingsPage */) const @@ -969,6 +976,7 @@ S60DeviceDebugRunControl::S60DeviceDebugRunControl(S60DeviceRunConfiguration *ru S60DeviceRunControlBase(runConfiguration), m_startParams(new Debugger::DebuggerStartParameters) { + setReleaseDeviceAfterLauncherFinish(true); // Debugger controls device after install Debugger::DebuggerManager *dm = Debugger::DebuggerManager::instance(); S60DeviceRunConfiguration *rc = qobject_cast<S60DeviceRunConfiguration *>(runConfiguration); QTC_ASSERT(dm && rc, return); @@ -981,7 +989,6 @@ S60DeviceDebugRunControl::S60DeviceDebugRunControl(S60DeviceRunConfiguration *ru m_startParams->remoteChannel = rc->serialPortName(); m_startParams->processArgs = rc->commandLineArguments(); - m_startParams->remoteChannelType = rc->communicationType(); m_startParams->startMode = Debugger::StartInternal; m_startParams->toolChainType = rc->toolChainType(); @@ -1018,6 +1025,8 @@ void S60DeviceDebugRunControl::initLauncher(const QString &executable, trk::Laun } launcher->addStartupActions(trk::Launcher::ActionCopyInstall); + // Avoid close/open sequence in quick succession, which may cause crashs + launcher->setCloseDevice(false); } void S60DeviceDebugRunControl::handleLauncherFinished() diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h index 05afe277a09bcf99c03a1fcdced6ccd96646f8f5..3b5699095b651726241d229fa5467a01764b0d56 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h +++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h @@ -47,6 +47,10 @@ namespace Debugger { class DebuggerStartParameters; } +namespace SymbianUtils { +class SymbianDevice; +} + namespace Qt4ProjectManager { namespace Internal { @@ -75,9 +79,6 @@ public: QString serialPortName() const; void setSerialPortName(const QString &name); - // See SymbianDeviceManager - int communicationType() const; - void setCommunicationType(int t); QString targetName() const; QString basePackageFilePath() const; @@ -126,7 +127,6 @@ private: QString m_packageTemplateFileName; bool m_cachedTargetInformationValid; QString m_serialPortName; - int m_communicationType; SigningMode m_signingMode; QString m_customSignaturePath; QString m_customKeyPath; @@ -179,9 +179,11 @@ protected: virtual bool checkConfiguration(QString *errorMessage, QString *settingsCategory, QString *settingsPage) const; + void setReleaseDeviceAfterLauncherFinish(bool); protected slots: void printApplicationOutput(const QString &output); + void deviceRemoved(const SymbianUtils::SymbianDevice &); private slots: void processStopped(uint pc, uint pid, uint tid, const QString& reason); @@ -213,7 +215,6 @@ private: ProjectExplorer::ToolChain::ToolChainType m_toolChain; QString m_serialPortName; QString m_serialPortFriendlyName; - int m_communicationType; QString m_targetName; QString m_baseFileName; QStringList m_commandLineArguments; @@ -232,7 +233,8 @@ private: QProcess *m_signsis; QString m_makesisTool; QString m_packageFile; - + bool m_releaseDeviceAfterLauncherFinish; + bool m_handleDeviceRemoval; QFutureInterface<void> *m_deployProgress; trk::Launcher *m_launcher; }; diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp index e496b174eb3e6f35172100673f49c16d37342b87..2d23a7260d4fe9d71af4ed4f9ab709fea85bb33b 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp @@ -244,7 +244,6 @@ void S60DeviceRunConfigurationWidget::setSerialPort(int index) { const SymbianUtils::SymbianDevice d = device(index); m_runConfiguration->setSerialPortName(d.portName()); - m_runConfiguration->setCommunicationType(d.type()); m_deviceInfoButton->setEnabled(index >= 0); clearDeviceInfo(); } @@ -321,17 +320,21 @@ void S60DeviceRunConfigurationWidget::updateDeviceInfo() setDeviceInfoLabel(tr("Connecting...")); // Do a launcher run with the ping protocol. Prompt to connect and // go asynchronous afterwards to pop up launch trk box if a timeout occurs. - m_infoLauncher = new trk::Launcher(trk::Launcher::ActionPingOnly, QSharedPointer<trk::TrkDevice>(), this); - connect(m_infoLauncher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int))); + QString message; const SymbianUtils::SymbianDevice commDev = currentDevice(); + m_infoLauncher = trk::Launcher::acquireFromDeviceManager(commDev.portName(), this, &message); + if (!m_infoLauncher) { + setDeviceInfoLabel(message, true); + return; + } + connect(m_infoLauncher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int))); + m_infoLauncher->setSerialFrame(commDev.type() == SymbianUtils::SerialPortCommunication); m_infoLauncher->setTrkServerName(commDev.portName()); // Prompt user - QString message; const trk::PromptStartCommunicationResult src = S60RunConfigBluetoothStarter::startCommunication(m_infoLauncher->trkDevice(), - commDev.type(), this, - &message); + this, &message); switch (src) { case trk::PromptStartCommunicationConnected: break; diff --git a/src/plugins/qt4projectmanager/qt-s60/s60runconfigbluetoothstarter.cpp b/src/plugins/qt4projectmanager/qt-s60/s60runconfigbluetoothstarter.cpp index 9bd6e2a60f2efa8b357821d424d5cb749197c3f4..e0cd4fb926fc2f6d0840bef1ce475612ef52f98f 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60runconfigbluetoothstarter.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60runconfigbluetoothstarter.cpp @@ -30,6 +30,7 @@ #include "s60runconfigbluetoothstarter.h" #include "bluetoothlistener.h" #include "symbiandevicemanager.h" +#include "trkdevice.h" #include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> @@ -53,18 +54,16 @@ trk::BluetoothListener *S60RunConfigBluetoothStarter::createListener() trk::PromptStartCommunicationResult S60RunConfigBluetoothStarter::startCommunication(const TrkDevicePtr &trkDevice, - int communicationType, QWidget *msgBoxParent, QString *errorMessage) { // Bluetooth? - if (communicationType == SymbianUtils::BlueToothCommunication) { - S60RunConfigBluetoothStarter bluetoothStarter(trkDevice); - return trk::promptStartBluetooth(bluetoothStarter, msgBoxParent, errorMessage); + if (trkDevice->serialFrame()) { + BaseCommunicationStarter serialStarter(trkDevice); + return trk::promptStartSerial(serialStarter, msgBoxParent, errorMessage); } - // Serial - BaseCommunicationStarter serialStarter(trkDevice); - return trk::promptStartSerial(serialStarter, msgBoxParent, errorMessage); + S60RunConfigBluetoothStarter bluetoothStarter(trkDevice); + return trk::promptStartBluetooth(bluetoothStarter, msgBoxParent, errorMessage); } } // namespace Internal diff --git a/src/plugins/qt4projectmanager/qt-s60/s60runconfigbluetoothstarter.h b/src/plugins/qt4projectmanager/qt-s60/s60runconfigbluetoothstarter.h index 67822af3485144e2f127e262341814bdc7735034..b293f7353672588db631dc641d5ffdfd89d46ab5 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60runconfigbluetoothstarter.h +++ b/src/plugins/qt4projectmanager/qt-s60/s60runconfigbluetoothstarter.h @@ -49,7 +49,6 @@ public: // passing on the right messages. static trk::PromptStartCommunicationResult startCommunication(const TrkDevicePtr &trkDevice, - int communicationType, QWidget *msgBoxParent, QString *errorMessage); diff --git a/src/plugins/qt4projectmanager/qt4target.cpp b/src/plugins/qt4projectmanager/qt4target.cpp index 497a9d17e901f28be0a4dcda865b0f620bbea458..11feec2c3b6ec4dc820d2f0a673fb0f3ec237eac 100644 --- a/src/plugins/qt4projectmanager/qt4target.cpp +++ b/src/plugins/qt4projectmanager/qt4target.cpp @@ -413,13 +413,7 @@ void Qt4Target::slotUpdateDeviceInformation() void Qt4Target::updateToolTipAndIcon() { - S60DeviceRunConfiguration *deviceRc(qobject_cast<S60DeviceRunConfiguration *>(activeRunConfiguration())); - if (!deviceRc) { - setToolTip(QString()); - setIcon(iconForId(id())); - } else { - QString friendlyPortName = SymbianUtils::SymbianDeviceManager::instance()->friendlyNameForPort( - deviceRc->serialPortName()); + if (const S60DeviceRunConfiguration *s60DeviceRc = qobject_cast<S60DeviceRunConfiguration *>(activeRunConfiguration())) { QPixmap pixmap(Core::Constants::TARGET_ICON_SIZE, Core::Constants::TARGET_ICON_SIZE); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); @@ -429,16 +423,25 @@ void Qt4Target::updateToolTipAndIcon() (Core::Constants::TARGET_ICON_SIZE-actualSize.height())/2, icon.pixmap(Core::Constants::TARGET_ICON_SIZE)); - if (!friendlyPortName.isEmpty()) { - // device connected - setToolTip(tr("<b>Device:</b> %1").arg(friendlyPortName)); - painter.drawPixmap(Core::Constants::TARGET_ICON_SIZE - m_connectedPixmap.width(), - m_connectedPixmap.height(), m_connectedPixmap); - } else { + const SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance(); + const int deviceIndex = sdm->findByPortName(s60DeviceRc->serialPortName()); + if (deviceIndex == -1) { setToolTip(tr("<b>Device:</b> Not connected")); painter.drawPixmap(Core::Constants::TARGET_ICON_SIZE - m_disconnectedPixmap.width(), m_disconnectedPixmap.height(), m_disconnectedPixmap); + } else { + // device connected + const SymbianUtils::SymbianDevice device = sdm->devices().at(deviceIndex); + const QString tooltip = device.additionalInformation().isEmpty() ? + tr("<b>Device:</b> %1").arg(device.friendlyName()) : + tr("<b>Device:</b> %1, %2").arg(device.friendlyName(), device.additionalInformation()); + setToolTip(tooltip); + painter.drawPixmap(Core::Constants::TARGET_ICON_SIZE - m_connectedPixmap.width(), + m_connectedPixmap.height(), m_connectedPixmap); } setIcon(QIcon(pixmap)); + return; } + setToolTip(QString()); + setIcon(iconForId(id())); } diff --git a/src/shared/symbianutils/launcher.cpp b/src/shared/symbianutils/launcher.cpp index 6f521571f528c87d9cfc46240cc15b8c9c22177d..40ad9fb650b929e3d6deeed5c47e0c4f78904765 100644 --- a/src/shared/symbianutils/launcher.cpp +++ b/src/shared/symbianutils/launcher.cpp @@ -32,6 +32,7 @@ #include "trkutils_p.h" #include "trkdevice.h" #include "bluetoothlistener.h" +#include "symbiandevicemanager.h" #include <QtCore/QTimer> #include <QtCore/QDateTime> @@ -89,11 +90,11 @@ Launcher::Launcher(Actions startupActions, { d->m_startupActions = startupActions; connect(d->m_device.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult))); - connect(this, SIGNAL(finished()), d->m_device.data(), SLOT(close())); } Launcher::~Launcher() { + emit destroyed(d->m_device->port()); logMessage("Shutting down.\n"); delete d; } @@ -202,11 +203,6 @@ bool Launcher::startServer(QString *errorMessage) } if (!d->m_device->isOpen() && !d->m_device->open(errorMessage)) return false; - if (d->m_closeDevice) { - connect(this, SIGNAL(finished()), d->m_device.data(), SLOT(close())); - } else { - disconnect(this, SIGNAL(finished()), d->m_device.data(), 0); - } setState(Connecting); // Set up the temporary 'waiting' state if we do not get immediate connection QTimer::singleShot(1000, this, SLOT(slotWaitingForTrk())); @@ -254,6 +250,13 @@ void Launcher::logMessage(const QString &msg) qDebug() << "LAUNCHER: " << qPrintable(msg); } +void Launcher::handleFinished() +{ + if (d->m_closeDevice) + d->m_device->close(); + emit finished(); +} + void Launcher::terminate() { switch (state()) { @@ -275,7 +278,7 @@ void Launcher::terminate() case Connecting: case WaitingForTrk: setState(Disconnected); - emit finished(); + handleFinished(); break; } } @@ -434,7 +437,7 @@ void Launcher::handleTrkVersion(const TrkResult &result) if (result.errorCode() || result.data.size() < 5) { if (d->m_startupActions == ActionPingOnly) { setState(Disconnected); - emit finished(); + handleFinished(); } return; } @@ -443,11 +446,13 @@ void Launcher::handleTrkVersion(const TrkResult &result) d->m_session.trkAppVersion.protocolMajor = result.data.at(3); d->m_session.trkAppVersion.protocolMinor = result.data.at(4); setState(DeviceDescriptionReceived); + const QString msg = deviceDescription(); + emit deviceDescriptionReceived(trkServerName(), msg); // Ping mode: Log & Terminate if (d->m_startupActions == ActionPingOnly) { - qWarning("%s", qPrintable(deviceDescription())); + qWarning("%s", qPrintable(msg)); setState(Disconnected); - emit finished(); + handleFinished(); } } @@ -574,7 +579,7 @@ void Launcher::handleWaitForFinished(const TrkResult &result) { logMessage(" FINISHED: " + stringFromArray(result.data)); setState(Disconnected); - emit finished(); + handleFinished(); } void Launcher::handleSupportMask(const TrkResult &result) @@ -725,4 +730,37 @@ void Launcher::resumeProcess(uint pid, uint tid) appendInt(&ba, tid, BigEndian); d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE"); } + +// Acquire a device from SymbianDeviceManager, return 0 if not available. +Launcher *Launcher::acquireFromDeviceManager(const QString &serverName, + QObject *parent, + QString *errorMessage) +{ + SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance(); + const QSharedPointer<trk::TrkDevice> device = sdm->acquireDevice(serverName); + if (device.isNull()) { + *errorMessage = tr("Unable to acquire a device for port '%1'. It appears to be in use.").arg(serverName); + return 0; + } + // Wire release signal. + Launcher *rc = new Launcher(trk::Launcher::ActionPingOnly, device, parent); + connect(rc, SIGNAL(deviceDescriptionReceived(QString,QString)), + sdm, SLOT(setAdditionalInformation(QString,QString))); + connect(rc, SIGNAL(destroyed(QString)), sdm, SLOT(releaseDevice(QString))); + return rc; +} + +// Preliminary release of device, disconnecting the signal. +void Launcher::releaseToDeviceManager(Launcher *launcher) +{ + SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance(); + // Disentangle launcher and its device, remove connection from destroyed + launcher->setCloseDevice(false); + TrkDevice *device = launcher->trkDevice().data(); + launcher->disconnect(device); + device->disconnect(launcher); + launcher->disconnect(sdm); + sdm->releaseDevice(launcher->trkServerName()); +} + } // namespace trk diff --git a/src/shared/symbianutils/launcher.h b/src/shared/symbianutils/launcher.h index b9b68e400d6c0f0f200914ad98130cd2f14f28a8..38f1bf77ce9c6f30c6b0f3550d4ee958b78bacf7 100644 --- a/src/shared/symbianutils/launcher.h +++ b/src/shared/symbianutils/launcher.h @@ -96,6 +96,15 @@ public: // becomes valid after successful execution of ActionPingOnly QString deviceDescription(unsigned verbose = 0u) const; + // Acquire a device from SymbianDeviceManager, return 0 if not available. + // The device will be released on destruction. + static Launcher *acquireFromDeviceManager(const QString &serverName, + QObject *parent, + QString *errorMessage); + // Preliminary release of device, disconnecting the signal. + static void releaseToDeviceManager(Launcher *l); + + // Create Trk message to start a process. static QByteArray startProcessMessage(const QString &executable, const QStringList &arguments); // Parse a TrkNotifyStopped message @@ -106,6 +115,7 @@ public: static QString msgStopped(uint pid, uint tid, uint address, const QString &why); signals: + void deviceDescriptionReceived(const QString &port, const QString &description); void copyingStarted(); void canNotConnect(const QString &errorMessage); void canNotCreateFile(const QString &filename, const QString &errorMessage); @@ -122,6 +132,8 @@ signals: void copyProgress(int percent); void stateChanged(int); void processStopped(uint pc, uint pid, uint tid, const QString& reason); + // Emitted by the destructor, for releasing devices of SymbianDeviceManager by name + void destroyed(const QString &serverName); public slots: void terminate(); @@ -154,6 +166,7 @@ private: void copyFileToRemote(); void installRemotePackageSilently(); void startInferiorIfNeeded(); + void handleFinished(); void logMessage(const QString &msg); void setState(State s); diff --git a/src/shared/symbianutils/symbiandevicemanager.cpp b/src/shared/symbianutils/symbiandevicemanager.cpp index 89b2b91449fda122f3f6f8ccf83d6acee452851a..6d6d7cef304cd444c357078717843ef9762d4171 100644 --- a/src/shared/symbianutils/symbiandevicemanager.cpp +++ b/src/shared/symbianutils/symbiandevicemanager.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "symbiandevicemanager.h" +#include "trkdevice.h" #include <QtCore/QSettings> #include <QtCore/QStringList> @@ -36,6 +37,7 @@ #include <QtCore/QTextStream> #include <QtCore/QSharedData> #include <QtCore/QScopedPointer> +#include <QtCore/QSignalMapper> namespace SymbianUtils { @@ -49,19 +51,51 @@ const char *SymbianDeviceManager::linuxBlueToothDeviceRootC = "/dev/rfcomm"; // ------------- SymbianDevice class SymbianDeviceData : public QSharedData { public: - SymbianDeviceData() : type(SerialPortCommunication) {} + SymbianDeviceData(); + ~SymbianDeviceData(); + + inline bool isOpen() const { return !device.isNull() && device->isOpen(); } + void forcedClose(); QString portName; QString friendlyName; QString deviceDesc; QString manufacturer; + QString additionalInformation; + DeviceCommunicationType type; + QSharedPointer<trk::TrkDevice> device; + bool deviceAcquired; }; +SymbianDeviceData::SymbianDeviceData() : + type(SerialPortCommunication), + deviceAcquired(false) +{ +} + +SymbianDeviceData::~SymbianDeviceData() +{ + forcedClose(); +} + +void SymbianDeviceData::forcedClose() +{ + // Close the device when unplugging. Should devices be in 'acquired' state, + // their owners should hit on write failures. + // Apart from the <shared item> destructor, also called by the devicemanager + // to ensure it also happens if other shared instances are still around. + if (isOpen()) { + if (deviceAcquired) + qWarning("Device on '%s' unplugged while an operation is in progress.", + qPrintable(portName)); + device->close(); + } +} + SymbianDevice::SymbianDevice(SymbianDeviceData *data) : m_data(data) { - } SymbianDevice::SymbianDevice() : @@ -84,6 +118,11 @@ SymbianDevice::~SymbianDevice() { } +void SymbianDevice::forcedClose() +{ + m_data->forcedClose(); +} + QString SymbianDevice::portName() const { return m_data->portName; @@ -94,6 +133,49 @@ QString SymbianDevice::friendlyName() const return m_data->friendlyName; } +QString SymbianDevice::additionalInformation() const +{ + return m_data->additionalInformation; +} + +void SymbianDevice::setAdditionalInformation(const QString &a) +{ + m_data->additionalInformation = a; +} + +SymbianDevice::TrkDevicePtr SymbianDevice::acquireDevice() +{ + if (debug) + qDebug() << "SymbianDevice::acquireDevice" << m_data->portName + << "acquired: " << m_data->deviceAcquired << " open: " << isOpen(); + if (isNull() || m_data->deviceAcquired) + return TrkDevicePtr(); + if (m_data->device.isNull()) { + m_data->device = TrkDevicePtr(new trk::TrkDevice); + m_data->device->setPort(m_data->portName); + m_data->device->setSerialFrame(m_data->type == SerialPortCommunication); + } + m_data->deviceAcquired = true; + return m_data->device; +} + +void SymbianDevice::releaseDevice(TrkDevicePtr *ptr /* = 0 */) +{ + if (debug) + qDebug() << "SymbianDevice::releaseDevice" << m_data->portName + << " open: " << isOpen(); + if (m_data->deviceAcquired) { + // Release if a valid pointer was passed in. + if (ptr && !ptr->isNull()) { + ptr->data()->disconnect(); + *ptr = TrkDevicePtr(); + } + m_data->deviceAcquired = false; + } else { + qWarning("Internal error: Attempt to release device that is not acquired."); + } +} + QString SymbianDevice::deviceDesc() const { return m_data->deviceDesc; @@ -111,7 +193,12 @@ DeviceCommunicationType SymbianDevice::type() const bool SymbianDevice::isNull() const { - return !m_data->portName.isEmpty(); + return m_data->portName.isEmpty(); +} + +bool SymbianDevice::isOpen() const +{ + return m_data->isOpen(); } QString SymbianDevice::toString() const @@ -146,7 +233,7 @@ int SymbianDevice::compare(const SymbianDevice &rhs) const return 0; } -QDebug operator<<(QDebug d, const SymbianDevice &cd) +SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &cd) { d.nospace() << cd.toString(); return d; @@ -154,10 +241,11 @@ QDebug operator<<(QDebug d, const SymbianDevice &cd) // ------------- SymbianDeviceManagerPrivate struct SymbianDeviceManagerPrivate { - SymbianDeviceManagerPrivate() : m_initialized(false) {} + SymbianDeviceManagerPrivate() : m_initialized(false), m_destroyReleaseMapper(0) {} bool m_initialized; SymbianDeviceManager::SymbianDeviceList m_devices; + QSignalMapper *m_destroyReleaseMapper; }; SymbianDeviceManager::SymbianDeviceManager(QObject *parent) : @@ -173,8 +261,7 @@ SymbianDeviceManager::~SymbianDeviceManager() SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const { - if (!d->m_initialized) - const_cast<SymbianDeviceManager*>(this)->update(false); + ensureInitialized(); return d->m_devices; } @@ -182,6 +269,7 @@ QString SymbianDeviceManager::toString() const { QString rc; QTextStream str(&rc); + str << d->m_devices.size() << " devices:\n"; const int count = d->m_devices.size(); for (int i = 0; i < count; i++) { str << '#' << i << ' '; @@ -191,15 +279,37 @@ QString SymbianDeviceManager::toString() const return rc; } +int SymbianDeviceManager::findByPortName(const QString &p) const +{ + ensureInitialized(); + const int count = d->m_devices.size(); + for (int i = 0; i < count; i++) + if (d->m_devices.at(i).portName() == p) + return i; + return -1; +} + QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const { - if (!d->m_initialized) - const_cast<SymbianDeviceManager*>(this)->update(false); - foreach (const SymbianDevice &device, d->m_devices) { - if (device.portName() == port) - return device.friendlyName(); - } - return QString(); + const int idx = findByPortName(port); + return idx == -1 ? QString() : d->m_devices.at(idx).friendlyName(); +} + +SymbianDeviceManager::TrkDevicePtr + SymbianDeviceManager::acquireDevice(const QString &port) +{ + ensureInitialized(); + const int idx = findByPortName(port); + if (idx == -1) { + qWarning("Attempt to acquire device '%s' that does not exist.", qPrintable(port)); + if (debug) + qDebug() << *this; + return TrkDevicePtr(); + } + const TrkDevicePtr rc = d->m_devices[idx].acquireDevice(); + if (debug) + qDebug() << "SymbianDeviceManager::acquireDevice" << port << " returns " << !rc.isNull(); + return rc; } void SymbianDeviceManager::update() @@ -207,12 +317,38 @@ void SymbianDeviceManager::update() update(true); } +void SymbianDeviceManager::releaseDevice(const QString &port) +{ + const int idx = findByPortName(port); + if (debug) + qDebug() << "SymbianDeviceManager::releaseDevice" << port << idx << sender(); + if (idx != -1) { + d->m_devices[idx].releaseDevice(); + } else { + qWarning("Attempt to release non-existing device %s.", qPrintable(port)); + } +} + +void SymbianDeviceManager::setAdditionalInformation(const QString &port, const QString &ai) +{ + const int idx = findByPortName(port); + if (idx != -1) + d->m_devices[idx].setAdditionalInformation(ai); +} + +void SymbianDeviceManager::ensureInitialized() const +{ + if (!d->m_initialized) // Flag is set in update() + const_cast<SymbianDeviceManager*>(this)->update(false); +} + void SymbianDeviceManager::update(bool emitSignals) { + static int n = 0; typedef SymbianDeviceList::iterator SymbianDeviceListIterator; if (debug) - qDebug(">SerialDeviceLister::update(%d)\n%s", int(emitSignals), + qDebug(">SerialDeviceLister::update(#%d, signals=%d)\n%s", n++, int(emitSignals), qPrintable(toString())); d->m_initialized = true; @@ -220,8 +356,11 @@ void SymbianDeviceManager::update(bool emitSignals) SymbianDeviceList newDevices = serialPorts() + blueToothDevices(); if (newDevices.size() > 1) qStableSort(newDevices.begin(), newDevices.end()); - if (d->m_devices == newDevices) // Happy, nothing changed. + if (d->m_devices == newDevices) { // Happy, nothing changed. + if (debug) + qDebug("<SerialDeviceLister::update: unchanged\n"); return; + } // Merge the lists and emit the respective added/removed signals, assuming // no one can plug a different device on the same port at the speed of lightning if (!d->m_devices.isEmpty()) { @@ -230,7 +369,8 @@ void SymbianDeviceManager::update(bool emitSignals) if (newDevices.contains(*oldIt)) { ++oldIt; } else { - const SymbianDevice toBeDeleted = *oldIt; + SymbianDevice toBeDeleted = *oldIt; + toBeDeleted.forcedClose(); oldIt = d->m_devices.erase(oldIt); if (emitSignals) emit deviceRemoved(toBeDeleted); @@ -312,7 +452,7 @@ SymbianDeviceManager *SymbianDeviceManager::instance() return symbianDeviceManager(); } -QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm) +SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm) { d.nospace() << sdm.toString(); return d; diff --git a/src/shared/symbianutils/symbiandevicemanager.h b/src/shared/symbianutils/symbiandevicemanager.h index 27305b5cb9b1725313ac84b682fbb65f5e07e9b9..08ae401f86ff29ba894df6350c78e007062deb94 100644 --- a/src/shared/symbianutils/symbiandevicemanager.h +++ b/src/shared/symbianutils/symbiandevicemanager.h @@ -34,12 +34,17 @@ #include <QtCore/QObject> #include <QtCore/QExplicitlySharedDataPointer> +#include <QtCore/QSharedPointer> QT_BEGIN_NAMESPACE class QDebug; class QTextStream; QT_END_NAMESPACE +namespace trk { + class TrkDevice; +} + namespace SymbianUtils { struct SymbianDeviceManagerPrivate; @@ -50,11 +55,16 @@ enum DeviceCommunicationType { BlueToothCommunication = 1 }; -// SymbianDevice, explicitly shared. +// SymbianDevice: Explicitly shared device data and a TrkDevice +// instance that can be acquired (exclusively) for use. +// A device removal from the manager will result in the +// device being closed. class SYMBIANUTILS_EXPORT SymbianDevice { explicit SymbianDevice(SymbianDeviceData *data); friend class SymbianDeviceManager; public: + typedef QSharedPointer<trk::TrkDevice> TrkDevicePtr; + SymbianDevice(); SymbianDevice(const SymbianDevice &rhs); SymbianDevice &operator=(const SymbianDevice &rhs); @@ -65,6 +75,17 @@ public: bool isNull() const; QString portName() const; QString friendlyName() const; + QString additionalInformation() const; + void setAdditionalInformation(const QString &); + + // Acquire: Mark the device as 'out' and return a shared pointer + // unless it is already in use by another owner. The result should not + // be passed on further. + TrkDevicePtr acquireDevice(); + // Give back a device and mark it as 'free'. + void releaseDevice(TrkDevicePtr *ptr = 0); + + bool isOpen() const; // Windows only. QString deviceDesc() const; @@ -74,10 +95,12 @@ public: QString toString() const; private: + void forcedClose(); + QExplicitlySharedDataPointer<SymbianDeviceData> m_data; }; -QDebug operator<<(QDebug d, const SymbianDevice &); +SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &); inline bool operator==(const SymbianDevice &d1, const SymbianDevice &d2) { return d1.compare(d2) == 0; } @@ -88,13 +111,15 @@ inline bool operator<(const SymbianDevice &d1, const SymbianDevice &d2) /* SymbianDeviceManager: Singleton that maintains a list of Symbian devices. * and emits change signals. - * On Windows, the update slot must be connected to a signal - * emitted from an event handler listening for WM_DEVICECHANGE. */ + * On Windows, the update slot must be connected to a [delayed] signal + * emitted from an event handler listening for WM_DEVICECHANGE. + * Device removal will result in the device being closed. */ class SYMBIANUTILS_EXPORT SymbianDeviceManager : public QObject { Q_OBJECT public: typedef QList<SymbianDevice> SymbianDeviceList; + typedef QSharedPointer<trk::TrkDevice> TrkDevicePtr; static const char *linuxBlueToothDeviceRootC; @@ -108,17 +133,25 @@ public: SymbianDeviceList devices() const; QString toString() const; + // Acquire a device for use. See releaseDevice(). + TrkDevicePtr acquireDevice(const QString &port); + + int findByPortName(const QString &p) const; QString friendlyNameForPort(const QString &port) const; public slots: void update(); + // Relase a device, make it available for further use. + void releaseDevice(const QString &port); + void setAdditionalInformation(const QString &port, const QString &ai); signals: - void deviceRemoved(const SymbianDevice &d); - void deviceAdded(const SymbianDevice &d); + void deviceRemoved(const SymbianUtils::SymbianDevice &d); + void deviceAdded(const SymbianUtils::SymbianDevice &d); void updated(); private: + void ensureInitialized() const; void update(bool emitSignals); SymbianDeviceList serialPorts() const; SymbianDeviceList blueToothDevices() const; @@ -126,7 +159,7 @@ private: SymbianDeviceManagerPrivate *d; }; -QDebug operator<<(QDebug d, const SymbianDeviceManager &); +SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &); } // namespace SymbianUtils diff --git a/src/shared/symbianutils/trkdevice.cpp b/src/shared/symbianutils/trkdevice.cpp index 6c5b8b2648f0d4337567622db9932d6a6324ff07..c9b1bb79eda5f12b6c565280fd34e791cedda7ba 100644 --- a/src/shared/symbianutils/trkdevice.cpp +++ b/src/shared/symbianutils/trkdevice.cpp @@ -40,6 +40,7 @@ #include <QtCore/QMutex> #include <QtCore/QWaitCondition> #include <QtCore/QSharedPointer> +#include <QtCore/QScopedPointer> #include <QtCore/QMetaType> #ifdef Q_OS_WIN @@ -90,6 +91,11 @@ QString winErrorMessage(unsigned long error) enum { verboseTrk = 0 }; +static inline QString msgAccessingClosedDevice(const QString &msg) +{ + return QString::fromLatin1("Error: Attempt to access device '%1', which is closed.").arg(msg); +} + namespace trk { /////////////////////////////////////////////////////////////////////// @@ -147,6 +153,7 @@ class TrkWriteQueue Q_DISABLE_COPY(TrkWriteQueue) public: explicit TrkWriteQueue(); + void clear(); // Enqueue messages. void queueTrkMessage(byte code, TrkCallback callback, @@ -196,13 +203,21 @@ TrkWriteQueue::TrkWriteQueue() : { } +void TrkWriteQueue::clear() +{ + m_trkWriteToken = 0; + m_trkWriteBusy = false; + m_trkWriteQueue.clear(); + m_writtenTrkMessages.clear(); +} + byte TrkWriteQueue::nextTrkWriteToken() { ++m_trkWriteToken; if (m_trkWriteToken == 0) ++m_trkWriteToken; if (verboseTrk) - qDebug() << "Write token: " << m_trkWriteToken; + qDebug() << "nextTrkWriteToken:" << m_trkWriteToken; return m_trkWriteToken; } @@ -450,6 +465,7 @@ void WriterThread::terminate() m_waitCondition.wakeAll(); wait(); m_terminate = false; + m_queue.clear(); } #ifdef Q_OS_WIN @@ -580,6 +596,8 @@ class ReaderThreadBase : public QThread Q_DISABLE_COPY(ReaderThreadBase) public: + int bytesPending() const { return m_trkReadBuffer.size(); } + signals: void messageReceived(const trk::TrkResult &result, const QByteArray &rawData); @@ -865,8 +883,8 @@ struct TrkDevicePrivate TrkDevicePrivate(); QSharedPointer<DeviceContext> deviceContext; - QSharedPointer<WriterThread> writerThread; - QSharedPointer<ReaderThread> readerThread; + QScopedPointer<WriterThread> writerThread; + QScopedPointer<ReaderThread> readerThread; QByteArray trkReadBuffer; int verbose; @@ -905,14 +923,14 @@ TrkDevice::~TrkDevice() bool TrkDevice::open(QString *errorMessage) { - if (d->verbose) + if (d->verbose || verboseTrk) qDebug() << "Opening" << port() << "is open: " << isOpen() << " serialFrame=" << serialFrame(); + if (isOpen()) + return true; if (d->port.isEmpty()) { *errorMessage = QLatin1String("Internal error: No port set on TrkDevice"); return false; } - - close(); #ifdef Q_OS_WIN const QString fullPort = QLatin1String("\\\\.\\") + d->port; d->deviceContext->device = CreateFile(reinterpret_cast<const WCHAR*>(fullPort.utf16()), @@ -963,7 +981,7 @@ bool TrkDevice::open(QString *errorMessage) return false; } #endif - d->readerThread = QSharedPointer<ReaderThread>(new ReaderThread(d->deviceContext)); + d->readerThread.reset(new ReaderThread(d->deviceContext)); connect(d->readerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)), Qt::QueuedConnection); connect(d->readerThread.data(), SIGNAL(messageReceived(trk::TrkResult,QByteArray)), @@ -971,18 +989,22 @@ bool TrkDevice::open(QString *errorMessage) Qt::QueuedConnection); d->readerThread->start(); - d->writerThread = QSharedPointer<WriterThread>(new WriterThread(d->deviceContext)); + d->writerThread.reset(new WriterThread(d->deviceContext)); connect(d->writerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)), Qt::QueuedConnection); d->writerThread->start(); - if (d->verbose) - qDebug() << "Opened" << d->port; + if (d->verbose || verboseTrk) + qDebug() << "Opened" << d->port << d->readerThread.data() << d->writerThread.data(); return true; } void TrkDevice::close() { + if (verboseTrk) + qDebug() << "close" << d->port << " is open: " << isOpen() + << " read pending " << (d->readerThread.isNull() ? 0 : d->readerThread->bytesPending()) + << sender(); if (!isOpen()) return; if (d->readerThread) @@ -998,6 +1020,7 @@ void TrkDevice::close() #else d->deviceContext->file.close(); #endif + if (d->verbose) emitLogMessage("Close"); } @@ -1018,6 +1041,8 @@ QString TrkDevice::port() const void TrkDevice::setPort(const QString &p) { + if (verboseTrk) + qDebug() << "setPort" << p; d->port = p; } @@ -1033,6 +1058,8 @@ bool TrkDevice::serialFrame() const void TrkDevice::setSerialFrame(bool f) { + if (verboseTrk) + qDebug() << "setSerialFrame" << f; d->deviceContext->serialFrame = f; } @@ -1048,12 +1075,14 @@ void TrkDevice::setVerbose(int b) void TrkDevice::slotMessageReceived(const trk::TrkResult &result, const QByteArray &rawData) { - d->writerThread->slotHandleResult(result); - if (d->verbose > 1) - qDebug() << "Received: " << result.toString(); - emit messageReceived(result); - if (!rawData.isEmpty()) - emit rawDataReceived(rawData); + if (isOpen()) { // Might receive bytes after closing due to queued connections. + d->writerThread->slotHandleResult(result); + if (d->verbose > 1) + qDebug() << "Received: " << result.toString(); + emit messageReceived(result); + if (!rawData.isEmpty()) + emit rawDataReceived(rawData); + } } void TrkDevice::emitError(const QString &s) @@ -1066,12 +1095,18 @@ void TrkDevice::emitError(const QString &s) void TrkDevice::sendTrkMessage(byte code, TrkCallback callback, const QByteArray &data, const QVariant &cookie) { + if (!isOpen()) { + emitError(msgAccessingClosedDevice(d->port)); + return; + } if (!d->writerThread.isNull()) { if (d->verbose > 1) { - QByteArray msg = "Sending: "; + QByteArray msg = "Sending: 0x"; msg += QByteArray::number(code, 16); msg += ": "; msg += stringFromArray(data).toLatin1(); + if (cookie.isValid()) + msg += " Cookie: " + cookie.toString().toLatin1(); qDebug("%s", msg.data()); } d->writerThread->queueTrkMessage(code, callback, data, cookie); @@ -1080,12 +1115,20 @@ void TrkDevice::sendTrkMessage(byte code, TrkCallback callback, void TrkDevice::sendTrkInitialPing() { + if (!isOpen()) { + emitError(msgAccessingClosedDevice(d->port)); + return; + } if (!d->writerThread.isNull()) d->writerThread->queueTrkInitialPing(); } bool TrkDevice::sendTrkAck(byte token) { + if (!isOpen()) { + emitError(msgAccessingClosedDevice(d->port)); + return false; + } if (d->writerThread.isNull()) return false; // The acknowledgement must not be queued! diff --git a/src/shared/symbianutils/trkdevice.h b/src/shared/symbianutils/trkdevice.h index 763b60ff2517b89b93dd52907c796c7b9c21e95b..531b40c30095abc10436f2be6f115a071d296cac 100644 --- a/src/shared/symbianutils/trkdevice.h +++ b/src/shared/symbianutils/trkdevice.h @@ -57,7 +57,9 @@ struct TrkDevicePrivate; * for queueing messages with a notification callback. If the message receives * an ACK, the callback is invoked. * The special message TRK_WRITE_QUEUE_NOOP_CODE code can be used for synchronization. - * The respective message will not be sent, the callback is just invoked. */ + * The respective message will not be sent, the callback is just invoked. + * Note that calling open/close in quick succession can cause crashes + * due to the use of queused signals. */ enum { TRK_WRITE_QUEUE_NOOP_CODE = 0x7f };