diff options
-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; +} +} |