summaryrefslogtreecommitdiffstats
path: root/plugins/sensors/blackberry/bbsensorbackend.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/sensors/blackberry/bbsensorbackend.cpp')
-rw-r--r--plugins/sensors/blackberry/bbsensorbackend.cpp401
1 files changed, 401 insertions, 0 deletions
diff --git a/plugins/sensors/blackberry/bbsensorbackend.cpp b/plugins/sensors/blackberry/bbsensorbackend.cpp
new file mode 100644
index 0000000000..3b6b581096
--- /dev/null
+++ b/plugins/sensors/blackberry/bbsensorbackend.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Research In Motion <blackberry-qt@qnx.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "bbsensorbackend.h"
+
+#include "bbguihelper.h"
+#include <QtCore/QDebug>
+#include <QtCore/qmath.h>
+#include <qorientablesensorbase.h>
+#include <fcntl.h>
+
+static const int microSecondsPerSecond = 1000 * 1000;
+static const int defaultBufferSize = 10;
+
+static int microSecondsToHertz(uint microSeconds)
+{
+ return microSecondsPerSecond / microSeconds;
+}
+
+static uint hertzToMicroSeconds(int hertz)
+{
+ return microSecondsPerSecond / hertz;
+}
+
+static void remapMatrix(const float inputMatrix[3*3],
+ const float mappingMatrix[4],
+ float outputMatrix[3*3])
+{
+ int i,j,k;
+
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 2; j++) { //only goto 2 because last column stays unchanged
+
+ outputMatrix[i*3+j] = 0;
+
+ for (k = 0; k < 2; k++) { //only goto 2 because we know rotation matrix is zero in bottom row
+ outputMatrix[i*3+j] += inputMatrix[i*3+k] * mappingMatrix[k*2+j];
+ }
+ }
+
+ outputMatrix[i*3+2] = inputMatrix[i*3+2];
+ }
+}
+
+BbSensorBackendBase::BbSensorBackendBase(const QString &devicePath, sensor_type_e sensorType,
+ QSensor *sensor)
+ : QSensorBackend(sensor), m_deviceFile(devicePath), m_sensorType(sensorType), m_guiHelper(0),
+ m_started(false)
+{
+ m_mappingMatrix[0] = m_mappingMatrix[3] = 1;
+ m_mappingMatrix[1] = m_mappingMatrix[2] = 0;
+ connect(sensor, SIGNAL(alwaysOnChanged()), this, SLOT(applyAlwaysOnProperty()));
+ QOrientableSensorBase * const base = dynamic_cast<QOrientableSensorBase*>(sensor);
+ if (base)
+ connect(sensor, SIGNAL(userOrientationChanged(int)), this, SLOT(updateOrientation()));
+
+ // Set some sensible default values
+ sensor->setProperty("efficientBufferSize", defaultBufferSize);
+ sensor->setProperty("maxBufferSize", defaultBufferSize);
+}
+
+BbGuiHelper *BbSensorBackendBase::guiHelper() const
+{
+ return m_guiHelper;
+}
+
+QFile &BbSensorBackendBase::deviceFile()
+{
+ return m_deviceFile;
+}
+
+sensor_type_e BbSensorBackendBase::sensorType() const
+{
+ return m_sensorType;
+}
+
+void BbSensorBackendBase::setDevice(const QString &deviceFile, sensor_type_e sensorType)
+{
+ const bool isActive = m_deviceFile.isOpen();
+ if (isActive)
+ stop();
+
+ m_sensorType = sensorType;
+ m_deviceFile.setFileName(deviceFile);
+
+ if (isActive)
+ start();
+}
+
+void BbSensorBackendBase::initSensorInfo()
+{
+ if (!m_deviceFile.open(QFile::ReadOnly | QFile::Unbuffered)) {
+ qDebug() << "Failed to open sensor" << m_deviceFile.fileName()
+ << ":" << m_deviceFile.errorString();
+ } else {
+ sensor_devctl_info_u deviceInfo;
+ const int result = devctl(m_deviceFile.handle(), DCMD_SENSOR_INFO, &deviceInfo,
+ sizeof(deviceInfo), NULL);
+ if (result != EOK) {
+ perror(QString::fromLatin1("Querying sensor info for %1 failed")
+ .arg(m_deviceFile.fileName()).toLocal8Bit());
+ } else {
+ if (addDefaultRange()) {
+ addOutputRange(convertValue(deviceInfo.rx.info.range_min),
+ convertValue(deviceInfo.rx.info.range_max),
+ convertValue(deviceInfo.rx.info.resolution));
+ }
+
+ // Min and max intentionally swapped here, as the minimum delay is the maximum rate
+ if (deviceInfo.rx.info.delay_max > 0 && deviceInfo.rx.info.delay_min > 0) {
+ addDataRate(microSecondsToHertz(deviceInfo.rx.info.delay_max),
+ microSecondsToHertz(deviceInfo.rx.info.delay_min));
+ }
+ }
+ additionalDeviceInit();
+
+ // Instead of closing the device here and opening it again in start(), just pause the sensor.
+ // This avoids an expensive close() and open() call.
+ setPaused(true);
+
+ m_socketNotifier.reset(new QSocketNotifier(m_deviceFile.handle(), QSocketNotifier::Read));
+ connect(m_socketNotifier.data(), SIGNAL(activated(int)), this, SLOT(dataAvailable()));
+ }
+}
+
+void BbSensorBackendBase::setGuiHelper(BbGuiHelper *guiHelper)
+{
+ Q_ASSERT(!m_guiHelper);
+ m_guiHelper = guiHelper;
+ connect(m_guiHelper, SIGNAL(applicationActiveChanged()), this, SLOT(updatePauseState()));
+ connect(m_guiHelper, SIGNAL(orientationChanged()), this, SLOT(updateOrientation()));
+ updateOrientation();
+}
+
+void BbSensorBackendBase::additionalDeviceInit()
+{
+}
+
+bool BbSensorBackendBase::addDefaultRange()
+{
+ return true;
+}
+
+qreal BbSensorBackendBase::convertValue(float bbValue)
+{
+ return bbValue;
+}
+
+bool BbSensorBackendBase::isAutoAxisRemappingEnabled() const
+{
+ const QOrientableSensorBase * const base = dynamic_cast<QOrientableSensorBase*>(sensor());
+ return base && base->axesOrientationMode() != QOrientableSensorBase::FixedOrientation;
+}
+
+void BbSensorBackendBase::remapMatrix(const float inputMatrix[], float outputMatrix[])
+{
+ if (!isAutoAxisRemappingEnabled() || orientationForRemapping() == 0) {
+ memcpy(outputMatrix, inputMatrix, sizeof(float) * 9);
+ return;
+ }
+
+ ::remapMatrix(inputMatrix, m_mappingMatrix, outputMatrix);
+}
+
+void BbSensorBackendBase::remapAxes(float *x, float *y, float *z)
+{
+ Q_ASSERT(x && y && z);
+ if (!isAutoAxisRemappingEnabled() || orientationForRemapping() == 0)
+ return;
+
+ const int angle = orientationForRemapping();
+
+ const float oldX = *x;
+ const float oldY = *y;
+
+ switch (angle) {
+ case 90:
+ *x = -oldY;
+ *y = oldX;
+ break;
+ case 180:
+ *x = -oldX;
+ *y = -oldY;
+ break;
+ case 270:
+ *x = oldY;
+ *y = -oldX;
+ break;
+ }
+}
+
+void BbSensorBackendBase::start()
+{
+ Q_ASSERT(m_guiHelper);
+
+ if (!m_deviceFile.isOpen() || !setPaused(false)) {
+ qDebug() << "Starting sensor" << m_deviceFile.fileName()
+ << "failed:" << m_deviceFile.errorString();
+ sensorError(m_deviceFile.error());
+ return;
+ }
+ m_started = true;
+
+ const int rateInHertz = sensor()->dataRate();
+ if (rateInHertz != 0) {
+ const uint rateInMicroseconds = hertzToMicroSeconds(rateInHertz);
+ sensor_devctl_rate_u deviceRate;
+ deviceRate.tx.rate = rateInMicroseconds;
+ const int result = devctl(m_deviceFile.handle(), DCMD_SENSOR_RATE, &deviceRate,
+ sizeof(deviceRate), NULL);
+ if (result != EOK) {
+ sensor()->setDataRate(0);
+ perror(QString::fromLatin1("Setting sensor rate for %1 failed")
+ .arg(m_deviceFile.fileName()).toLocal8Bit());
+ } else {
+ if (deviceRate.rx.rate > 0)
+ sensor()->setDataRate(microSecondsToHertz(deviceRate.rx.rate));
+ else
+ sensor()->setDataRate(0);
+ }
+ }
+
+ // Enable/disable duplicate skipping
+ sensor_devctl_skipdupevent_u deviceSkip;
+ deviceSkip.tx.enable = sensor()->skipDuplicates() ? 1 : 0;
+ const int result = devctl(deviceFile().handle(), DCMD_SENSOR_SKIPDUPEVENT, &deviceSkip,
+ sizeof(deviceSkip), NULL);
+ if (result != EOK) {
+ perror(QString::fromLatin1("Setting duplicate skipping for %1 failed")
+ .arg(m_deviceFile.fileName()).toLocal8Bit());
+ }
+
+ // Explicitly switch to non-blocking mode, otherwise read() will wait until new sensor
+ // data is available, and we have no way to check if there is more data or not (bytesAvailable()
+ // does not work for unbuffered mode)
+ const int oldFlags = fcntl(m_deviceFile.handle(), F_GETFL);
+ if (fcntl(m_deviceFile.handle(), F_SETFL, oldFlags | O_NONBLOCK) == -1) {
+ perror(QString::fromLatin1("Starting sensor %1 failed, fcntl() returned -1")
+ .arg(m_deviceFile.fileName()).toLocal8Bit());
+ sensorError(errno);
+ stop();
+ return;
+ }
+
+ // Activate event queuing if needed
+ bool ok = false;
+ const int requestedBufferSize = sensor()->property("bufferSize").toInt(&ok);
+ if (ok && requestedBufferSize > 1) {
+ sensor_devctl_queue_u queueControl;
+ queueControl.tx.enable = 1;
+ const int result = devctl(m_deviceFile.handle(), DCMD_SENSOR_QUEUE, &queueControl, sizeof(queueControl), NULL);
+ if (result != EOK) {
+ perror(QString::fromLatin1("Enabling sensor queuing for %1 failed")
+ .arg(m_deviceFile.fileName()).toLocal8Bit());
+ }
+
+ const int actualBufferSize = queueControl.rx.size;
+ sensor()->setProperty("bufferSize", actualBufferSize);
+ sensor()->setProperty("efficientBufferSize", actualBufferSize);
+ sensor()->setProperty("maxBufferSize", actualBufferSize);
+ }
+
+ applyAlwaysOnProperty();
+}
+
+void BbSensorBackendBase::stop()
+{
+ setPaused(true);
+ m_started = false;
+}
+
+void BbSensorBackendBase::dataAvailable()
+{
+ if (!m_started)
+ return;
+
+ Q_FOREVER {
+ sensor_event_t event;
+ const qint64 numBytes = m_deviceFile.read(reinterpret_cast<char *>(&event),
+ sizeof(sensor_event_t));
+ if (numBytes == -1) {
+ break;
+ } else if (numBytes == sizeof(sensor_event_t)) {
+ processEvent(event);
+ } else {
+ qDebug() << "Reading sensor event data for" << m_deviceFile.fileName()
+ << "failed (unexpected data size):" << m_deviceFile.errorString();
+ }
+ }
+}
+
+void BbSensorBackendBase::applyAlwaysOnProperty()
+{
+ if (!m_deviceFile.isOpen() || !m_started)
+ return;
+
+ sensor_devctl_bkgrnd_u bgState;
+ bgState.tx.enable = sensor()->isAlwaysOn() ? 1 : 0;
+
+ const int result = devctl(m_deviceFile.handle(), DCMD_SENSOR_BKGRND, &bgState, sizeof(bgState), NULL);
+ if (result != EOK) {
+ perror(QString::fromLatin1("Setting sensor always on for %1 failed")
+ .arg(m_deviceFile.fileName()).toLocal8Bit());
+ }
+
+ // We might need to pause now
+ updatePauseState();
+}
+
+bool BbSensorBackendBase::setPaused(bool paused)
+{
+ if (!m_deviceFile.isOpen())
+ return false;
+
+ sensor_devctl_enable_u enableState;
+ enableState.tx.enable = paused ? 0 : 1;
+
+ const int result = devctl(m_deviceFile.handle(), DCMD_SENSOR_ENABLE, &enableState, sizeof(enableState), NULL);
+ if (result != EOK) {
+ perror(QString::fromLatin1("Setting sensor enabled (%1) for %2 failed")
+ .arg(paused)
+ .arg(m_deviceFile.fileName()).toLocal8Bit());
+ return false;
+ }
+
+ return true;
+}
+
+void BbSensorBackendBase::updatePauseState()
+{
+ if (!m_started)
+ return;
+
+ setPaused(!sensor()->isAlwaysOn() && !m_guiHelper->applicationActive());
+}
+
+void BbSensorBackendBase::updateOrientation()
+{
+ // ### I can't really test this, the rotation matrix has too many glitches and drifts over time,
+ // making any measurement quite hard
+ const int rotationAngle = orientationForRemapping();
+
+ m_mappingMatrix[0] = cos(rotationAngle*M_PI/180);
+ m_mappingMatrix[1] = sin(rotationAngle*M_PI/180);
+ m_mappingMatrix[2] = -sin(rotationAngle*M_PI/180);
+ m_mappingMatrix[3] = cos(rotationAngle*M_PI/180);
+
+ QOrientableSensorBase * const base = dynamic_cast<QOrientableSensorBase*>(sensor());
+ if (base)
+ base->setCurrentOrientation(rotationAngle);
+}
+
+int BbSensorBackendBase::orientationForRemapping() const
+{
+ const QOrientableSensorBase * const base = dynamic_cast<QOrientableSensorBase*>(sensor());
+ if (!base)
+ return 0;
+
+ switch (base->axesOrientationMode()) {
+ case QOrientableSensorBase::FixedOrientation: return 0;
+ case QOrientableSensorBase::AutomaticOrientation: return guiHelper()->currentOrientation();
+ case QOrientableSensorBase::UserOrientation: return base->userOrientation();
+ }
+}