diff options
Diffstat (limited to 'src/bluetooth/qbluetoothserver_win.cpp')
-rw-r--r-- | src/bluetooth/qbluetoothserver_win.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/src/bluetooth/qbluetoothserver_win.cpp b/src/bluetooth/qbluetoothserver_win.cpp new file mode 100644 index 00000000..70695112 --- /dev/null +++ b/src/bluetooth/qbluetoothserver_win.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbluetoothserver.h" +#include "qbluetoothserver_p.h" +#include "qbluetoothsocket.h" +#include "qbluetoothlocaldevice.h" + +#include <QtCore/QLoggingCategory> +#include <QtCore/QSocketNotifier> + +#include <winsock2.h> +#include <ws2bth.h> +#include <bluetoothapis.h> +#include <errno.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) + +QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType, + QBluetoothServer *parent) + : maxPendingConnections(1), serverType(sType), q_ptr(parent), + m_lastError(QBluetoothServer::NoError) +{ + Q_Q(QBluetoothServer); + Q_ASSERT(sType == QBluetoothServiceInfo::RfcommProtocol); + socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, q); +} + +QBluetoothServerPrivate::~QBluetoothServerPrivate() +{ +} + +void QBluetoothServerPrivate::_q_newConnection() +{ + // disable socket notifier until application calls nextPendingConnection(). + socketNotifier->setEnabled(false); + + emit q_ptr->newConnection(); +} + +void QBluetoothServer::close() +{ + Q_D(QBluetoothServer); + + delete d->socketNotifier; + d->socketNotifier = nullptr; + + d->socket->close(); +} + +bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) +{ + Q_D(QBluetoothServer); + + if (d->serverType != QBluetoothServiceInfo::RfcommProtocol) { + qCWarning(QT_BT_WINDOWS) << "Protocol is not supported."; + d->m_lastError = QBluetoothServer::UnsupportedProtocolError; + emit error(d->m_lastError); + return false; + } + + if (d->socket->state() == QBluetoothSocket::ListeningState) { + qCWarning(QT_BT_WINDOWS) << "Socket already in listen mode, close server first"; + return false; + } + + const QBluetoothLocalDevice device(address); + if (!device.isValid()) { + qCWarning(QT_BT_WINDOWS) << "Device does not support Bluetooth or" + << address.toString() << "is not a valid local adapter"; + d->m_lastError = QBluetoothServer::UnknownError; + emit error(d->m_lastError); + return false; + } + + const QBluetoothLocalDevice::HostMode hostMode = device.hostMode(); + if (hostMode == QBluetoothLocalDevice::HostPoweredOff) { + d->m_lastError = QBluetoothServer::PoweredOffError; + emit error(d->m_lastError); + qCWarning(QT_BT_WINDOWS) << "Bluetooth device is powered off"; + return false; + } + + int sock = d->socket->socketDescriptor(); + if (sock < 0) { + /* Negative socket descriptor is not always an error case. + * Another cause could be a call to close()/abort(). + * Check whether we can recover by re-creating the socket. + */ + delete d->socket; + d->socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); + sock = d->socket->socketDescriptor(); + if (sock < 0) { + d->m_lastError = InputOutputError; + emit error(d->m_lastError); + return false; + } + } + + if (sock < 0) + return false; + + SOCKADDR_BTH addr = {}; + addr.addressFamily = AF_BTH; + addr.port = (port == 0) ? BT_PORT_ANY : port; + addr.btAddr = address.toUInt64(); + + if (::bind(sock, reinterpret_cast<sockaddr *>(&addr), sizeof(SOCKADDR_BTH)) < 0) { + if (errno == EADDRINUSE) + d->m_lastError = ServiceAlreadyRegisteredError; + else + d->m_lastError = InputOutputError; + emit error(d->m_lastError); + return false; + } + + if (::listen(sock, d->maxPendingConnections) < 0) { + d->m_lastError = InputOutputError; + emit error(d->m_lastError); + return false; + } + + d->socket->setSocketState(QBluetoothSocket::ListeningState); + + if (!d->socketNotifier) { + d->socketNotifier = new QSocketNotifier(d->socket->socketDescriptor(), + QSocketNotifier::Read, this); + connect(d->socketNotifier, &QSocketNotifier::activated, this, [d](){ + d->_q_newConnection(); + }); + } + + return true; +} + +void QBluetoothServer::setMaxPendingConnections(int numConnections) +{ + Q_UNUSED(numConnections); +} + +bool QBluetoothServer::hasPendingConnections() const +{ + Q_D(const QBluetoothServer); + + if (!d || !d->socketNotifier) + return false; + + // if the socket notifier is disabled there is a pending connection waiting for us to accept. + return !d->socketNotifier->isEnabled(); +} + +QBluetoothSocket *QBluetoothServer::nextPendingConnection() +{ + Q_D(QBluetoothServer); + + if (!hasPendingConnections()) + return nullptr; + + if (d->serverType != QBluetoothServiceInfo::RfcommProtocol) + return nullptr; + + SOCKADDR_BTH addr = {}; + int length = sizeof(SOCKADDR_BTH); + int pending = ::accept(d->socket->socketDescriptor(), + reinterpret_cast<sockaddr *>(&addr), &length); + + QBluetoothSocket *newSocket = nullptr; + + if (pending >= 0) { + newSocket = new QBluetoothSocket(); + newSocket->setSocketDescriptor(pending, QBluetoothServiceInfo::RfcommProtocol); + } + + d->socketNotifier->setEnabled(true); + return newSocket; +} + +QBluetoothAddress QBluetoothServer::serverAddress() const +{ + Q_D(const QBluetoothServer); + + return d->socket->localAddress(); +} + +quint16 QBluetoothServer::serverPort() const +{ + Q_D(const QBluetoothServer); + + return d->socket->localPort(); +} + +void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security) +{ + Q_UNUSED(security); +} + +QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const +{ + return QBluetooth::NoSecurity; +} + +QT_END_NAMESPACE |