1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** 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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "symbiandevicemanager.h"
43 #include "trkdevice.h"
44 #include "codadevice.h"
45 #include "virtualserialdevice.h"
46
47 #include <QtCore/QCoreApplication>
48 #include <QtCore/QEvent>
49 #include <QtCore/QSettings>
50 #include <QtCore/QStringList>
51 #include <QtCore/QFileInfo>
52 #include <QtCore/QtDebug>
53 #include <QtCore/QTextStream>
54 #include <QtCore/QSharedData>
55 #include <QtCore/QScopedPointer>
56 #include <QtCore/QSignalMapper>
57 #include <QtCore/QThread>
58 #include <QtCore/QWaitCondition>
59 #include <QtCore/QTimer>
60 #include <QtCore/QDir>
61
62 namespace SymbianUtils {
63
64 enum { debug = 0 };
65
66 static const char REGKEY_CURRENT_CONTROL_SET[] = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet";
67 static const char USBSER[] = "Services/usbser/Enum";
68
69 const char *SymbianDeviceManager::linuxBlueToothDeviceRootC = "/dev/rfcomm";
70
71 // ------------- SymbianDevice
72 class SymbianDeviceData : public QSharedData {
73 public:
74 SymbianDeviceData();
75 ~SymbianDeviceData();
76
77 bool isOpen() const;
78 void forcedClose();
79
80 QString portName;
81 QString friendlyName;
82 QString deviceDesc;
83 QString manufacturer;
84 QString additionalInformation;
85
86 DeviceCommunicationType type;
87 QSharedPointer<trk::TrkDevice> device;
88 QSharedPointer<Coda::CodaDevice> codaDevice;
89 int deviceAcquired;
90 };
91
SymbianDeviceData()92 SymbianDeviceData::SymbianDeviceData() :
93 type(SerialPortCommunication),
94 deviceAcquired(0)
95 {
96 }
97
isOpen() const98 bool SymbianDeviceData::isOpen() const
99 {
100 if (device)
101 return device->isOpen();
102 if (codaDevice)
103 return codaDevice->device()->isOpen();
104 return false;
105 }
106
~SymbianDeviceData()107 SymbianDeviceData::~SymbianDeviceData()
108 {
109 forcedClose();
110 }
111
forcedClose()112 void SymbianDeviceData::forcedClose()
113 {
114 // Close the device when unplugging. Should devices be in 'acquired' state,
115 // their owners should hit on write failures.
116 // Apart from the <shared item> destructor, also called by the devicemanager
117 // to ensure it also happens if other shared instances are still around.
118 if (isOpen()) {
119 if (deviceAcquired)
120 qWarning("Device on '%s' unplugged while an operation is in progress.",
121 qPrintable(portName));
122 if (device)
123 device->close();
124 else
125 codaDevice->device()->close();
126 }
127 }
128
SymbianDevice(SymbianDeviceData * data)129 SymbianDevice::SymbianDevice(SymbianDeviceData *data) :
130 m_data(data)
131 {
132 }
133
SymbianDevice()134 SymbianDevice::SymbianDevice() :
135 m_data(new SymbianDeviceData)
136 {
137 }
SymbianDevice(const SymbianDevice & rhs)138 SymbianDevice::SymbianDevice(const SymbianDevice &rhs) :
139 m_data(rhs.m_data)
140 {
141 }
142
operator =(const SymbianDevice & rhs)143 SymbianDevice &SymbianDevice::operator=(const SymbianDevice &rhs)
144 {
145 if (this != &rhs)
146 m_data = rhs.m_data;
147 return *this;
148 }
149
~SymbianDevice()150 SymbianDevice::~SymbianDevice()
151 {
152 }
153
forcedClose()154 void SymbianDevice::forcedClose()
155 {
156 m_data->forcedClose();
157 }
158
portName() const159 QString SymbianDevice::portName() const
160 {
161 return m_data->portName;
162 }
163
friendlyName() const164 QString SymbianDevice::friendlyName() const
165 {
166 return m_data->friendlyName;
167 }
168
additionalInformation() const169 QString SymbianDevice::additionalInformation() const
170 {
171 return m_data->additionalInformation;
172 }
173
setAdditionalInformation(const QString & a)174 void SymbianDevice::setAdditionalInformation(const QString &a)
175 {
176 m_data->additionalInformation = a;
177 }
178
acquireDevice()179 SymbianDevice::TrkDevicePtr SymbianDevice::acquireDevice()
180 {
181 if (debug)
182 qDebug() << "SymbianDevice::acquireDevice" << m_data->portName
183 << "acquired: " << m_data->deviceAcquired << " open: " << isOpen();
184 if (isNull() || m_data->deviceAcquired)
185 return TrkDevicePtr();
186 //if port was opened for coda (but is not acquired) then close it first.
187 if (m_data->codaDevice)
188 m_data->codaDevice->device()->close();
189 if (m_data->device.isNull()) {
190 m_data->device = TrkDevicePtr(new trk::TrkDevice);
191 m_data->device->setPort(m_data->portName);
192 m_data->device->setSerialFrame(m_data->type == SerialPortCommunication);
193 }
194 m_data->deviceAcquired = 1;
195 return m_data->device;
196 }
197
releaseDevice(TrkDevicePtr * ptr)198 void SymbianDevice::releaseDevice(TrkDevicePtr *ptr /* = 0 */)
199 {
200 if (debug)
201 qDebug() << "SymbianDevice::releaseDevice" << m_data->portName
202 << " open: " << isOpen();
203 if (m_data->deviceAcquired) {
204 if (m_data->device->isOpen())
205 m_data->device->clearWriteQueue();
206 // Release if a valid pointer was passed in.
207 if (ptr && !ptr->isNull()) {
208 ptr->data()->disconnect();
209 *ptr = TrkDevicePtr();
210 }
211 m_data->deviceAcquired = 0;
212 } else {
213 qWarning("Internal error: Attempt to release device that is not acquired.");
214 }
215 }
216
deviceDesc() const217 QString SymbianDevice::deviceDesc() const
218 {
219 return m_data->deviceDesc;
220 }
221
manufacturer() const222 QString SymbianDevice::manufacturer() const
223 {
224 return m_data->manufacturer;
225 }
226
type() const227 DeviceCommunicationType SymbianDevice::type() const
228 {
229 return m_data->type;
230 }
231
isNull() const232 bool SymbianDevice::isNull() const
233 {
234 return m_data->portName.isEmpty();
235 }
236
isOpen() const237 bool SymbianDevice::isOpen() const
238 {
239 return m_data->isOpen();
240 }
241
toString() const242 QString SymbianDevice::toString() const
243 {
244 QString rc;
245 QTextStream str(&rc);
246 format(str);
247 return rc;
248 }
249
format(QTextStream & str) const250 void SymbianDevice::format(QTextStream &str) const
251 {
252 str << (m_data->type == BlueToothCommunication ? "Bluetooth: " : "Serial: ")
253 << m_data->portName;
254 if (!m_data->friendlyName.isEmpty()) {
255 str << " (" << m_data->friendlyName;
256 if (!m_data->deviceDesc.isEmpty())
257 str << " / " << m_data->deviceDesc;
258 str << ')';
259 }
260 if (!m_data->manufacturer.isEmpty())
261 str << " [" << m_data->manufacturer << ']';
262 }
263
264 // Compare by port and friendly name
compare(const SymbianDevice & rhs) const265 int SymbianDevice::compare(const SymbianDevice &rhs) const
266 {
267 if (const int prc = m_data->portName.compare(rhs.m_data->portName))
268 return prc;
269 if (const int frc = m_data->friendlyName.compare(rhs.m_data->friendlyName))
270 return frc;
271 return 0;
272 }
273
operator <<(QDebug d,const SymbianDevice & cd)274 SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &cd)
275 {
276 d.nospace() << cd.toString();
277 return d;
278 }
279
280 // ------------- SymbianDeviceManagerPrivate
281 struct SymbianDeviceManagerPrivate {
SymbianDeviceManagerPrivateSymbianUtils::SymbianDeviceManagerPrivate282 SymbianDeviceManagerPrivate() : m_initialized(false), m_devicesLock(QMutex::Recursive) {}
283
284 bool m_initialized;
285 SymbianDeviceManager::SymbianDeviceList m_devices;
286 QMutex m_devicesLock; // Used for protecting access to m_devices and serialising getCodaDevice/delayedClosePort
287 // The following 2 variables are needed to manage requests for a TCF port not coming from the main thread
288 int m_constructTcfPortEventType;
289 QMutex m_codaPortWaitMutex;
290 };
291
292 class QConstructTcfPortEvent : public QEvent
293 {
294 public:
QConstructTcfPortEvent(QEvent::Type eventId,const QString & portName,CodaDevicePtr * device,QWaitCondition * waiter)295 QConstructTcfPortEvent(QEvent::Type eventId, const QString &portName, CodaDevicePtr *device, QWaitCondition *waiter) :
296 QEvent(eventId), m_portName(portName), m_device(device), m_waiter(waiter)
297 {}
298
299 QString m_portName;
300 CodaDevicePtr* m_device;
301 QWaitCondition *m_waiter;
302 };
303
304
SymbianDeviceManager(QObject * parent)305 SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
306 QObject(parent),
307 d(new SymbianDeviceManagerPrivate)
308 {
309 d->m_constructTcfPortEventType = QEvent::registerEventType();
310 }
311
~SymbianDeviceManager()312 SymbianDeviceManager::~SymbianDeviceManager()
313 {
314 delete d;
315 }
316
devices() const317 SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const
318 {
319 ensureInitialized();
320 QMutexLocker lock(&d->m_devicesLock);
321 return d->m_devices;
322 }
323
toString() const324 QString SymbianDeviceManager::toString() const
325 {
326 QMutexLocker lock(&d->m_devicesLock);
327 QString rc;
328 QTextStream str(&rc);
329 str << d->m_devices.size() << " devices:\n";
330 const int count = d->m_devices.size();
331 for (int i = 0; i < count; i++) {
332 str << '#' << i << ' ';
333 d->m_devices.at(i).format(str);
334 str << '\n';
335 }
336 return rc;
337 }
338
findByPortName(const QString & p) const339 int SymbianDeviceManager::findByPortName(const QString &p) const
340 {
341 ensureInitialized();
342 const int count = d->m_devices.size();
343 for (int i = 0; i < count; i++)
344 if (d->m_devices.at(i).portName() == p)
345 return i;
346 return -1;
347 }
348
friendlyNameForPort(const QString & port) const349 QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const
350 {
351 QMutexLocker lock(&d->m_devicesLock);
352 const int idx = findByPortName(port);
353 return idx == -1 ? QString() : d->m_devices.at(idx).friendlyName();
354 }
355
356 SymbianDeviceManager::TrkDevicePtr
acquireDevice(const QString & port)357 SymbianDeviceManager::acquireDevice(const QString &port)
358 {
359 ensureInitialized();
360 const int idx = findByPortName(port);
361 if (idx == -1) {
362 qWarning("Attempt to acquire device '%s' that does not exist.", qPrintable(port));
363 if (debug)
364 qDebug() << *this;
365 return TrkDevicePtr();
366 }
367 const TrkDevicePtr rc = d->m_devices[idx].acquireDevice();
368 if (debug)
369 qDebug() << "SymbianDeviceManager::acquireDevice" << port << " returns " << !rc.isNull();
370 return rc;
371 }
372
getCodaDevice(const QString & port)373 CodaDevicePtr SymbianDeviceManager::getCodaDevice(const QString &port)
374 {
375 ensureInitialized();
376 QMutexLocker lock(&d->m_devicesLock);
377 const int idx = findByPortName(port);
378 if (idx == -1) {
379 qWarning("Attempt to acquire device '%s' that does not exist.", qPrintable(port));
380 if (debug)
381 qDebug() << *this;
382 return CodaDevicePtr();
383 }
384 SymbianDevice& device = d->m_devices[idx];
385 if (device.m_data->device && device.m_data->device.data()->isOpen()) {
386 qWarning("Attempting to open a port '%s' that is configured for TRK!", qPrintable(port));
387 return CodaDevicePtr();
388 }
389 CodaDevicePtr& devicePtr = device.m_data->codaDevice;
390 if (devicePtr.isNull() || !devicePtr->device()->isOpen()) {
391 // 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.
392 // Therefore, if we're not in SymbianDeviceManager's thread, rejig things so it's opened in the main thread
393 if (QThread::currentThread() != thread()) {
394 // SymbianDeviceManager is owned by the main thread
395 d->m_codaPortWaitMutex.lock();
396 QWaitCondition waiter;
397 QCoreApplication::postEvent(this, new QConstructTcfPortEvent((QEvent::Type)d->m_constructTcfPortEventType, port, &devicePtr, &waiter));
398 waiter.wait(&d->m_codaPortWaitMutex);
399 // When the wait returns (due to the wakeAll in SymbianDeviceManager::customEvent), the CodaDevice will be fully set up
400 d->m_codaPortWaitMutex.unlock();
401 } else {
402 // We're in the main thread, just set it up directly
403 constructCodaPort(devicePtr, port);
404 }
405 // We still carry on in the case we failed to open so the client can access the IODevice's errorString()
406 }
407 if (devicePtr->device()->isOpen())
408 device.m_data->deviceAcquired++;
409 return devicePtr;
410 }
411
constructCodaPort(CodaDevicePtr & device,const QString & portName)412 void SymbianDeviceManager::constructCodaPort(CodaDevicePtr& device, const QString& portName)
413 {
414 QMutexLocker locker(&d->m_codaPortWaitMutex);
415 if (device.isNull()) {
416 device = QSharedPointer<Coda::CodaDevice>(new Coda::CodaDevice);
417 const QSharedPointer<SymbianUtils::VirtualSerialDevice> serialDevice(new SymbianUtils::VirtualSerialDevice(portName));
418 device->setSerialFrame(true);
419 device->setDevice(serialDevice);
420 }
421 if (!device->device()->isOpen()) {
422 bool ok = device->device().staticCast<SymbianUtils::VirtualSerialDevice>()->open(QIODevice::ReadWrite);
423 if (!ok && debug) {
424 qDebug("SymbianDeviceManager: Failed to open port %s", qPrintable(portName));
425 }
426 }
427 }
428
customEvent(QEvent * event)429 void SymbianDeviceManager::customEvent(QEvent *event)
430 {
431 if (event->type() == d->m_constructTcfPortEventType) {
432 QConstructTcfPortEvent* constructEvent = static_cast<QConstructTcfPortEvent*>(event);
433 constructCodaPort(*constructEvent->m_device, constructEvent->m_portName);
434 constructEvent->m_waiter->wakeAll(); // Should only ever be one thing waiting on this
435 }
436 }
437
releaseCodaDevice(CodaDevicePtr & port)438 void SymbianDeviceManager::releaseCodaDevice(CodaDevicePtr &port)
439 {
440 if (port) {
441 QMutexLocker(&d->m_devicesLock);
442 // Check if this was the last reference to the port, if so close it after a short delay
443 foreach (const SymbianDevice& device, d->m_devices) {
444 if (device.m_data->codaDevice.data() == port.data()) {
445 if (device.m_data->deviceAcquired > 0)
446 device.m_data->deviceAcquired--;
447 if (device.m_data->deviceAcquired == 0) {
448 if (debug)
449 qDebug("Starting timer to close port %s", qPrintable(device.m_data->portName));
450 QTimer::singleShot(1000, this, SLOT(delayedClosePort()));
451 }
452 break;
453 }
454 }
455 port.clear();
456 }
457 }
458
delayedClosePort()459 void SymbianDeviceManager::delayedClosePort()
460 {
461 // Find any coda ports that are still open but have a reference count of zero, and delete them
462 QMutexLocker(&d->m_devicesLock);
463 foreach (const SymbianDevice& device, d->m_devices) {
464 Coda::CodaDevice* codaDevice = device.m_data->codaDevice.data();
465 if (codaDevice && device.m_data->deviceAcquired == 0 && codaDevice->device()->isOpen()) {
466 if (debug)
467 qDebug("Closing device %s", qPrintable(device.m_data->portName));
468 device.m_data->codaDevice->device()->close();
469 }
470 }
471 }
472
update()473 void SymbianDeviceManager::update()
474 {
475 update(true);
476 }
477
releaseDevice(const QString & port)478 void SymbianDeviceManager::releaseDevice(const QString &port)
479 {
480 const int idx = findByPortName(port);
481 if (debug)
482 qDebug() << "SymbianDeviceManager::releaseDevice" << port << idx << sender();
483 if (idx != -1)
484 d->m_devices[idx].releaseDevice();
485 else
486 qWarning("Attempt to release non-existing device %s.", qPrintable(port));
487 }
488
setAdditionalInformation(const QString & port,const QString & ai)489 void SymbianDeviceManager::setAdditionalInformation(const QString &port, const QString &ai)
490 {
491 const int idx = findByPortName(port);
492 if (idx != -1)
493 d->m_devices[idx].setAdditionalInformation(ai);
494 }
495
ensureInitialized() const496 void SymbianDeviceManager::ensureInitialized() const
497 {
498 if (!d->m_initialized) // Flag is set in update()
499 const_cast<SymbianDeviceManager*>(this)->update(false);
500 }
501
update(bool emitSignals)502 void SymbianDeviceManager::update(bool emitSignals)
503 {
504 QMutexLocker lock(&d->m_devicesLock);
505
506 static int n = 0;
507 typedef SymbianDeviceList::iterator SymbianDeviceListIterator;
508
509 if (debug)
510 qDebug(">SerialDeviceLister::update(#%d, signals=%d)\n%s", n++, int(emitSignals),
511 qPrintable(toString()));
512
513 d->m_initialized = true;
514 // Get ordered new list
515 SymbianDeviceList newDevices = serialPorts() + blueToothDevices();
516 if (newDevices.size() > 1)
517 qStableSort(newDevices.begin(), newDevices.end());
518 if (d->m_devices == newDevices) { // Happy, nothing changed.
519 if (debug)
520 qDebug("<SerialDeviceLister::update: unchanged\n");
521 return;
522 }
523 // Merge the lists and emit the respective added/removed signals, assuming
524 // no one can plug a different device on the same port at the speed of lightning
525 SymbianDeviceList removedDevices;
526 if (!d->m_devices.isEmpty()) {
527 // Find deleted devices
528 for (SymbianDeviceListIterator oldIt = d->m_devices.begin(); oldIt != d->m_devices.end(); ) {
529 if (newDevices.contains(*oldIt)) {
530 ++oldIt;
531 } else {
532 SymbianDevice toBeDeleted = *oldIt;
533 toBeDeleted.forcedClose();
534 oldIt = d->m_devices.erase(oldIt);
535 removedDevices.append(toBeDeleted);
536 }
537 }
538 }
539 SymbianDeviceList addedDevices;
540 if (!newDevices.isEmpty()) {
541 // Find new devices and insert in order
542 foreach(const SymbianDevice &newDevice, newDevices) {
543 if (!d->m_devices.contains(newDevice)) {
544 d->m_devices.append(newDevice);
545 addedDevices.append(newDevice);
546 }
547 }
548 if (d->m_devices.size() > 1)
549 qStableSort(d->m_devices.begin(), d->m_devices.end());
550 }
551
552 lock.unlock();
553 if (emitSignals) {
554 foreach (const SymbianDevice &device, removedDevices) {
555 emit deviceRemoved(device);
556 }
557 foreach (const SymbianDevice &device, addedDevices) {
558 emit deviceAdded(device);
559 }
560 emit updated();
561 }
562
563 if (debug)
564 qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString()));
565 }
566
serialPorts() const567 SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() const
568 {
569 SymbianDeviceList rc;
570 #ifdef Q_OS_WIN
571 const QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
572 const QString usbSerialRootKey = QLatin1String(USBSER) + QLatin1Char('/');
573 const int count = registry.value(usbSerialRootKey + QLatin1String("Count")).toInt();
574 for (int i = 0; i < count; ++i) {
575 QString driver = registry.value(usbSerialRootKey + QString::number(i)).toString();
576 if (driver.contains(QLatin1String("JAVACOMM"))) {
577 driver.replace(QLatin1Char('\\'), QLatin1Char('/'));
578 const QString driverRootKey = QLatin1String("Enum/") + driver + QLatin1Char('/');
579 if (debug > 1)
580 qDebug() << "SerialDeviceLister::serialPorts(): Checking " << i << count
581 << REGKEY_CURRENT_CONTROL_SET << usbSerialRootKey << driverRootKey;
582 QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
583 device->type = SerialPortCommunication;
584 device->friendlyName = registry.value(driverRootKey + QLatin1String("FriendlyName")).toString();
585 device->portName = registry.value(driverRootKey + QLatin1String("Device Parameters/PortName")).toString();
586 device->deviceDesc = registry.value(driverRootKey + QLatin1String("DeviceDesc")).toString();
587 device->manufacturer = registry.value(driverRootKey + QLatin1String("Mfg")).toString();
588 rc.append(SymbianDevice(device.take()));
589 }
590 }
591 #endif
592 return rc;
593 }
594
blueToothDevices() const595 SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::blueToothDevices() const
596 {
597 SymbianDeviceList rc;
598 #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
599 // Bluetooth devices are created on connection. List the existing ones
600 // or at least the first one.
601 const QString prefix = QLatin1String(linuxBlueToothDeviceRootC);
602 const QString blueToothfriendlyFormat = QLatin1String("Bluetooth device (%1)");
603 for (int d = 0; d < 4; d++) {
604 QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
605 device->type = BlueToothCommunication;
606 device->portName = prefix + QString::number(d);
607 if (d == 0 || QFileInfo(device->portName).exists()) {
608 device->friendlyName = blueToothfriendlyFormat.arg(device->portName);
609 rc.push_back(SymbianDevice(device.take()));
610 }
611 }
612 // New kernel versions support /dev/ttyUSB0, /dev/ttyUSB1. Trk responds
613 // on the latter (usually), try first.
614 static const char *usbTtyDevices[] = {
615 "/dev/ttyUSB3", "/dev/ttyUSB2", "/dev/ttyUSB1", "/dev/ttyUSB0",
616 "/dev/ttyACM3", "/dev/ttyACM2", "/dev/ttyACM1", "/dev/ttyACM0"};
617 const int usbTtyCount = sizeof(usbTtyDevices)/sizeof(const char *);
618 for (int d = 0; d < usbTtyCount; d++) {
619 const QString ttyUSBDevice = QLatin1String(usbTtyDevices[d]);
620 if (QFileInfo(ttyUSBDevice).exists()) {
621 SymbianDeviceData *device = new SymbianDeviceData;
622 device->type = SerialPortCommunication;
623 device->portName = ttyUSBDevice;
624 device->friendlyName = QString::fromLatin1("USB/Serial device (%1)").arg(device->portName);
625 rc.push_back(SymbianDevice(device));
626 }
627 }
628 #endif
629 #if defined(Q_OS_MAC)
630 QDir dir("/dev");
631 QStringList filters;
632 filters << "cu.usbmodem*";
633 dir.setNameFilters(filters);
634 QStringList entries = dir.entryList(QDir::System, QDir::Name);
635 foreach (const QString &dev, entries) {
636 SymbianDeviceData *device = new SymbianDeviceData;
637 device->type = SerialPortCommunication;
638 device->portName = dir.filePath(dev);
639 device->friendlyName = tr("USB/Serial device (%1)").arg(device->portName);
640 rc.push_back(SymbianDevice(device));
641 }
642 #endif
643 return rc;
644 }
645
Q_GLOBAL_STATIC(SymbianDeviceManager,symbianDeviceManager)646 Q_GLOBAL_STATIC(SymbianDeviceManager, symbianDeviceManager)
647
648 SymbianDeviceManager *SymbianDeviceManager::instance()
649 {
650 return symbianDeviceManager();
651 }
652
operator <<(QDebug d,const SymbianDeviceManager & sdm)653 SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
654 {
655 d.nospace() << sdm.toString();
656 return d;
657 }
658
getOstChannel(const QString & port,uchar channelId)659 OstChannel *SymbianDeviceManager::getOstChannel(const QString &port, uchar channelId)
660 {
661 CodaDevicePtr coda = getCodaDevice(port);
662 if (coda.isNull() || !coda->device()->isOpen())
663 return 0;
664 return new OstChannel(coda, channelId);
665 }
666
667 struct OstChannelPrivate
668 {
669 CodaDevicePtr m_codaPtr;
670 QByteArray m_dataBuffer;
671 uchar m_channelId;
672 bool m_hasReceivedData;
673 };
674
OstChannel(const CodaDevicePtr & codaPtr,uchar channelId)675 OstChannel::OstChannel(const CodaDevicePtr &codaPtr, uchar channelId)
676 : d(new OstChannelPrivate)
677 {
678 d->m_codaPtr = codaPtr;
679 d->m_channelId = channelId;
680 d->m_hasReceivedData = false;
681 connect(codaPtr.data(), SIGNAL(unknownEvent(uchar, QByteArray)), this, SLOT(ostDataReceived(uchar,QByteArray)));
682 connect(codaPtr->device().data(), SIGNAL(aboutToClose()), this, SLOT(deviceAboutToClose()));
683 QIODevice::open(ReadWrite|Unbuffered);
684 }
685
close()686 void OstChannel::close()
687 {
688 QIODevice::close();
689 if (d && d->m_codaPtr.data()) {
690 disconnect(d->m_codaPtr.data(), 0, this, 0);
691 SymbianDeviceManager::instance()->releaseCodaDevice(d->m_codaPtr);
692 }
693 }
694
~OstChannel()695 OstChannel::~OstChannel()
696 {
697 close();
698 delete d;
699 }
700
flush()701 void OstChannel::flush()
702 {
703 //TODO d->m_codaPtr->device()-
704 }
705
bytesAvailable() const706 qint64 OstChannel::bytesAvailable() const
707 {
708 return d->m_dataBuffer.size();
709 }
710
isSequential() const711 bool OstChannel::isSequential() const
712 {
713 return true;
714 }
715
readData(char * data,qint64 maxSize)716 qint64 OstChannel::readData(char *data, qint64 maxSize)
717 {
718 qint64 amount = qMin(maxSize, (qint64)d->m_dataBuffer.size());
719 qMemCopy(data, d->m_dataBuffer.constData(), amount);
720 d->m_dataBuffer.remove(0, amount);
721 return amount;
722 }
723
writeData(const char * data,qint64 maxSize)724 qint64 OstChannel::writeData(const char *data, qint64 maxSize)
725 {
726 static const qint64 KMaxOstPayload = 1024;
727 // If necessary, split the packet up
728 while (maxSize) {
729 QByteArray dataBuf = QByteArray::fromRawData(data, qMin(KMaxOstPayload, maxSize));
730 d->m_codaPtr->writeCustomData(d->m_channelId, dataBuf);
731 data += dataBuf.length();
732 maxSize -= dataBuf.length();
733 }
734 return maxSize;
735 }
736
ostDataReceived(uchar channelId,const QByteArray & aData)737 void OstChannel::ostDataReceived(uchar channelId, const QByteArray &aData)
738 {
739 if (channelId == d->m_channelId) {
740 d->m_hasReceivedData = true;
741 d->m_dataBuffer.append(aData);
742 emit readyRead();
743 }
744 }
745
codaDevice() const746 Coda::CodaDevice& OstChannel::codaDevice() const
747 {
748 return *d->m_codaPtr;
749 }
750
hasReceivedData() const751 bool OstChannel::hasReceivedData() const
752 {
753 return isOpen() && d->m_hasReceivedData;
754 }
755
deviceAboutToClose()756 void OstChannel::deviceAboutToClose()
757 {
758 close();
759 }
760
761 } // namespace SymbianUtils
762