diff options
author | Claudiu Olteanu <olteanu.claudiu@ymail.com> | 2015-07-06 17:07:34 +0300 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2015-07-06 08:37:42 -0700 |
commit | 198cc419596dbd17f65124d0495e98fb7acf81c2 (patch) | |
tree | a0bb99131eb82932f6f0894c95b85310a25e6ac5 /qtserialbluetooth.cpp | |
parent | dff4e5f33eee0ba112ddd2265410a5e858d6c98f (diff) | |
download | subsurface-198cc419596dbd17f65124d0495e98fb7acf81c2.tar.gz |
Implement the custom Bluetooth serial communication and use it
Create a custom Bluetooth serial communication using the QTBluetooth
API and use it when the Bluetooth download mode is enabled.
First try to connect on RFCOMM channel 1 because this is the default
RFCOMM channel of SPP service for most devices. If this doesn't work
try again on RFCOMM channel number 5 because it could be a Petrel2 device.
Add a fake open function for the custom implementation. This is
used when the selected device is HW OSTC 2N and the Bluetooth
mode is activated, then fake the open call of the serial device.
Signed-off-by: Claudiu Olteanu <olteanu.claudiu@ymail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'qtserialbluetooth.cpp')
-rw-r--r-- | qtserialbluetooth.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/qtserialbluetooth.cpp b/qtserialbluetooth.cpp new file mode 100644 index 000000000..b71845657 --- /dev/null +++ b/qtserialbluetooth.cpp @@ -0,0 +1,240 @@ +#include <errno.h> + +#include <QtBluetooth/QBluetoothAddress> +#include <QtBluetooth/QBluetoothSocket> +#include <QEventLoop> +#include <QTimer> + +#include <libdivecomputer/custom_serial.h> + +extern "C" { +typedef struct serial_t { + /* Library context. */ + dc_context_t *context; + /* + * RFCOMM socket used for Bluetooth Serial communication. + */ + QBluetoothSocket *socket; + long timeout; +} serial_t; + +static int qt_serial_open(serial_t **out, dc_context_t *context, const char* devaddr) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + serial_t *serial_port = (serial_t *) malloc (sizeof (serial_t)); + if (serial_port == NULL) { + return DC_STATUS_NOMEMORY; + } + + // Library context. + serial_port->context = context; + + // Default to blocking reads. + serial_port->timeout = -1; + + // Create a RFCOMM socket + serial_port->socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); + + // Wait until the connection succeeds or until an error occurs + QEventLoop loop; + loop.connect(serial_port->socket, SIGNAL(connected()), SLOT(quit())); + loop.connect(serial_port->socket, SIGNAL(error(QBluetoothSocket::SocketError)), SLOT(quit())); + + // Create a timer. If the connection doesn't succeed after five seconds or no error occurs then stop the opening step + QTimer timer; + int msec = 5000; + timer.setSingleShot(true); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + + // First try to connect on RFCOMM channel 1. This is the default channel for most devices + QBluetoothAddress remoteDeviceAddress(devaddr); + serial_port->socket->connectToService(remoteDeviceAddress, 1); + timer.start(msec); + loop.exec(); + + if (serial_port->socket->state() == QBluetoothSocket::ConnectingState) { + // It seems that the connection on channel 1 took more than expected. Wait another 15 seconds + timer.start(3 * msec); + loop.exec(); + } else if (serial_port->socket->state() == QBluetoothSocket::UnconnectedState) { + // Try to connect on channel number 5. Maybe this is a Shearwater Petrel2 device. + serial_port->socket->connectToService(remoteDeviceAddress, 5); + timer.start(msec); + loop.exec(); + + if (serial_port->socket->state() == QBluetoothSocket::ConnectingState) { + // It seems that the connection on channel 5 took more than expected. Wait another 15 seconds + timer.start(3 * msec); + loop.exec(); + } + } + + if (serial_port->socket->socketDescriptor() == -1 || serial_port->socket->state() != QBluetoothSocket::ConnectedState) { + free (serial_port); + + // Get the latest error and try to match it with one from libdivecomputer + QBluetoothSocket::SocketError err = serial_port->socket->error(); + switch(err) { + case QBluetoothSocket::HostNotFoundError: + case QBluetoothSocket::ServiceNotFoundError: + return DC_STATUS_NODEVICE; + case QBluetoothSocket::UnsupportedProtocolError: + return DC_STATUS_PROTOCOL; + case QBluetoothSocket::OperationError: + return DC_STATUS_UNSUPPORTED; + case QBluetoothSocket::NetworkError: + return DC_STATUS_IO; + default: + return QBluetoothSocket::UnknownSocketError; + } + } + + *out = serial_port; + + return DC_STATUS_SUCCESS; +} + +static int qt_serial_close(serial_t *device) +{ + if (device == NULL || device->socket == NULL) + return DC_STATUS_SUCCESS; + + device->socket->close(); + + delete device->socket; + free(device); + + return DC_STATUS_SUCCESS; +} + +static int qt_serial_read(serial_t *device, void* data, unsigned int size) +{ + if (device == NULL || device->socket == NULL) + return DC_STATUS_INVALIDARGS; + + unsigned int nbytes = 0, rc; + + while(nbytes < size) + { + device->socket->waitForReadyRead(device->timeout); + + rc = device->socket->read((char *) data + nbytes, size - nbytes); + + if (rc < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; // Retry. + + return -1; // Something really bad happened :-( + } else if (rc == 0) { + // Wait until the device is available for read operations + QEventLoop loop; + loop.connect(device->socket, SIGNAL(readyRead()), SLOT(quit())); + loop.exec(); + } + + nbytes += rc; + } + + return nbytes; +} + +static int qt_serial_write(serial_t *device, const void* data, unsigned int size) +{ + if (device == NULL || device->socket == NULL) + return DC_STATUS_INVALIDARGS; + + unsigned int nbytes = 0, rc; + + while(nbytes < size) + { + device->socket->waitForBytesWritten(device->timeout); + + rc = device->socket->write((char *) data + nbytes, size - nbytes); + + if (rc < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; // Retry. + + return -1; // Something really bad happened :-( + } else if (rc == 0) { + break; + } + + nbytes += rc; + } + + return nbytes; +} + +static int qt_serial_flush(serial_t *device, int queue) +{ + if (device == NULL || device->socket == NULL) + return DC_STATUS_INVALIDARGS; + + //TODO: add implementation + + return DC_STATUS_SUCCESS; +} + +static int qt_serial_get_received(serial_t *device) +{ + if (device == NULL || device->socket == NULL) + return DC_STATUS_INVALIDARGS; + + return device->socket->bytesAvailable(); +} + +static int qt_serial_get_transmitted(serial_t *device) +{ + if (device == NULL || device->socket == NULL) + return DC_STATUS_INVALIDARGS; + + return device->socket->bytesToWrite(); +} + + +const dc_serial_operations_t qt_serial_ops = { + .open = qt_serial_open, + .close = qt_serial_close, + .read = qt_serial_read, + .write = qt_serial_write, + .flush = qt_serial_flush, + .get_received = qt_serial_get_received, + .get_transmitted = qt_serial_get_transmitted +}; + +extern void dc_serial_init (dc_serial_t *serial, void *data, const dc_serial_operations_t *ops); + +dc_status_t dc_serial_qt_open(dc_serial_t **out, dc_context_t *context, const char *devaddr) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + dc_serial_t *serial_device = (dc_serial_t *) malloc (sizeof (dc_serial_t)); + + if (serial_device == NULL) { + return DC_STATUS_NOMEMORY; + } + + // Initialize data and function pointers + dc_serial_init(serial_device, NULL, &qt_serial_ops); + + // Open the serial device. + dc_status_t rc = (dc_status_t)qt_serial_open (&serial_device->port, context, devaddr); + if (rc != DC_STATUS_SUCCESS) { + free (serial_device); + return rc; + } + + // Set the type of the device + serial_device->type = DC_TRANSPORT_BLUETOOTH; + + *out = serial_device; + + return DC_STATUS_SUCCESS; +} +} |