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

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

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

File size: 15.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "symbiandevicemanager.h"
43#include "trkdevice.h"
44
45#include <QtCore/QSettings>
46#include <QtCore/QStringList>
47#include <QtCore/QFileInfo>
48#include <QtCore/QtDebug>
49#include <QtCore/QTextStream>
50#include <QtCore/QSharedData>
51#include <QtCore/QScopedPointer>
52#include <QtCore/QSignalMapper>
53
54namespace SymbianUtils {
55
56enum { debug = 0 };
57
58static const char REGKEY_CURRENT_CONTROL_SET[] = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet";
59static const char USBSER[] = "Services/usbser/Enum";
60
61const char *SymbianDeviceManager::linuxBlueToothDeviceRootC = "/dev/rfcomm";
62
63// ------------- SymbianDevice
64class SymbianDeviceData : public QSharedData {
65public:
66 SymbianDeviceData();
67 ~SymbianDeviceData();
68
69 inline bool isOpen() const { return !device.isNull() && device->isOpen(); }
70 void forcedClose();
71
72 QString portName;
73 QString friendlyName;
74 QString deviceDesc;
75 QString manufacturer;
76 QString additionalInformation;
77
78 DeviceCommunicationType type;
79 QSharedPointer<trk::TrkDevice> device;
80 bool deviceAcquired;
81};
82
83SymbianDeviceData::SymbianDeviceData() :
84 type(SerialPortCommunication),
85 deviceAcquired(false)
86{
87}
88
89SymbianDeviceData::~SymbianDeviceData()
90{
91 forcedClose();
92}
93
94void SymbianDeviceData::forcedClose()
95{
96 // Close the device when unplugging. Should devices be in 'acquired' state,
97 // their owners should hit on write failures.
98 // Apart from the <shared item> destructor, also called by the devicemanager
99 // to ensure it also happens if other shared instances are still around.
100 if (isOpen()) {
101 if (deviceAcquired)
102 qWarning("Device on '%s' unplugged while an operation is in progress.",
103 qPrintable(portName));
104 device->close();
105 }
106}
107
108SymbianDevice::SymbianDevice(SymbianDeviceData *data) :
109 m_data(data)
110{
111}
112
113SymbianDevice::SymbianDevice() :
114 m_data(new SymbianDeviceData)
115{
116}
117SymbianDevice::SymbianDevice(const SymbianDevice &rhs) :
118 m_data(rhs.m_data)
119{
120}
121
122SymbianDevice &SymbianDevice::operator=(const SymbianDevice &rhs)
123{
124 if (this != &rhs)
125 m_data = rhs.m_data;
126 return *this;
127}
128
129SymbianDevice::~SymbianDevice()
130{
131}
132
133void SymbianDevice::forcedClose()
134{
135 m_data->forcedClose();
136}
137
138QString SymbianDevice::portName() const
139{
140 return m_data->portName;
141}
142
143QString SymbianDevice::friendlyName() const
144{
145 return m_data->friendlyName;
146}
147
148QString SymbianDevice::additionalInformation() const
149{
150 return m_data->additionalInformation;
151}
152
153void SymbianDevice::setAdditionalInformation(const QString &a)
154{
155 m_data->additionalInformation = a;
156}
157
158SymbianDevice::TrkDevicePtr SymbianDevice::acquireDevice()
159{
160 if (debug)
161 qDebug() << "SymbianDevice::acquireDevice" << m_data->portName
162 << "acquired: " << m_data->deviceAcquired << " open: " << isOpen();
163 if (isNull() || m_data->deviceAcquired)
164 return TrkDevicePtr();
165 if (m_data->device.isNull()) {
166 m_data->device = TrkDevicePtr(new trk::TrkDevice);
167 m_data->device->setPort(m_data->portName);
168 m_data->device->setSerialFrame(m_data->type == SerialPortCommunication);
169 }
170 m_data->deviceAcquired = true;
171 return m_data->device;
172}
173
174void SymbianDevice::releaseDevice(TrkDevicePtr *ptr /* = 0 */)
175{
176 if (debug)
177 qDebug() << "SymbianDevice::releaseDevice" << m_data->portName
178 << " open: " << isOpen();
179 if (m_data->deviceAcquired) {
180 if (m_data->device->isOpen())
181 m_data->device->clearWriteQueue();
182 // Release if a valid pointer was passed in.
183 if (ptr && !ptr->isNull()) {
184 ptr->data()->disconnect();
185 *ptr = TrkDevicePtr();
186 }
187 m_data->deviceAcquired = false;
188 } else {
189 qWarning("Internal error: Attempt to release device that is not acquired.");
190 }
191}
192
193QString SymbianDevice::deviceDesc() const
194{
195 return m_data->deviceDesc;
196}
197
198QString SymbianDevice::manufacturer() const
199{
200 return m_data->manufacturer;
201}
202
203DeviceCommunicationType SymbianDevice::type() const
204{
205 return m_data->type;
206}
207
208bool SymbianDevice::isNull() const
209{
210 return m_data->portName.isEmpty();
211}
212
213bool SymbianDevice::isOpen() const
214{
215 return m_data->isOpen();
216}
217
218QString SymbianDevice::toString() const
219{
220 QString rc;
221 QTextStream str(&rc);
222 format(str);
223 return rc;
224}
225
226void SymbianDevice::format(QTextStream &str) const
227{
228 str << (m_data->type == BlueToothCommunication ? "Bluetooth: " : "Serial: ")
229 << m_data->portName;
230 if (!m_data->friendlyName.isEmpty()) {
231 str << " (" << m_data->friendlyName;
232 if (!m_data->deviceDesc.isEmpty())
233 str << " / " << m_data->deviceDesc;
234 str << ')';
235 }
236 if (!m_data->manufacturer.isEmpty())
237 str << " [" << m_data->manufacturer << ']';
238}
239
240// Compare by port and friendly name
241int SymbianDevice::compare(const SymbianDevice &rhs) const
242{
243 if (const int prc = m_data->portName.compare(rhs.m_data->portName))
244 return prc;
245 if (const int frc = m_data->friendlyName.compare(rhs.m_data->friendlyName))
246 return frc;
247 return 0;
248}
249
250SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &cd)
251{
252 d.nospace() << cd.toString();
253 return d;
254}
255
256// ------------- SymbianDeviceManagerPrivate
257struct SymbianDeviceManagerPrivate {
258 SymbianDeviceManagerPrivate() : m_initialized(false), m_destroyReleaseMapper(0) {}
259
260 bool m_initialized;
261 SymbianDeviceManager::SymbianDeviceList m_devices;
262 QSignalMapper *m_destroyReleaseMapper;
263};
264
265SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
266 QObject(parent),
267 d(new SymbianDeviceManagerPrivate)
268{
269}
270
271SymbianDeviceManager::~SymbianDeviceManager()
272{
273 delete d;
274}
275
276SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const
277{
278 ensureInitialized();
279 return d->m_devices;
280}
281
282QString SymbianDeviceManager::toString() const
283{
284 QString rc;
285 QTextStream str(&rc);
286 str << d->m_devices.size() << " devices:\n";
287 const int count = d->m_devices.size();
288 for (int i = 0; i < count; i++) {
289 str << '#' << i << ' ';
290 d->m_devices.at(i).format(str);
291 str << '\n';
292 }
293 return rc;
294}
295
296int SymbianDeviceManager::findByPortName(const QString &p) const
297{
298 ensureInitialized();
299 const int count = d->m_devices.size();
300 for (int i = 0; i < count; i++)
301 if (d->m_devices.at(i).portName() == p)
302 return i;
303 return -1;
304}
305
306QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const
307{
308 const int idx = findByPortName(port);
309 return idx == -1 ? QString() : d->m_devices.at(idx).friendlyName();
310}
311
312SymbianDeviceManager::TrkDevicePtr
313 SymbianDeviceManager::acquireDevice(const QString &port)
314{
315 ensureInitialized();
316 const int idx = findByPortName(port);
317 if (idx == -1) {
318 qWarning("Attempt to acquire device '%s' that does not exist.", qPrintable(port));
319 if (debug)
320 qDebug() << *this;
321 return TrkDevicePtr();
322 }
323 const TrkDevicePtr rc = d->m_devices[idx].acquireDevice();
324 if (debug)
325 qDebug() << "SymbianDeviceManager::acquireDevice" << port << " returns " << !rc.isNull();
326 return rc;
327}
328
329void SymbianDeviceManager::update()
330{
331 update(true);
332}
333
334void SymbianDeviceManager::releaseDevice(const QString &port)
335{
336 const int idx = findByPortName(port);
337 if (debug)
338 qDebug() << "SymbianDeviceManager::releaseDevice" << port << idx << sender();
339 if (idx != -1) {
340 d->m_devices[idx].releaseDevice();
341 } else {
342 qWarning("Attempt to release non-existing device %s.", qPrintable(port));
343 }
344}
345
346void SymbianDeviceManager::setAdditionalInformation(const QString &port, const QString &ai)
347{
348 const int idx = findByPortName(port);
349 if (idx != -1)
350 d->m_devices[idx].setAdditionalInformation(ai);
351}
352
353void SymbianDeviceManager::ensureInitialized() const
354{
355 if (!d->m_initialized) // Flag is set in update()
356 const_cast<SymbianDeviceManager*>(this)->update(false);
357}
358
359void SymbianDeviceManager::update(bool emitSignals)
360{
361 static int n = 0;
362 typedef SymbianDeviceList::iterator SymbianDeviceListIterator;
363
364 if (debug)
365 qDebug(">SerialDeviceLister::update(#%d, signals=%d)\n%s", n++, int(emitSignals),
366 qPrintable(toString()));
367
368 d->m_initialized = true;
369 // Get ordered new list
370 SymbianDeviceList newDevices = serialPorts() + blueToothDevices();
371 if (newDevices.size() > 1)
372 qStableSort(newDevices.begin(), newDevices.end());
373 if (d->m_devices == newDevices) { // Happy, nothing changed.
374 if (debug)
375 qDebug("<SerialDeviceLister::update: unchanged\n");
376 return;
377 }
378 // Merge the lists and emit the respective added/removed signals, assuming
379 // no one can plug a different device on the same port at the speed of lightning
380 if (!d->m_devices.isEmpty()) {
381 // Find deleted devices
382 for (SymbianDeviceListIterator oldIt = d->m_devices.begin(); oldIt != d->m_devices.end(); ) {
383 if (newDevices.contains(*oldIt)) {
384 ++oldIt;
385 } else {
386 SymbianDevice toBeDeleted = *oldIt;
387 toBeDeleted.forcedClose();
388 oldIt = d->m_devices.erase(oldIt);
389 if (emitSignals)
390 emit deviceRemoved(toBeDeleted);
391 }
392 }
393 }
394 if (!newDevices.isEmpty()) {
395 // Find new devices and insert in order
396 foreach(const SymbianDevice &newDevice, newDevices) {
397 if (!d->m_devices.contains(newDevice)) {
398 d->m_devices.append(newDevice);
399 if (emitSignals)
400 emit deviceAdded(newDevice);
401 }
402 }
403 if (d->m_devices.size() > 1)
404 qStableSort(d->m_devices.begin(), d->m_devices.end());
405 }
406 if (emitSignals)
407 emit updated();
408
409 if (debug)
410 qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString()));
411}
412
413SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() const
414{
415 SymbianDeviceList rc;
416#ifdef Q_OS_WIN
417 const QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
418 const QString usbSerialRootKey = QLatin1String(USBSER) + QLatin1Char('/');
419 const int count = registry.value(usbSerialRootKey + QLatin1String("Count")).toInt();
420 for (int i = 0; i < count; ++i) {
421 QString driver = registry.value(usbSerialRootKey + QString::number(i)).toString();
422 if (driver.contains(QLatin1String("JAVACOMM"))) {
423 driver.replace(QLatin1Char('\\'), QLatin1Char('/'));
424 const QString driverRootKey = QLatin1String("Enum/") + driver + QLatin1Char('/');
425 if (debug > 1)
426 qDebug() << "SerialDeviceLister::serialPorts(): Checking " << i << count
427 << REGKEY_CURRENT_CONTROL_SET << usbSerialRootKey << driverRootKey;
428 QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
429 device->type = SerialPortCommunication;
430 device->friendlyName = registry.value(driverRootKey + QLatin1String("FriendlyName")).toString();
431 device->portName = registry.value(driverRootKey + QLatin1String("Device Parameters/PortName")).toString();
432 device->deviceDesc = registry.value(driverRootKey + QLatin1String("DeviceDesc")).toString();
433 device->manufacturer = registry.value(driverRootKey + QLatin1String("Mfg")).toString();
434 rc.append(SymbianDevice(device.take()));
435 }
436 }
437#endif
438 return rc;
439}
440
441SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::blueToothDevices() const
442{
443 SymbianDeviceList rc;
444#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
445 // Bluetooth devices are created on connection. List the existing ones
446 // or at least the first one.
447 const QString prefix = QLatin1String(linuxBlueToothDeviceRootC);
448 const QString blueToothfriendlyFormat = QLatin1String("Bluetooth device (%1)");
449 for (int d = 0; d < 4; d++) {
450 QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
451 device->type = BlueToothCommunication;
452 device->portName = prefix + QString::number(d);
453 if (d == 0 || QFileInfo(device->portName).exists()) {
454 device->friendlyName = blueToothfriendlyFormat.arg(device->portName);
455 rc.push_back(SymbianDevice(device.take()));
456 }
457 }
458 // New kernel versions support /dev/ttyUSB0, /dev/ttyUSB1. Trk responds
459 // on the latter (usually), try first.
460 static const char *usbTtyDevices[] = { "/dev/ttyUSB1", "/dev/ttyUSB0" };
461 const int usbTtyCount = sizeof(usbTtyDevices)/sizeof(const char *);
462 for (int d = 0; d < usbTtyCount; d++) {
463 const QString ttyUSBDevice = QLatin1String(usbTtyDevices[d]);
464 if (QFileInfo(ttyUSBDevice).exists()) {
465 SymbianDeviceData *device = new SymbianDeviceData;
466 device->type = SerialPortCommunication;
467 device->portName = ttyUSBDevice;
468 device->friendlyName = QString::fromLatin1("USB/Serial device (%1)").arg(device->portName);
469 rc.push_back(SymbianDevice(device));
470 }
471 }
472#endif
473 return rc;
474}
475
476Q_GLOBAL_STATIC(SymbianDeviceManager, symbianDeviceManager)
477
478SymbianDeviceManager *SymbianDeviceManager::instance()
479{
480 return symbianDeviceManager();
481}
482
483SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
484{
485 d.nospace() << sdm.toString();
486 return d;
487}
488
489} // namespace SymbianUtilsInternal
Note: See TracBrowser for help on using the repository browser.