summaryrefslogtreecommitdiffstats
path: root/tools/runonphone/symbianutils/symbiandevicemanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/runonphone/symbianutils/symbiandevicemanager.cpp')
-rw-r--r--tools/runonphone/symbianutils/symbiandevicemanager.cpp290
1 files changed, 272 insertions, 18 deletions
diff --git a/tools/runonphone/symbianutils/symbiandevicemanager.cpp b/tools/runonphone/symbianutils/symbiandevicemanager.cpp
index 02727d7..23f5348 100644
--- a/tools/runonphone/symbianutils/symbiandevicemanager.cpp
+++ b/tools/runonphone/symbianutils/symbiandevicemanager.cpp
@@ -41,7 +41,11 @@
#include "symbiandevicemanager.h"
#include "trkdevice.h"
+#include "codadevice.h"
+#include "virtualserialdevice.h"
+#include <QtCore/QCoreApplication>
+#include <QtCore/QEvent>
#include <QtCore/QSettings>
#include <QtCore/QStringList>
#include <QtCore/QFileInfo>
@@ -50,6 +54,9 @@
#include <QtCore/QSharedData>
#include <QtCore/QScopedPointer>
#include <QtCore/QSignalMapper>
+#include <QtCore/QThread>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QTimer>
namespace SymbianUtils {
@@ -66,7 +73,7 @@ public:
SymbianDeviceData();
~SymbianDeviceData();
- inline bool isOpen() const { return !device.isNull() && device->isOpen(); }
+ bool isOpen() const;
void forcedClose();
QString portName;
@@ -77,15 +84,25 @@ public:
DeviceCommunicationType type;
QSharedPointer<trk::TrkDevice> device;
- bool deviceAcquired;
+ QSharedPointer<Coda::CodaDevice> codaDevice;
+ int deviceAcquired;
};
SymbianDeviceData::SymbianDeviceData() :
type(SerialPortCommunication),
- deviceAcquired(false)
+ deviceAcquired(0)
{
}
+bool SymbianDeviceData::isOpen() const
+{
+ if (device)
+ return device->isOpen();
+ if (codaDevice)
+ return codaDevice->device()->isOpen();
+ return false;
+}
+
SymbianDeviceData::~SymbianDeviceData()
{
forcedClose();
@@ -101,7 +118,10 @@ void SymbianDeviceData::forcedClose()
if (deviceAcquired)
qWarning("Device on '%s' unplugged while an operation is in progress.",
qPrintable(portName));
- device->close();
+ if (device)
+ device->close();
+ else
+ codaDevice->device()->close();
}
}
@@ -167,7 +187,7 @@ SymbianDevice::TrkDevicePtr SymbianDevice::acquireDevice()
m_data->device->setPort(m_data->portName);
m_data->device->setSerialFrame(m_data->type == SerialPortCommunication);
}
- m_data->deviceAcquired = true;
+ m_data->deviceAcquired = 1;
return m_data->device;
}
@@ -184,7 +204,7 @@ void SymbianDevice::releaseDevice(TrkDevicePtr *ptr /* = 0 */)
ptr->data()->disconnect();
*ptr = TrkDevicePtr();
}
- m_data->deviceAcquired = false;
+ m_data->deviceAcquired = 0;
} else {
qWarning("Internal error: Attempt to release device that is not acquired.");
}
@@ -255,17 +275,34 @@ SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &cd)
// ------------- SymbianDeviceManagerPrivate
struct SymbianDeviceManagerPrivate {
- SymbianDeviceManagerPrivate() : m_initialized(false), m_destroyReleaseMapper(0) {}
+ SymbianDeviceManagerPrivate() : m_initialized(false), m_devicesLock(QMutex::Recursive) {}
bool m_initialized;
SymbianDeviceManager::SymbianDeviceList m_devices;
- QSignalMapper *m_destroyReleaseMapper;
+ QMutex m_devicesLock; // Used for protecting access to m_devices and serialising getCodaDevice/delayedClosePort
+ // The following 2 variables are needed to manage requests for a TCF port not coming from the main thread
+ int m_constructTcfPortEventType;
+ QMutex m_codaPortWaitMutex;
+};
+
+class QConstructTcfPortEvent : public QEvent
+{
+public:
+ QConstructTcfPortEvent(QEvent::Type eventId, const QString &portName, CodaDevicePtr *device, QWaitCondition *waiter) :
+ QEvent(eventId), m_portName(portName), m_device(device), m_waiter(waiter)
+ {}
+
+ QString m_portName;
+ CodaDevicePtr* m_device;
+ QWaitCondition *m_waiter;
};
+
SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
QObject(parent),
d(new SymbianDeviceManagerPrivate)
{
+ d->m_constructTcfPortEventType = QEvent::registerEventType();
}
SymbianDeviceManager::~SymbianDeviceManager()
@@ -276,11 +313,13 @@ SymbianDeviceManager::~SymbianDeviceManager()
SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const
{
ensureInitialized();
+ QMutexLocker lock(&d->m_devicesLock);
return d->m_devices;
}
QString SymbianDeviceManager::toString() const
{
+ QMutexLocker lock(&d->m_devicesLock);
QString rc;
QTextStream str(&rc);
str << d->m_devices.size() << " devices:\n";
@@ -305,6 +344,7 @@ int SymbianDeviceManager::findByPortName(const QString &p) const
QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const
{
+ QMutexLocker lock(&d->m_devicesLock);
const int idx = findByPortName(port);
return idx == -1 ? QString() : d->m_devices.at(idx).friendlyName();
}
@@ -326,6 +366,106 @@ SymbianDeviceManager::TrkDevicePtr
return rc;
}
+CodaDevicePtr SymbianDeviceManager::getCodaDevice(const QString &port)
+{
+ ensureInitialized();
+ QMutexLocker lock(&d->m_devicesLock);
+ 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 CodaDevicePtr();
+ }
+ SymbianDevice& device = d->m_devices[idx];
+ if (device.m_data->device && device.m_data->device.data()->isOpen()) {
+ qWarning("Attempting to open a port '%s' that is configured for TRK!", qPrintable(port));
+ return CodaDevicePtr();
+ }
+ CodaDevicePtr& devicePtr = device.m_data->codaDevice;
+ if (devicePtr.isNull() || !devicePtr->device()->isOpen()) {
+ // Check we instanciate in the correct thread - we can't afford to create the CodaDevice (and more specifically, open the VirtualSerialDevice) in a thread that isn't guaranteed to be long-lived.
+ // Therefore, if we're not in SymbianDeviceManager's thread, rejig things so it's opened in the main thread
+ if (QThread::currentThread() != thread()) {
+ // SymbianDeviceManager is owned by the main thread
+ d->m_codaPortWaitMutex.lock();
+ QWaitCondition waiter;
+ QCoreApplication::postEvent(this, new QConstructTcfPortEvent((QEvent::Type)d->m_constructTcfPortEventType, port, &devicePtr, &waiter));
+ waiter.wait(&d->m_codaPortWaitMutex);
+ // When the wait returns (due to the wakeAll in SymbianDeviceManager::customEvent), the CodaDevice will be fully set up
+ d->m_codaPortWaitMutex.unlock();
+ } else {
+ // We're in the main thread, just set it up directly
+ constructCodaPort(devicePtr, port);
+ }
+ // We still carry on in the case we failed to open so the client can access the IODevice's errorString()
+ }
+ if (devicePtr->device()->isOpen())
+ device.m_data->deviceAcquired++;
+ return devicePtr;
+}
+
+void SymbianDeviceManager::constructCodaPort(CodaDevicePtr& device, const QString& portName)
+{
+ QMutexLocker locker(&d->m_codaPortWaitMutex);
+ if (device.isNull()) {
+ device = QSharedPointer<Coda::CodaDevice>(new Coda::CodaDevice);
+ const QSharedPointer<SymbianUtils::VirtualSerialDevice> serialDevice(new SymbianUtils::VirtualSerialDevice(portName));
+ device->setSerialFrame(true);
+ device->setDevice(serialDevice);
+ }
+ if (!device->device()->isOpen()) {
+ bool ok = device->device().staticCast<SymbianUtils::VirtualSerialDevice>()->open(QIODevice::ReadWrite);
+ if (!ok && debug) {
+ qDebug("SymbianDeviceManager: Failed to open port %s", qPrintable(portName));
+ }
+ }
+}
+
+void SymbianDeviceManager::customEvent(QEvent *event)
+{
+ if (event->type() == d->m_constructTcfPortEventType) {
+ QConstructTcfPortEvent* constructEvent = static_cast<QConstructTcfPortEvent*>(event);
+ constructCodaPort(*constructEvent->m_device, constructEvent->m_portName);
+ constructEvent->m_waiter->wakeAll(); // Should only ever be one thing waiting on this
+ }
+}
+
+void SymbianDeviceManager::releaseCodaDevice(CodaDevicePtr &port)
+{
+ if (port) {
+ QMutexLocker(&d->m_devicesLock);
+ // Check if this was the last reference to the port, if so close it after a short delay
+ foreach (const SymbianDevice& device, d->m_devices) {
+ if (device.m_data->codaDevice.data() == port.data()) {
+ if (device.m_data->deviceAcquired > 0)
+ device.m_data->deviceAcquired--;
+ if (device.m_data->deviceAcquired == 0) {
+ if (debug)
+ qDebug("Starting timer to close port %s", qPrintable(device.m_data->portName));
+ QTimer::singleShot(1000, this, SLOT(delayedClosePort()));
+ }
+ break;
+ }
+ }
+ port.clear();
+ }
+}
+
+void SymbianDeviceManager::delayedClosePort()
+{
+ // Find any coda ports that are still open but have a reference count of zero, and delete them
+ QMutexLocker(&d->m_devicesLock);
+ foreach (const SymbianDevice& device, d->m_devices) {
+ Coda::CodaDevice* codaDevice = device.m_data->codaDevice.data();
+ if (codaDevice && device.m_data->deviceAcquired == 0 && codaDevice->device()->isOpen()) {
+ if (debug)
+ qDebug("Closing device %s", qPrintable(device.m_data->portName));
+ device.m_data->codaDevice->device()->close();
+ }
+ }
+}
+
void SymbianDeviceManager::update()
{
update(true);
@@ -336,11 +476,10 @@ void SymbianDeviceManager::releaseDevice(const QString &port)
const int idx = findByPortName(port);
if (debug)
qDebug() << "SymbianDeviceManager::releaseDevice" << port << idx << sender();
- if (idx != -1) {
+ if (idx != -1)
d->m_devices[idx].releaseDevice();
- } else {
+ else
qWarning("Attempt to release non-existing device %s.", qPrintable(port));
- }
}
void SymbianDeviceManager::setAdditionalInformation(const QString &port, const QString &ai)
@@ -358,6 +497,8 @@ void SymbianDeviceManager::ensureInitialized() const
void SymbianDeviceManager::update(bool emitSignals)
{
+ QMutexLocker lock(&d->m_devicesLock);
+
static int n = 0;
typedef SymbianDeviceList::iterator SymbianDeviceListIterator;
@@ -377,6 +518,7 @@ void SymbianDeviceManager::update(bool emitSignals)
}
// 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
+ SymbianDeviceList removedDevices;
if (!d->m_devices.isEmpty()) {
// Find deleted devices
for (SymbianDeviceListIterator oldIt = d->m_devices.begin(); oldIt != d->m_devices.end(); ) {
@@ -386,25 +528,33 @@ void SymbianDeviceManager::update(bool emitSignals)
SymbianDevice toBeDeleted = *oldIt;
toBeDeleted.forcedClose();
oldIt = d->m_devices.erase(oldIt);
- if (emitSignals)
- emit deviceRemoved(toBeDeleted);
+ removedDevices.append(toBeDeleted);
}
}
}
+ SymbianDeviceList addedDevices;
if (!newDevices.isEmpty()) {
// Find new devices and insert in order
foreach(const SymbianDevice &newDevice, newDevices) {
if (!d->m_devices.contains(newDevice)) {
d->m_devices.append(newDevice);
- if (emitSignals)
- emit deviceAdded(newDevice);
+ addedDevices.append(newDevice);
}
}
if (d->m_devices.size() > 1)
qStableSort(d->m_devices.begin(), d->m_devices.end());
}
- if (emitSignals)
+
+ lock.unlock();
+ if (emitSignals) {
+ foreach (const SymbianDevice &device, removedDevices) {
+ emit deviceRemoved(device);
+ }
+ foreach (const SymbianDevice &device, addedDevices) {
+ emit deviceAdded(device);
+ }
emit updated();
+ }
if (debug)
qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString()));
@@ -457,7 +607,9 @@ SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::blueToothDevices()
}
// New kernel versions support /dev/ttyUSB0, /dev/ttyUSB1. Trk responds
// on the latter (usually), try first.
- static const char *usbTtyDevices[] = { "/dev/ttyUSB1", "/dev/ttyUSB0" };
+ static const char *usbTtyDevices[] = {
+ "/dev/ttyUSB3", "/dev/ttyUSB2", "/dev/ttyUSB1", "/dev/ttyUSB0",
+ "/dev/ttyACM3", "/dev/ttyACM2", "/dev/ttyACM1", "/dev/ttyACM0"};
const int usbTtyCount = sizeof(usbTtyDevices)/sizeof(const char *);
for (int d = 0; d < usbTtyCount; d++) {
const QString ttyUSBDevice = QLatin1String(usbTtyDevices[d]);
@@ -486,4 +638,106 @@ SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
return d;
}
-} // namespace SymbianUtilsInternal
+OstChannel *SymbianDeviceManager::getOstChannel(const QString &port, uchar channelId)
+{
+ CodaDevicePtr coda = getCodaDevice(port);
+ if (coda.isNull() || !coda->device()->isOpen())
+ return 0;
+ return new OstChannel(coda, channelId);
+}
+
+struct OstChannelPrivate
+{
+ CodaDevicePtr m_codaPtr;
+ QByteArray m_dataBuffer;
+ uchar m_channelId;
+ bool m_hasReceivedData;
+};
+
+OstChannel::OstChannel(const CodaDevicePtr &codaPtr, uchar channelId)
+ : d(new OstChannelPrivate)
+{
+ d->m_codaPtr = codaPtr;
+ d->m_channelId = channelId;
+ d->m_hasReceivedData = false;
+ connect(codaPtr.data(), SIGNAL(unknownEvent(uchar, QByteArray)), this, SLOT(ostDataReceived(uchar,QByteArray)));
+ connect(codaPtr->device().data(), SIGNAL(aboutToClose()), this, SLOT(deviceAboutToClose()));
+ QIODevice::open(ReadWrite|Unbuffered);
+}
+
+void OstChannel::close()
+{
+ QIODevice::close();
+ if (d && d->m_codaPtr.data()) {
+ disconnect(d->m_codaPtr.data(), 0, this, 0);
+ SymbianDeviceManager::instance()->releaseCodaDevice(d->m_codaPtr);
+ }
+}
+
+OstChannel::~OstChannel()
+{
+ close();
+ delete d;
+}
+
+void OstChannel::flush()
+{
+ //TODO d->m_codaPtr->device()-
+}
+
+qint64 OstChannel::bytesAvailable() const
+{
+ return d->m_dataBuffer.size();
+}
+
+bool OstChannel::isSequential() const
+{
+ return true;
+}
+
+qint64 OstChannel::readData(char *data, qint64 maxSize)
+{
+ qint64 amount = qMin(maxSize, (qint64)d->m_dataBuffer.size());
+ qMemCopy(data, d->m_dataBuffer.constData(), amount);
+ d->m_dataBuffer.remove(0, amount);
+ return amount;
+}
+
+qint64 OstChannel::writeData(const char *data, qint64 maxSize)
+{
+ static const qint64 KMaxOstPayload = 1024;
+ // If necessary, split the packet up
+ while (maxSize) {
+ QByteArray dataBuf = QByteArray::fromRawData(data, qMin(KMaxOstPayload, maxSize));
+ d->m_codaPtr->writeCustomData(d->m_channelId, dataBuf);
+ data += dataBuf.length();
+ maxSize -= dataBuf.length();
+ }
+ return maxSize;
+}
+
+void OstChannel::ostDataReceived(uchar channelId, const QByteArray &aData)
+{
+ if (channelId == d->m_channelId) {
+ d->m_hasReceivedData = true;
+ d->m_dataBuffer.append(aData);
+ emit readyRead();
+ }
+}
+
+Coda::CodaDevice& OstChannel::codaDevice() const
+{
+ return *d->m_codaPtr;
+}
+
+bool OstChannel::hasReceivedData() const
+{
+ return isOpen() && d->m_hasReceivedData;
+}
+
+void OstChannel::deviceAboutToClose()
+{
+ close();
+}
+
+} // namespace SymbianUtils