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 | |
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>
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | configuredivecomputerthreads.cpp | 10 | ||||
-rw-r--r-- | libdivecomputer.c | 22 | ||||
-rw-r--r-- | libdivecomputer.h | 1 | ||||
-rw-r--r-- | qtserialbluetooth.cpp | 240 |
5 files changed, 271 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index eac98539f..77d202661 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,6 +314,7 @@ set(SUBSURFACE_CORE_LIB_SRCS windowtitleupdate.cpp divelogexportlogic.cpp qt-init.cpp + qtserialbluetooth.cpp ${PLATFORM_SRC} ) source_group("Subsurface Core" FILES ${SUBSURFACE_CORE_LIB_SRCS}) diff --git a/configuredivecomputerthreads.cpp b/configuredivecomputerthreads.cpp index 5c610db96..e07466018 100644 --- a/configuredivecomputerthreads.cpp +++ b/configuredivecomputerthreads.cpp @@ -82,6 +82,16 @@ static dc_status_t local_dc_device_open(dc_device_t **out, dc_context_t *context } #define dc_device_open local_dc_device_open +// Fake the custom open function +static dc_status_t local_dc_device_custom_open(dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, dc_serial_t *serial) +{ + if (strcmp(dc_descriptor_get_vendor(descriptor), "Heinrichs Weikamp") == 0 &&strcmp(dc_descriptor_get_product(descriptor), "OSTC 2N") == 0) + return DC_STATUS_SUCCESS; + else + return dc_device_custom_open(out, context, descriptor, serial); +} +#define dc_device_custom_open local_dc_device_custom_open + static dc_status_t local_hw_ostc_device_eeprom_read(void *ignored, unsigned char bank, unsigned char data[], unsigned int data_size) { FILE *f; diff --git a/libdivecomputer.c b/libdivecomputer.c index 24f4d0ff5..e308c132d 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -923,14 +923,30 @@ const char *do_libdivecomputer_import(device_data_t *data) } err = translate("gettextFromC", "Unable to open %s %s (%s)"); - rc = dc_device_open(&data->device, data->context, data->descriptor, data->devname); + + if (data->bluetooth_mode) { + dc_serial_t *serial_device; + + rc = dc_serial_qt_open(&serial_device, data->context, data->devname); + if (rc == DC_STATUS_SUCCESS) { + rc = dc_device_custom_open(&data->device, data->context, data->descriptor, serial_device); + } else { + report_error(errmsg(rc)); + } + + } else { + rc = dc_device_open(&data->device, data->context, data->descriptor, data->devname); + + if (rc != DC_STATUS_SUCCESS && subsurface_access(data->devname, R_OK | W_OK) != 0) + err = translate("gettextFromC", "Insufficient privileges to open the device %s %s (%s)"); + } + if (rc == DC_STATUS_SUCCESS) { err = do_device_import(data); /* TODO: Show the logfile to the user on error. */ dc_device_close(data->device); data->device = NULL; - } else if (subsurface_access(data->devname, R_OK | W_OK) != 0) - err = translate("gettextFromC", "Insufficient privileges to open the device %s %s (%s)"); + } dc_context_free(data->context); data->context = NULL; diff --git a/libdivecomputer.h b/libdivecomputer.h index f5c0cadbe..649d89847 100644 --- a/libdivecomputer.h +++ b/libdivecomputer.h @@ -53,6 +53,7 @@ extern const char *progress_bar_text; extern double progress_bar_fraction; extern char *logfile_name; extern char *dumpfile_name; +extern dc_status_t dc_serial_qt_open(dc_serial_t **out, dc_context_t *context, const char *devaddr); #ifdef __cplusplus } 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; +} +} |