Hello,

I need some help to test if the native serial implementation
works after the changes I did on the libdivecomputer project.

If you have a device from HW_OSTC3 or SHEARWATER families
please take the first 7 patches and apply them on the libdivecomputer
branch.

Unfortunately I don't have a SHEARWATER device so the only think
I did was to make sure that the sources compile after my modifications.

If you have a dive computer with Bluetooth support you can take the
the last 4 patches[*] and apply them on the Subsurface branch.
The Bluetooth support should work on Linux/OS X platforms.

For devices that require a custom PIN code on the pairing
step you should start a keyboard agent. One possible solution is
to use *bluetoothctl* tool and to run the following commands:
*# agent KeyboardOnly*
*# default-agent*

Also it would be useful if you could run the *hcidump *tool in
background.

Please let me know if you have any questions.

Thanks,
Claudiu


[*] Just to be sure that there is no confusion, the following patches
should be applied on Subsurface project:
- 0001-Add-checkbox-and-button-for-Bluetooth-download-mode.patch
- 0002-Add-a-new-dialog-which-can-be-use-to-select-the-Blue.patch
- 0003-Implement-the-custom-Bluetooth-serial-communication-.patch
- 0004-Enable-QtBluetooth-logging.patch
From 05d830371f2bce35820bebb7419ed27946b3a658 Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sat, 27 Jun 2015 15:11:15 +0300
Subject: [PATCH 1/7] Extend the transport enum descriptor for serial
 communication

Add a new transport type which can be used to identify
Bluetooth serial communication.

Signed-off-by Claudiu Oleanu <olteanu.clau...@ymail.com>
---
 include/libdivecomputer/descriptor.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/libdivecomputer/descriptor.h b/include/libdivecomputer/descriptor.h
index 6f9735d..f1c815d 100644
--- a/include/libdivecomputer/descriptor.h
+++ b/include/libdivecomputer/descriptor.h
@@ -33,7 +33,8 @@ typedef enum dc_transport_t {
 	DC_TRANSPORT_NONE,
 	DC_TRANSPORT_SERIAL,
 	DC_TRANSPORT_USB,
-	DC_TRANSPORT_IRDA
+	DC_TRANSPORT_IRDA,
+	DC_TRANSPORT_BLUETOOTH
 } dc_transport_t;
 
 typedef struct dc_descriptor_t dc_descriptor_t;
-- 
2.1.4

From b4092f3b4b04f9b31097f6c79b2e1cdafcaf5a06 Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sat, 27 Jun 2015 15:14:16 +0300
Subject: [PATCH 2/7] Create a generic way to represent any type of serial
 communication

Add a structure which holds references to basic operations
on a serial communication. This can be used to pass a set
of function pointer callbacks in order to create a custom
implementation for serial communication.

Add a generic structure to represent the needed information
for a serial communication.

Implement the initialization method where the user can
pass a set of function pointer callbacks and set some
custom data for the serial device.

Create open method for the native serial implementation.

Signed-off-by: Claudiu Olteanu <olteanu.clau...@ymail.com>
---
 include/libdivecomputer/Makefile.am     |  3 +-
 include/libdivecomputer/custom_serial.h | 63 ++++++++++++++++++++++++++
 src/Makefile.am                         |  3 +-
 src/custom_serial.c                     | 79 +++++++++++++++++++++++++++++++++
 4 files changed, 146 insertions(+), 2 deletions(-)
 create mode 100644 include/libdivecomputer/custom_serial.h
 create mode 100644 src/custom_serial.c

diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am
index 71887d5..a0ed196 100644
--- a/include/libdivecomputer/Makefile.am
+++ b/include/libdivecomputer/Makefile.am
@@ -53,4 +53,5 @@ libdivecomputer_HEADERS = 	\
 	citizen.h \
 	citizen_aqualand.h \
 	divesystem.h \
-	divesystem_idive.h
+	divesystem_idive.h \
+	custom_serial.h
diff --git a/include/libdivecomputer/custom_serial.h b/include/libdivecomputer/custom_serial.h
new file mode 100644
index 0000000..a52d49b
--- /dev/null
+++ b/include/libdivecomputer/custom_serial.h
@@ -0,0 +1,63 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2015 Claudiu Olteanu
+ * base on code that is Copyright (C) 2008 Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef CUSTOM_SERIAL_H
+#define CUSTOM_SERIAL_H
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "context.h"
+#include "descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct serial_t serial_t;
+
+typedef struct dc_serial_operations_t
+{
+	int (*open) (serial_t **device, dc_context_t *context, const char *name);
+	int (*close) (serial_t *device);
+	int (*read) (serial_t *device, void* data, unsigned int size);
+	int (*write) (serial_t *device, const void* data, unsigned int size);
+	int (*flush) (serial_t *device, int queue);
+	int (*get_received) (serial_t *device);
+	int (*get_transmitted) (serial_t *device);
+} dc_serial_operations_t;
+
+typedef struct dc_serial_t {
+	serial_t *port;				//serial device port
+	dc_transport_t type;			//the type of the transport (USB, SERIAL, IRDA, BLUETOOTH)
+	void *data;				//specific data for serial device
+	const dc_serial_operations_t *ops;	//reference to a custom set of operations
+} dc_serial_t;
+
+void dc_serial_init(dc_serial_t *device, void *data, const dc_serial_operations_t *ops);
+
+dc_status_t dc_serial_native_open(dc_serial_t **serial, dc_context_t *context, const char *devname);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CUSTOM_SERIAL_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index abbaa48..0c60d02 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -59,7 +59,8 @@ libdivecomputer_la_SOURCES = \
 	ringbuffer.h ringbuffer.c \
 	checksum.h checksum.c \
 	array.h array.c \
-	buffer.c
+	buffer.c \
+	custom_serial.c
 
 if OS_WIN32
 libdivecomputer_la_SOURCES += serial.h serial_win32.c
diff --git a/src/custom_serial.c b/src/custom_serial.c
new file mode 100644
index 0000000..6e024b2
--- /dev/null
+++ b/src/custom_serial.c
@@ -0,0 +1,79 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2015 Claudiu Olteanu
+ * base on code that is Copyright (C) 2008 Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <libdivecomputer/custom_serial.h>
+#include <serial.h>
+
+#include "context-private.h"
+
+const dc_serial_operations_t native_serial_ops = {
+	.open = serial_open,
+	.close = serial_close,
+	.read = serial_read,
+	.write = serial_write,
+	.flush = serial_flush,
+	.get_received = serial_get_received,
+	.get_transmitted = serial_get_transmitted
+};
+
+
+void
+dc_serial_init(dc_serial_t *device, void *data, const dc_serial_operations_t *ops)
+{
+	memset(device, 0, sizeof (*device));
+	device->data = data;
+	device->ops = ops;
+}
+
+
+dc_status_t
+dc_serial_native_open(dc_serial_t **out, dc_context_t *context, const char *devname)
+{
+	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) {
+		ERROR (context, "Failed to allocate memory.");
+		return DC_STATUS_NOMEMORY;
+	}
+
+	// Initialize data and function pointers
+	dc_serial_init(serial_device, NULL, &native_serial_ops);
+
+	// Open the serial device.
+	int rc = serial_open (&serial_device->port, context, devname);
+	if (rc == -1) {
+		ERROR (context, "Failed to open the serial port.");
+		free (serial_device);
+		return DC_STATUS_IO;
+	}
+
+	// Set the type of the device
+	serial_device->type = DC_TRANSPORT_SERIAL;
+
+	*out = serial_device;
+
+	return DC_STATUS_SUCCESS;
+}
-- 
2.1.4

From 803984baaf0f91020b4344bcdd5f801d1dad3e0f Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sat, 27 Jun 2015 15:18:01 +0300
Subject: [PATCH 3/7] Use the dc_serial_t structure in HW OSTC family 3

Open a native serial device and use it in the HW OSTC3
implementation.

This patch replaces the old serial structure with the
new one, which can be used for custom serial implementations.

Signed-off-by: Claudiu Olteanu <olteanu.clau...@ymail.com>
---
 include/libdivecomputer/hw_ostc3.h |  2 ++
 src/hw_ostc3.c                     | 41 +++++++++++++++++++-------------------
 2 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/include/libdivecomputer/hw_ostc3.h b/include/libdivecomputer/hw_ostc3.h
index c60dc63..454d45c 100644
--- a/include/libdivecomputer/hw_ostc3.h
+++ b/include/libdivecomputer/hw_ostc3.h
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2013 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -26,6 +27,7 @@
 #include "device.h"
 #include "parser.h"
 #include "buffer.h"
+#include "custom_serial.h"
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c
index a221fb2..a55c5cc 100644
--- a/src/hw_ostc3.c
+++ b/src/hw_ostc3.c
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2013 Jef Driesen
  * Copyright (C) 2014 Anton Lundin
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -88,7 +89,7 @@ typedef enum hw_ostc3_state_t {
 
 typedef struct hw_ostc3_device_t {
 	dc_device_t base;
-	serial_t *port;
+	dc_serial_t *serial;
 	unsigned char fingerprint[5];
 	hw_ostc3_state_t state;
 } hw_ostc3_device_t;
@@ -163,7 +164,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 
 	// Send the command.
 	unsigned char command[1] = {cmd};
-	int n = serial_write (device->port, command, sizeof (command));
+	int n = device->serial->ops->write (device->serial->port, command, sizeof (command));
 	if (n != sizeof (command)) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return EXITCODE (n);
@@ -171,7 +172,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 
 	// Read the echo.
 	unsigned char echo[1] = {0};
-	n = serial_read (device->port, echo, sizeof (echo));
+	n = device->serial->ops->read (device->serial->port, echo, sizeof (echo));
 	if (n != sizeof (echo)) {
 		ERROR (abstract->context, "Failed to receive the echo.");
 		return EXITCODE (n);
@@ -190,7 +191,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 
 	if (input) {
 		// Send the input data packet.
-		n = serial_write (device->port, input, isize);
+		n = device->serial->ops->write (device->serial->port, input, isize);
 		if (n != isize) {
 			ERROR (abstract->context, "Failed to send the data packet.");
 			return EXITCODE (n);
@@ -204,7 +205,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 			unsigned int len = 1024;
 
 			// Increase the packet size if more data is immediately available.
-			int available = serial_get_received (device->port);
+			int available = device->serial->ops->get_received (device->serial->port);
 			if (available > len)
 				len = available;
 
@@ -213,7 +214,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 				len = osize - nbytes;
 
 			// Read the packet.
-			n = serial_read (device->port, output + nbytes, len);
+			n = device->serial->ops->read (device->serial->port, output + nbytes, len);
 			if (n != len) {
 				ERROR (abstract->context, "Failed to receive the answer.");
 				return EXITCODE (n);
@@ -232,7 +233,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 	if (cmd != EXIT) {
 		// Read the ready byte.
 		unsigned char answer[1] = {0};
-		n = serial_read (device->port, answer, sizeof (answer));
+		n = device->serial->ops->read (device->serial->port, answer, sizeof (answer));
 		if (n != sizeof (answer)) {
 			ERROR (abstract->context, "Failed to receive the ready byte.");
 			return EXITCODE (n);
@@ -266,11 +267,11 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name
 	device_init (&device->base, context, &hw_ostc3_device_vtable);
 
 	// Set the default values.
-	device->port = NULL;
+	device->serial = NULL;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
 	// Open the device.
-	int rc = serial_open (&device->port, context, name);
+	int rc = dc_serial_native_open (&device->serial, context, name);
 	if (rc == -1) {
 		ERROR (context, "Failed to open the serial port.");
 		free (device);
@@ -278,25 +279,25 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name
 	}
 
 	// Set the serial communication protocol (115200 8N1).
-	rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+	rc = serial_configure (device->serial->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
 	if (rc == -1) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		serial_close (device->port);
+		device->serial->ops->close (device->serial->port);
 		free (device);
 		return DC_STATUS_IO;
 	}
 
 	// Set the timeout for receiving data (3000ms).
-	if (serial_set_timeout (device->port, 3000) == -1) {
+	if (serial_set_timeout (device->serial->port, 3000) == -1) {
 		ERROR (context, "Failed to set the timeout.");
-		serial_close (device->port);
+		device->serial->ops->close (device->serial->port);
 		free (device);
 		return DC_STATUS_IO;
 	}
 
 	// Make sure everything is in a sane state.
-	serial_sleep (device->port, 300);
-	serial_flush (device->port, SERIAL_QUEUE_BOTH);
+	serial_sleep (device->serial->port, 300);
+	device->serial->ops->flush (device->serial->port, SERIAL_QUEUE_BOTH);
 
 	device->state = OPEN;
 
@@ -336,17 +337,17 @@ hw_ostc3_device_init_service (hw_ostc3_device_t *device)
 	int n = 0;
 
 	// We cant use hw_ostc3_transfer here, due to the different echos
-	n = serial_write (device->port, command, sizeof (command));
+	n = device->serial->ops->write (device->serial->port, command, sizeof (command));
 	if (n != sizeof (command)) {
 		ERROR (context, "Failed to send the command.");
 		return EXITCODE (n);
 	}
 
 	// Give the device some time to enter service mode
-	serial_sleep (device->port, 100);
+	serial_sleep (device->serial->port, 100);
 
 	// Read the response
-	n = serial_read (device->port, output, sizeof (output));
+	n = device->serial->ops->read (device->serial->port, output, sizeof (output));
 	if (n != sizeof (output)) {
 		ERROR (context, "Failed to receive the echo.");
 		return EXITCODE (n);
@@ -408,14 +409,14 @@ hw_ostc3_device_close (dc_device_t *abstract)
 		rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0);
 		if (rc != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to send the command.");
-			serial_close (device->port);
+			device->serial->ops->close (device->serial->port);
 			free (device);
 			return rc;
 		}
 	}
 
 	// Close the device.
-	if (serial_close (device->port) == -1) {
+	if (device->serial->ops->close (device->serial->port) == -1) {
 		free (device);
 		return DC_STATUS_IO;
 	}
-- 
2.1.4

From c9dfb26aca258672ceac788ceecb809f183db93c Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sat, 27 Jun 2015 15:21:19 +0300
Subject: [PATCH 4/7] Implement custom open method for HW OSTC 3 family

Create a custom open method for HW OSTC3 family.
This method can be used to pass a reference to a dc_serial_t
structure. In this way the applications can implement their
own implementation for a serial communication and set their
callbacks for the basic serial functions.

Signed-off-by: Claudiu Olteanu <olteanu.clau...@ymail.com>
---
 include/libdivecomputer/hw_ostc3.h |  3 ++
 src/hw_ostc3.c                     | 57 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/include/libdivecomputer/hw_ostc3.h b/include/libdivecomputer/hw_ostc3.h
index 454d45c..8463cb2 100644
--- a/include/libdivecomputer/hw_ostc3.h
+++ b/include/libdivecomputer/hw_ostc3.h
@@ -40,6 +40,9 @@ dc_status_t
 hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, const char *name);
 
 dc_status_t
+hw_ostc3_device_custom_open (dc_device_t **device, dc_context_t *context, dc_serial_t *serial);
+
+dc_status_t
 hw_ostc3_device_version (dc_device_t *device, unsigned char data[], unsigned int size);
 
 dc_status_t
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c
index a55c5cc..2d975b7 100644
--- a/src/hw_ostc3.c
+++ b/src/hw_ostc3.c
@@ -272,10 +272,10 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name
 
 	// Open the device.
 	int rc = dc_serial_native_open (&device->serial, context, name);
-	if (rc == -1) {
+	if (rc != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to open the serial port.");
 		free (device);
-		return DC_STATUS_IO;
+		return rc;
 	}
 
 	// Set the serial communication protocol (115200 8N1).
@@ -307,6 +307,59 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name
 }
 
 
+dc_status_t
+hw_ostc3_device_custom_open (dc_device_t **out, dc_context_t *context, dc_serial_t *serial)
+{
+	if (out == NULL || serial == NULL || serial->port == NULL)
+		return DC_STATUS_INVALIDARGS;
+
+	// Allocate memory.
+	hw_ostc3_device_t *device = (hw_ostc3_device_t *) malloc (sizeof (hw_ostc3_device_t));
+	if (device == NULL) {
+		ERROR (context, "Failed to allocate memory.");
+		return DC_STATUS_NOMEMORY;
+	}
+
+	// Initialize the base class.
+	device_init (&device->base, context, &hw_ostc3_device_vtable);
+
+	// Set the default values.
+	memset (device->fingerprint, 0, sizeof (device->fingerprint));
+
+	// Set the serial reference
+	device->serial = serial;
+
+	if (serial->type == DC_TRANSPORT_SERIAL) {
+		// Set the serial communication protocol (115200 8N1).
+		int rc = serial_configure (device->serial->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		if (rc == -1) {
+			ERROR (context, "Failed to set the terminal attributes.");
+			device->serial->ops->close (device->serial->port);
+			free (device);
+			return DC_STATUS_IO;
+		}
+	}
+
+	// Set the timeout for receiving data (3000ms).
+	if (serial_set_timeout (device->serial->port, 3000) == -1) {
+		ERROR (context, "Failed to set the timeout.");
+		device->serial->ops->close (device->serial->port);
+		free (device);
+		return DC_STATUS_IO;
+	}
+
+	// Make sure everything is in a sane state.
+	serial_sleep (device->serial->port, 300);
+	device->serial->ops->flush (device->serial->port, SERIAL_QUEUE_BOTH);
+
+	device->state = OPEN;
+
+	*out = (dc_device_t *) device;
+
+	return DC_STATUS_SUCCESS;
+}
+
+
 static dc_status_t
 hw_ostc3_device_init_download (hw_ostc3_device_t *device)
 {
-- 
2.1.4

From e8a132a921f8aac846c9e21cd7032e174f441ee8 Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sat, 27 Jun 2015 15:22:04 +0300
Subject: [PATCH 5/7] Implement custom device open method

This method can be used by external applications to open a
device and to pass their custom implementation for the
serial communication.

Signed-off-by: Claudiu Olteanu <olteanu.clau...@ymail.com>
---
 include/libdivecomputer/device.h |  5 +++++
 src/device.c                     | 22 ++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/include/libdivecomputer/device.h b/include/libdivecomputer/device.h
index 7ba4bd6..60590b5 100644
--- a/include/libdivecomputer/device.h
+++ b/include/libdivecomputer/device.h
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2008 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -27,6 +28,7 @@
 #include "descriptor.h"
 #include "buffer.h"
 #include "datetime.h"
+#include "custom_serial.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -72,6 +74,9 @@ typedef int (*dc_dive_callback_t) (const unsigned char *data, unsigned int size,
 dc_status_t
 dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, const char *name);
 
+dc_status_t
+dc_device_custom_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, dc_serial_t *serial);
+
 dc_family_t
 dc_device_get_type (dc_device_t *device);
 
diff --git a/src/device.c b/src/device.c
index d95585d..257dc75 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2008 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -174,6 +175,27 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
 	return rc;
 }
 
+dc_status_t
+dc_device_custom_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, dc_serial_t *serial)
+{
+	dc_status_t rc = DC_STATUS_SUCCESS;
+	dc_device_t *device = NULL;
+
+	if (out == NULL || descriptor == NULL || serial == NULL)
+		return DC_STATUS_INVALIDARGS;
+
+	switch (dc_descriptor_get_type (descriptor)) {
+	case DC_FAMILY_HW_OSTC3:
+		rc = hw_ostc3_device_custom_open (&device, context, serial);
+		break;
+	default:
+		return DC_STATUS_INVALIDARGS;
+	}
+
+	*out = device;
+
+	return rc;
+}
 
 int
 dc_device_isinstance (dc_device_t *device, const dc_device_vtable_t *vtable)
-- 
2.1.4

From e22c14994f1a58d2d258463163ceceb961b94b4b Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sun, 28 Jun 2015 23:46:56 +0300
Subject: [PATCH 6/7] Use the dc_serial_t structure in SHEARWATER family

Use the new structure in the SHEARWATER family implementation.
This patch opens a native serial device and use it
for the serial communication.

Also the patch uses the set of callback functions saved in the
dc_serial_t structure.

Signed-off-by: Claudiu Olteanu <olteanu.clau...@ymail.com>
---
 src/shearwater_common.c | 29 +++++++++++++++--------------
 src/shearwater_common.h |  4 +++-
 2 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/src/shearwater_common.c b/src/shearwater_common.c
index b809a3c..a4e94de 100644
--- a/src/shearwater_common.c
+++ b/src/shearwater_common.c
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2013 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -41,30 +42,30 @@ dc_status_t
 shearwater_common_open (shearwater_common_device_t *device, dc_context_t *context, const char *name)
 {
 	// Open the device.
-	int rc = serial_open (&device->port, context, name);
+	int rc = dc_serial_native_open (&device->serial, context, name);
 	if (rc == -1) {
 		ERROR (context, "Failed to open the serial port.");
-		return DC_STATUS_IO;
+		return rc;
 	}
 
 	// Set the serial communication protocol (115200 8N1).
-	rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+	rc = serial_configure (device->serial->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
 	if (rc == -1) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		serial_close (device->port);
+		device->serial->ops->close (device->serial->port);
 		return DC_STATUS_IO;
 	}
 
 	// Set the timeout for receiving data (3000ms).
-	if (serial_set_timeout (device->port, 3000) == -1) {
+	if (serial_set_timeout (device->serial->port, 3000) == -1) {
 		ERROR (context, "Failed to set the timeout.");
-		serial_close (device->port);
+		device->serial->ops->close (device->serial->port);
 		return DC_STATUS_IO;
 	}
 
 	// Make sure everything is in a sane state.
-	serial_sleep (device->port, 300);
-	serial_flush (device->port, SERIAL_QUEUE_BOTH);
+	serial_sleep (device->serial->port, 300);
+	device->serial->ops->flush (device->serial->port, SERIAL_QUEUE_BOTH);
 
 	return DC_STATUS_SUCCESS;
 }
@@ -74,7 +75,7 @@ dc_status_t
 shearwater_common_close (shearwater_common_device_t *device)
 {
 	// Close the device.
-	if (serial_close (device->port) == -1) {
+	if (device->serial->ops->close (device->serial->port) == -1) {
 		return DC_STATUS_IO;
 	}
 
@@ -154,7 +155,7 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned
 #if 0
 	// Send an initial END character to flush out any data that may have
 	// accumulated in the receiver due to line noise.
-	n = serial_write (device->port, end, sizeof (end));
+	n = device->serial->ops->write (device->serial->port, end, sizeof (end));
 	if (n != sizeof (end)) {
 		return EXITCODE(n);
 	}
@@ -183,7 +184,7 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned
 
 		// Flush the buffer if necessary.
 		if (nbytes + len + sizeof(end) > sizeof(buffer)) {
-			n = serial_write (device->port, buffer, nbytes);
+			n = device->serial->ops->write (device->serial->port, buffer, nbytes);
 			if (n != nbytes) {
 				return EXITCODE(n);
 			}
@@ -201,7 +202,7 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned
 	nbytes += sizeof(end);
 
 	// Flush the buffer.
-	n = serial_write (device->port, buffer, nbytes);
+	n = device->serial->ops->write (device->serial->port, buffer, nbytes);
 	if (n != nbytes) {
 		return EXITCODE(n);
 	}
@@ -224,7 +225,7 @@ shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char d
 		int n = 0;
 
 		// Get a single character to process.
-		n = serial_read (device->port, &c, 1);
+		n = device->serial->ops->read (device->serial->port, &c, 1);
 		if (n != 1) {
 			return EXITCODE(n);
 		}
@@ -243,7 +244,7 @@ shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char d
 		case ESC:
 			// If it's an ESC character, get another character and then
 			// figure out what to store in the packet based on that.
-			n = serial_read (device->port, &c, 1);
+			n = device->serial->ops->read (device->serial->port, &c, 1);
 			if (n != 1) {
 				return EXITCODE(n);
 			}
diff --git a/src/shearwater_common.h b/src/shearwater_common.h
index a07de14..b930fb9 100644
--- a/src/shearwater_common.h
+++ b/src/shearwater_common.h
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2013 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -24,6 +25,7 @@
 
 #include "device-private.h"
 #include "serial.h"
+#include "libdivecomputer/custom_serial.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -34,7 +36,7 @@ extern "C" {
 
 typedef struct shearwater_common_device_t {
 	dc_device_t base;
-	serial_t *port;
+	dc_serial_t *serial;
 } shearwater_common_device_t;
 
 dc_status_t
-- 
2.1.4

From b50db86e3f0da31aedfe3d2afbddcd94ba2c8a39 Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Mon, 29 Jun 2015 00:08:20 +0300
Subject: [PATCH 7/7] Implement custom open device method for SHEARWATER family

Create a custom open method for SHEARWATER family.
This method can be used to pass a reference to a dc_serial_t
structure. In this way the applications can implement their
own implementation for a serial communication and set their
callbacks for the basic serial functions.

Signed-off-by: Claudiu Olteanu <olteanu.clau...@ymail.com>
---
 include/libdivecomputer/shearwater_petrel.h   |  4 +++
 include/libdivecomputer/shearwater_predator.h |  4 +++
 src/device.c                                  |  6 +++++
 src/shearwater_common.c                       | 33 ++++++++++++++++++++++++-
 src/shearwater_common.h                       |  3 +++
 src/shearwater_petrel.c                       | 35 +++++++++++++++++++++++++++
 src/shearwater_predator.c                     | 35 +++++++++++++++++++++++++++
 7 files changed, 119 insertions(+), 1 deletion(-)

diff --git a/include/libdivecomputer/shearwater_petrel.h b/include/libdivecomputer/shearwater_petrel.h
index 18a4bce..97a8dc2 100644
--- a/include/libdivecomputer/shearwater_petrel.h
+++ b/include/libdivecomputer/shearwater_petrel.h
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2013 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -34,6 +35,9 @@ dc_status_t
 shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, const char *name);
 
 dc_status_t
+shearwater_petrel_device_custom_open (dc_device_t **out, dc_context_t *context, dc_serial_t *serial);
+
+dc_status_t
 shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int serial);
 
 #ifdef __cplusplus
diff --git a/include/libdivecomputer/shearwater_predator.h b/include/libdivecomputer/shearwater_predator.h
index 28163e2..9a37bcf 100644
--- a/include/libdivecomputer/shearwater_predator.h
+++ b/include/libdivecomputer/shearwater_predator.h
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2012 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -34,6 +35,9 @@ dc_status_t
 shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, const char *name);
 
 dc_status_t
+shearwater_predator_device_custom_open (dc_device_t **device, dc_context_t *context, dc_serial_t *serial);
+
+dc_status_t
 shearwater_predator_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
 
 dc_status_t
diff --git a/src/device.c b/src/device.c
index 257dc75..fd6fe9b 100644
--- a/src/device.c
+++ b/src/device.c
@@ -188,6 +188,12 @@ dc_device_custom_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t
 	case DC_FAMILY_HW_OSTC3:
 		rc = hw_ostc3_device_custom_open (&device, context, serial);
 		break;
+	case DC_FAMILY_SHEARWATER_PREDATOR:
+		rc = shearwater_predator_device_custom_open (&device, context, serial);
+		break;
+	case DC_FAMILY_SHEARWATER_PETREL:
+		rc = shearwater_petrel_device_custom_open (&device, context, serial);
+		break;
 	default:
 		return DC_STATUS_INVALIDARGS;
 	}
diff --git a/src/shearwater_common.c b/src/shearwater_common.c
index a4e94de..b12bf9b 100644
--- a/src/shearwater_common.c
+++ b/src/shearwater_common.c
@@ -45,7 +45,7 @@ shearwater_common_open (shearwater_common_device_t *device, dc_context_t *contex
 	int rc = dc_serial_native_open (&device->serial, context, name);
 	if (rc == -1) {
 		ERROR (context, "Failed to open the serial port.");
-		return rc;
+		return DC_STATUS_IO;
 	}
 
 	// Set the serial communication protocol (115200 8N1).
@@ -72,6 +72,37 @@ shearwater_common_open (shearwater_common_device_t *device, dc_context_t *contex
 
 
 dc_status_t
+shearwater_common_custom_open (shearwater_common_device_t *device, dc_context_t *context, dc_serial_t *serial)
+{
+	// Set the serial reference
+	device->serial = serial;
+
+	if (serial->type == DC_TRANSPORT_SERIAL) {
+		// Set the serial communication protocol (115200 8N1).
+		int rc = serial_configure (device->serial->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		if (rc == -1) {
+			ERROR (context, "Failed to set the terminal attributes.");
+			device->serial->ops->close (device->serial->port);
+			return DC_STATUS_IO;
+		}
+	}
+
+	// Set the timeout for receiving data (3000ms).
+	if (serial_set_timeout (device->serial->port, 3000) == -1) {
+		ERROR (context, "Failed to set the timeout.");
+		device->serial->ops->close (device->serial->port);
+		return DC_STATUS_IO;
+	}
+
+	// Make sure everything is in a sane state.
+	serial_sleep (device->serial->port, 300);
+	device->serial->ops->flush (device->serial->port, SERIAL_QUEUE_BOTH);
+
+	return DC_STATUS_SUCCESS;
+}
+
+
+dc_status_t
 shearwater_common_close (shearwater_common_device_t *device)
 {
 	// Close the device.
diff --git a/src/shearwater_common.h b/src/shearwater_common.h
index b930fb9..8e4112f 100644
--- a/src/shearwater_common.h
+++ b/src/shearwater_common.h
@@ -43,6 +43,9 @@ dc_status_t
 shearwater_common_open (shearwater_common_device_t *device, dc_context_t *context, const char *name);
 
 dc_status_t
+shearwater_common_custom_open (shearwater_common_device_t *device, dc_context_t *context, dc_serial_t *serial);
+
+dc_status_t
 shearwater_common_close (shearwater_common_device_t *device);
 
 dc_status_t
diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c
index 3534a26..e33dc8f 100644
--- a/src/shearwater_petrel.c
+++ b/src/shearwater_petrel.c
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2013 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -110,6 +111,40 @@ shearwater_petrel_device_open (dc_device_t **out, dc_context_t *context, const c
 }
 
 
+dc_status_t
+shearwater_petrel_device_custom_open (dc_device_t **out, dc_context_t *context, dc_serial_t *serial)
+{
+	dc_status_t rc = DC_STATUS_SUCCESS;
+
+	if (out == NULL || serial == NULL || serial->port == NULL)
+		return DC_STATUS_INVALIDARGS;
+
+	// Allocate memory.
+	shearwater_petrel_device_t *device = (shearwater_petrel_device_t *) malloc (sizeof (shearwater_petrel_device_t));
+	if (device == NULL) {
+		ERROR (context, "Failed to allocate memory.");
+		return DC_STATUS_NOMEMORY;
+	}
+
+	// Initialize the base class.
+	device_init (&device->base.base, context, &shearwater_petrel_device_vtable);
+
+	// Set the default values.
+	memset (device->fingerprint, 0, sizeof (device->fingerprint));
+
+	// Open the device.
+	rc = shearwater_common_custom_open (&device->base, context, serial);
+	if (rc != DC_STATUS_SUCCESS) {
+		free (device);
+		return rc;
+	}
+
+	*out = (dc_device_t *) device;
+
+	return DC_STATUS_SUCCESS;
+}
+
+
 static dc_status_t
 shearwater_petrel_device_close (dc_device_t *abstract)
 {
diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c
index e8a44ac..4901787 100644
--- a/src/shearwater_predator.c
+++ b/src/shearwater_predator.c
@@ -2,6 +2,7 @@
  * libdivecomputer
  *
  * Copyright (C) 2012 Jef Driesen
+ * Copyright (C) 2015 Claudiu Olteanu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -96,6 +97,40 @@ shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, const
 }
 
 
+dc_status_t
+shearwater_predator_device_custom_open (dc_device_t **out, dc_context_t *context, dc_serial_t *serial)
+{
+	dc_status_t rc = DC_STATUS_SUCCESS;
+
+	if (out == NULL || serial == NULL || serial->port == NULL)
+		return DC_STATUS_INVALIDARGS;
+
+	// Allocate memory.
+	shearwater_predator_device_t *device = (shearwater_predator_device_t *) malloc (sizeof (shearwater_predator_device_t));
+	if (device == NULL) {
+		ERROR (context, "Failed to allocate memory.");
+		return DC_STATUS_NOMEMORY;
+	}
+
+	// Initialize the base class.
+	device_init (&device->base.base, context, &shearwater_predator_device_vtable);
+
+	// Set the default values.
+	memset (device->fingerprint, 0, sizeof (device->fingerprint));
+
+	// Open the device.
+	rc = shearwater_common_custom_open (&device->base, context, serial);
+	if (rc != DC_STATUS_SUCCESS) {
+		free (device);
+		return rc;
+	}
+
+	*out = (dc_device_t *) device;
+
+	return DC_STATUS_SUCCESS;
+}
+
+
 static dc_status_t
 shearwater_predator_device_close (dc_device_t *abstract)
 {
-- 
2.1.4

From 27be3bb65972ef07fe781b4c9a3c8cf88558d68d Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sun, 28 Jun 2015 19:05:35 +0300
Subject: [PATCH 1/4] Add checkbox and button for Bluetooth download mode

The checkbox will be used to enable the Bluetooth
downloading mode. The button will be used to create
a dialog selection where the user will be able to
scan and select remote devices.

Signed-off-by: Claudiu Olteanu <olteanu.clau...@ymail.com>
---
 qt-ui/downloadfromdivecomputer.cpp | 20 ++++++++++++++++++++
 qt-ui/downloadfromdivecomputer.h   |  2 ++
 qt-ui/downloadfromdivecomputer.ui  | 17 +++++++++++++++++
 3 files changed, 39 insertions(+)

diff --git a/qt-ui/downloadfromdivecomputer.cpp b/qt-ui/downloadfromdivecomputer.cpp
index c5d57e6..838a9e5 100644
--- a/qt-ui/downloadfromdivecomputer.cpp
+++ b/qt-ui/downloadfromdivecomputer.cpp
@@ -99,6 +99,9 @@ DownloadFromDCWidget::DownloadFromDCWidget(QWidget *parent, Qt::WindowFlags f) :
 	ui.ok->setEnabled(false);
 	ui.downloadCancelRetryButton->setEnabled(true);
 	ui.downloadCancelRetryButton->setText(tr("Download"));
+	ui.chooseBluetoothDevice->setEnabled(ui.bluetoothMode->isChecked());
+	connect(ui.bluetoothMode, SIGNAL(stateChanged(int)), this, SLOT(enableBluetoothMode(int)));
+	connect(ui.chooseBluetoothDevice, SIGNAL(clicked()), this, SLOT(selectRemoteBluetoothDevice()));
 }
 
 void DownloadFromDCWidget::updateProgressBar()
@@ -493,6 +496,8 @@ void DownloadFromDCWidget::markChildrenAsDisabled()
 	ui.chooseDumpFile->setEnabled(false);
 	ui.selectAllButton->setEnabled(false);
 	ui.unselectAllButton->setEnabled(false);
+	ui.bluetoothMode->setEnabled(false);
+	ui.chooseBluetoothDevice->setEnabled(false);
 }
 
 void DownloadFromDCWidget::markChildrenAsEnabled()
@@ -512,6 +517,21 @@ void DownloadFromDCWidget::markChildrenAsEnabled()
 	ui.chooseDumpFile->setEnabled(true);
 	ui.selectAllButton->setEnabled(true);
 	ui.unselectAllButton->setEnabled(true);
+	ui.bluetoothMode->setEnabled(true);
+	ui.chooseBluetoothDevice->setEnabled(true);
+}
+
+void DownloadFromDCWidget::selectRemoteBluetoothDevice()
+{
+	qWarning() << "Selecting a remote Bluetooth device...";
+	//TODO add implementation
+}
+
+void DownloadFromDCWidget::enableBluetoothMode(int state)
+{
+	ui.chooseBluetoothDevice->setEnabled(state == Qt::Checked);
+	if (state == Qt::Checked)
+		selectRemoteBluetoothDevice();
 }
 
 static void fillDeviceList(const char *name, void *data)
diff --git a/qt-ui/downloadfromdivecomputer.h b/qt-ui/downloadfromdivecomputer.h
index 92db09d..0b63d28 100644
--- a/qt-ui/downloadfromdivecomputer.h
+++ b/qt-ui/downloadfromdivecomputer.h
@@ -77,8 +77,10 @@ slots:
 	void updateProgressBar();
 	void checkLogFile(int state);
 	void checkDumpFile(int state);
+	void enableBluetoothMode(int state);
 	void pickDumpFile();
 	void pickLogFile();
+	void selectRemoteBluetoothDevice();
 
 private:
 	void markChildrenAsDisabled();
diff --git a/qt-ui/downloadfromdivecomputer.ui b/qt-ui/downloadfromdivecomputer.ui
index ff80935..f232967 100644
--- a/qt-ui/downloadfromdivecomputer.ui
+++ b/qt-ui/downloadfromdivecomputer.ui
@@ -116,6 +116,23 @@
            </property>
           </widget>
          </item>
+         <item row="11" column="0">
+          <widget class="QCheckBox" name="bluetoothMode">
+           <property name="text">
+            <string>Choose Bluetooth Download mode</string>
+           </property>
+          </widget>
+         </item>
+         <item row="11" column="1">
+          <widget class="QToolButton" name="chooseBluetoothDevice">
+           <property name="toolTip">
+            <string>Select a remote Bluetooth device.</string>
+           </property>
+           <property name="text">
+            <string>...</string>
+           </property>
+          </widget>
+         </item>
          <item row="0" column="0" colspan="2">
           <widget class="QLabel" name="label">
            <property name="text">
-- 
2.1.4

From e8bd012da549b60ae489e4d8af531e9d92e02922 Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sun, 28 Jun 2015 20:48:00 +0300
Subject: [PATCH 2/4] Add a new dialog which can be use to select the Bluetooth
 devices

Implement a dialog which can be used for remote Bluetooth devices
selection and to control the local Bluetooth device.
Functionalities of the widget:
- expose information about the local BT device
- scan for remote BT devices
- pair/unpair with a remote BT device
- turn on/off the local BT device
- logging
- save the selected BT device

The selection dialog is created when the bluetoothMode checkbox
is enabled.
---
 CMakeLists.txt                     |   5 +-
 libdivecomputer.h                  |   1 +
 qt-ui/btdeviceselectiondialog.cpp  | 262 +++++++++++++++++++++++++++++++++++++
 qt-ui/btdeviceselectiondialog.h    |  46 +++++++
 qt-ui/btdeviceselectiondialog.ui   | 211 +++++++++++++++++++++++++++++
 qt-ui/downloadfromdivecomputer.cpp |  28 +++-
 qt-ui/downloadfromdivecomputer.h   |   3 +
 7 files changed, 551 insertions(+), 5 deletions(-)
 create mode 100644 qt-ui/btdeviceselectiondialog.cpp
 create mode 100644 qt-ui/btdeviceselectiondialog.h
 create mode 100644 qt-ui/btdeviceselectiondialog.ui

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 943de13..92d9f81 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -139,8 +139,8 @@ endif()
 if(SUBSURFACE_MOBILE)
 	set(QT_QUICK Quick)
 endif()
-find_package(Qt5 REQUIRED COMPONENTS Core Concurrent Widgets Network WebKitWidgets PrintSupport Svg Test LinguistTools ${QT_QUICK})
-set(QT_LIBRARIES Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network Qt5::WebKitWidgets Qt5::PrintSupport Qt5::Svg)
+find_package(Qt5 REQUIRED COMPONENTS Core Concurrent Widgets Network WebKitWidgets PrintSupport Svg Test LinguistTools ${QT_QUICK} Bluetooth)
+set(QT_LIBRARIES Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network Qt5::WebKitWidgets Qt5::PrintSupport Qt5::Svg Qt5::Bluetooth)
 if(SUBSURFACE_MOBILE)
 	set(QT_LIBRARIES ${QT_LIBRARIES} Qt5::Quick)
 endif()
@@ -326,6 +326,7 @@ source_group("Subsurface Models" FILES ${SUBSURFACE_MODELS})
 set(SUBSURFACE_INTERFACE
 	qt-ui/updatemanager.cpp
 	qt-ui/about.cpp
+	qt-ui/btdeviceselectiondialog.cpp
 	qt-ui/divecomputermanagementdialog.cpp
 	qt-ui/divelistview.cpp
 	qt-ui/diveplanner.cpp
diff --git a/libdivecomputer.h b/libdivecomputer.h
index dfb6267..f5c0cad 100644
--- a/libdivecomputer.h
+++ b/libdivecomputer.h
@@ -37,6 +37,7 @@ typedef struct device_data_t
 	bool create_new_trip;
 	bool libdc_log;
 	bool libdc_dump;
+	bool bluetooth_mode;
 	FILE *libdc_logfile;
 	struct dive_table *download_table;
 } device_data_t;
diff --git a/qt-ui/btdeviceselectiondialog.cpp b/qt-ui/btdeviceselectiondialog.cpp
new file mode 100644
index 0000000..b64ff30
--- /dev/null
+++ b/qt-ui/btdeviceselectiondialog.cpp
@@ -0,0 +1,262 @@
+#include <QShortcut>
+#include <QDebug>
+#include <QMessageBox>
+#include <QMenu>
+
+#include "ui_btdeviceselectiondialog.h"
+#include "btdeviceselectiondialog.h"
+
+BtDeviceSelectionDialog::BtDeviceSelectionDialog(QWidget *parent) :
+	QDialog(parent),
+	localDevice(new QBluetoothLocalDevice),
+	ui(new Ui::BtDeviceSelectionDialog)
+{
+	/* Check if Bluetooth is available on this device */
+	if (!localDevice->isValid()) {
+		QMessageBox::warning(this, tr("Warning"),
+				     "This should never happen, please contact the Subsurface developers "
+				     "and tell them that the Bluetooth download mode doesn't work.");
+		return;
+	}
+
+	ui->setupUi(this);
+
+	/* Quit button callbacks*/
+	QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this);
+	connect(quit, SIGNAL(activated()), this, SLOT(reject()));
+	connect(ui->quit, SIGNAL(clicked()), this, SLOT(reject()));
+
+	/* Disable the save button because there is no device selected */
+	ui->save->setEnabled(false);
+
+	connect(ui->discoveredDevicesList, SIGNAL(itemActivated(QListWidgetItem*)),
+		this, SLOT(itemActivated(QListWidgetItem*)));
+
+	/* Set UI information about the local device */
+	ui->deviceAddress->setText(localDevice->address().toString());
+	ui->deviceName->setText(localDevice->name());
+
+	connect(localDevice, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode)),
+		this, SLOT(hostModeStateChanged(QBluetoothLocalDevice::HostMode)));
+
+	/* Initialize the state of the local device and activate/deactive the scan button */
+	hostModeStateChanged(localDevice->hostMode());
+
+	/* Intialize the discovery agent */
+	remoteDeviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent();
+
+	connect(remoteDeviceDiscoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
+		this, SLOT(addRemoteDevice(QBluetoothDeviceInfo)));
+	connect(remoteDeviceDiscoveryAgent, SIGNAL(finished()),
+		this, SLOT(remoteDeviceScanFinished()));
+
+	/* Add context menu for devices to be able to pair them */
+	ui->discoveredDevicesList->setContextMenuPolicy(Qt::CustomContextMenu);
+	connect(ui->discoveredDevicesList, SIGNAL(customContextMenuRequested(QPoint)),
+		this, SLOT(displayPairingMenu(QPoint)));
+	connect(localDevice, SIGNAL(pairingFinished(QBluetoothAddress, QBluetoothLocalDevice::Pairing)),
+		this, SLOT(pairingFinished(QBluetoothAddress, QBluetoothLocalDevice::Pairing)));
+
+	connect(localDevice, SIGNAL(error(QBluetoothLocalDevice::Error)),
+		this, SLOT(error(QBluetoothLocalDevice::Error)));
+}
+
+BtDeviceSelectionDialog::~BtDeviceSelectionDialog()
+{
+	delete ui;
+}
+
+void BtDeviceSelectionDialog::on_changeDeviceState_clicked()
+{
+	if (localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff) {
+		ui->dialogStatus->setText("Trying to turn on the local Bluetooth device...");
+		localDevice->powerOn();
+	} else {
+		ui->dialogStatus->setText("Trying to turn off the local Bluetooth device...");
+		localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);
+	}
+}
+
+void BtDeviceSelectionDialog::on_save_clicked()
+{
+	/* Get the selected device. There will be always a selected device if the save button is enabled. */
+	QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
+	QBluetoothDeviceInfo remoteDeviceInfo = currentItem->data(Qt::UserRole).value<QBluetoothDeviceInfo>();
+
+	/* Save the selected device */
+	selectedRemoteDeviceInfo = QSharedPointer<QBluetoothDeviceInfo>(new QBluetoothDeviceInfo(remoteDeviceInfo));
+
+	/* Close the device selection dialog and set the result code to Accepted */
+	accept();
+}
+
+void BtDeviceSelectionDialog::on_clear_clicked()
+{
+	ui->dialogStatus->setText("Remote devices list was cleaned.");
+	ui->discoveredDevicesList->clear();
+	ui->save->setEnabled(false);
+}
+
+void BtDeviceSelectionDialog::on_scan_clicked()
+{
+	ui->dialogStatus->setText("Scanning for remote devices...");
+	remoteDeviceDiscoveryAgent->start();
+	ui->scan->setEnabled(false);
+}
+
+void BtDeviceSelectionDialog::remoteDeviceScanFinished()
+{
+	ui->dialogStatus->setText("Scanning finished.");
+	ui->scan->setEnabled(true);
+}
+
+void BtDeviceSelectionDialog::hostModeStateChanged(QBluetoothLocalDevice::HostMode mode)
+{
+	bool on = !(mode == QBluetoothLocalDevice::HostPoweredOff);
+
+	ui->dialogStatus->setText(QString("The local Bluetooth device was turned %1.")
+				  .arg(on? "ON" : "OFF"));
+	ui->deviceState->setChecked(on);
+	ui->scan->setEnabled(on);
+}
+
+void BtDeviceSelectionDialog::addRemoteDevice(const QBluetoothDeviceInfo &remoteDeviceInfo)
+{
+	//TODO use a QTableView
+	QString deviceLable = QString("%1  (%2)").arg(remoteDeviceInfo.name()).arg(remoteDeviceInfo.address().toString());
+	QList<QListWidgetItem *> itemsWithSameSignature = ui->discoveredDevicesList->findItems(deviceLable, Qt::MatchStartsWith);
+
+	/* Check if the remote device is already in the list */
+	if (itemsWithSameSignature.empty()) {
+		QListWidgetItem *item = new QListWidgetItem(deviceLable);
+		QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(remoteDeviceInfo.address());
+		item->setData(Qt::UserRole, QVariant::fromValue(remoteDeviceInfo));
+
+		if (pairingStatus == QBluetoothLocalDevice::Paired) {
+			item->setText(QString("%1   [State: PAIRED]").arg(item->text()));
+			item->setBackgroundColor(QColor(Qt::gray));
+		} else if (pairingStatus == QBluetoothLocalDevice::AuthorizedPaired) {
+			item->setText(QString("%1   [State: AUTHORIZED_PAIRED]").arg(item->text()));
+			item->setBackgroundColor(QColor(Qt::blue));
+		} else {
+			item->setText(QString("%1   [State: UNPAIRED]").arg(item->text()));
+			item->setTextColor(QColor(Qt::black));
+		}
+
+		ui->discoveredDevicesList->addItem(item);
+	}
+}
+
+void BtDeviceSelectionDialog::itemActivated(QListWidgetItem *item)
+{
+	QBluetoothDeviceInfo remoteDeviceInfo = item->data(Qt::UserRole).value<QBluetoothDeviceInfo>();
+	QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(remoteDeviceInfo.address());
+
+	if (pairingStatus == QBluetoothLocalDevice::Unpaired) {
+		ui->dialogStatus->setText(QString("The device %1 must be paired in order to be used. Please use the context menu for pairing options.")
+					  .arg(remoteDeviceInfo.address().toString()));
+		ui->save->setEnabled(false);
+	} else {
+		ui->dialogStatus->setText(QString("The device %1 can be used for connection. You can press the Save button.")
+					  .arg(remoteDeviceInfo.address().toString()));
+		ui->save->setEnabled(true);
+	}
+}
+
+void BtDeviceSelectionDialog::displayPairingMenu(const QPoint &pos)
+{
+	QMenu menu(this);
+	QAction *pairAction = menu.addAction("Pair");
+	QAction *removePairAction = menu.addAction("Remove Pairing");
+	QAction *chosenAction = menu.exec(ui->discoveredDevicesList->viewport()->mapToGlobal(pos));
+	QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
+	QBluetoothDeviceInfo currentRemoteDeviceInfo = currentItem->data(Qt::UserRole).value<QBluetoothDeviceInfo>();
+	QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(currentRemoteDeviceInfo.address());
+
+	//TODO: disable the actions
+	if (pairingStatus == QBluetoothLocalDevice::Unpaired) {
+		pairAction->setEnabled(true);
+		removePairAction->setEnabled(false);
+	} else {
+		pairAction->setEnabled(false);
+		removePairAction->setEnabled(true);
+	}
+
+	if (chosenAction == pairAction) {
+		ui->dialogStatus->setText(QString("Trying to pair device %1")
+					  .arg(currentRemoteDeviceInfo.address().toString()));
+		localDevice->requestPairing(currentRemoteDeviceInfo.address(), QBluetoothLocalDevice::Paired);
+	} else if (chosenAction == removePairAction) {
+		ui->dialogStatus->setText(QString("Trying to unpair device %1")
+					  .arg(currentRemoteDeviceInfo.address().toString()));
+		localDevice->requestPairing(currentRemoteDeviceInfo.address(), QBluetoothLocalDevice::Unpaired);
+	}
+}
+
+void BtDeviceSelectionDialog::pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
+{
+	QString remoteDeviceStringAddress = address.toString();
+	QList<QListWidgetItem *> items = ui->discoveredDevicesList->findItems(remoteDeviceStringAddress, Qt::MatchContains);
+
+	if (pairing == QBluetoothLocalDevice::Paired || pairing == QBluetoothLocalDevice::Paired ) {
+		ui->dialogStatus->setText(QString("Device %1 was paired.")
+					  .arg(remoteDeviceStringAddress));
+
+		for (int i = 0; i < items.count(); ++i) {
+			QListWidgetItem *item = items.at(i);
+
+			item->setText(QString("%1   [State: PAIRED]").arg(remoteDeviceStringAddress));
+			item->setBackgroundColor(QColor(Qt::gray));
+		}
+
+		QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
+
+		if (currentItem != NULL && currentItem->text().contains(remoteDeviceStringAddress, Qt::CaseInsensitive)) {
+			ui->dialogStatus->setText(QString("The device %1 can now be used for connection. You can press the Save button.")
+						  .arg(remoteDeviceStringAddress));
+			ui->save->setEnabled(true);
+		}
+	} else {
+		ui->dialogStatus->setText(QString("Device %1 was unpaired.")
+					  .arg(remoteDeviceStringAddress));
+
+		for (int i = 0; i < items.count(); ++i) {
+			QListWidgetItem *item = items.at(i);
+
+			item->setText(QString("%1   [State: UNPAIRED]").arg(remoteDeviceStringAddress));
+			item->setBackgroundColor(QColor(Qt::white));
+		}
+
+		QListWidgetItem *currentItem = ui->discoveredDevicesList->currentItem();
+
+		if (currentItem != NULL && currentItem->text().contains(remoteDeviceStringAddress, Qt::CaseInsensitive)) {
+			ui->dialogStatus->setText(QString("The device %1 must be paired in order to be used. Please use the context menu for pairing options.")
+						  .arg(remoteDeviceStringAddress));
+			ui->save->setEnabled(false);
+		}
+	}
+}
+
+void BtDeviceSelectionDialog::error(QBluetoothLocalDevice::Error error)
+{
+	ui->dialogStatus->setText(QString("Local device error: %1.")
+				  .arg((error == QBluetoothLocalDevice::PairingError)? "Pairing error" : "Unknown error"));
+}
+
+QString BtDeviceSelectionDialog::getSelectedDeviceAddress()
+{
+	if (selectedRemoteDeviceInfo) {
+		return selectedRemoteDeviceInfo.data()->address().toString();
+	}
+
+	return QString();
+}
+
+QString BtDeviceSelectionDialog::getSelectedDeviceName()
+{
+	if (selectedRemoteDeviceInfo) {
+		return selectedRemoteDeviceInfo.data()->name();
+	}
+
+	return QString();
+}
diff --git a/qt-ui/btdeviceselectiondialog.h b/qt-ui/btdeviceselectiondialog.h
new file mode 100644
index 0000000..6bcc43f
--- /dev/null
+++ b/qt-ui/btdeviceselectiondialog.h
@@ -0,0 +1,46 @@
+#ifndef BTDEVICESELECTIONDIALOG_H
+#define BTDEVICESELECTIONDIALOG_H
+
+#include <QDialog>
+#include <QListWidgetItem>
+#include <QPointer>
+#include <QtBluetooth/QBluetoothLocalDevice>
+#include <QtBluetooth/qbluetoothglobal.h>
+#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
+
+Q_DECLARE_METATYPE(QBluetoothDeviceInfo)
+
+namespace Ui {
+	class BtDeviceSelectionDialog;
+}
+
+class BtDeviceSelectionDialog : public QDialog {
+	Q_OBJECT
+
+public:
+	explicit BtDeviceSelectionDialog(QWidget *parent = 0);
+	~BtDeviceSelectionDialog();
+	QString getSelectedDeviceAddress();
+	QString getSelectedDeviceName();
+
+private slots:
+	void on_changeDeviceState_clicked();
+	void on_save_clicked();
+	void on_clear_clicked();
+	void on_scan_clicked();
+	void remoteDeviceScanFinished();
+	void hostModeStateChanged(QBluetoothLocalDevice::HostMode mode);
+	void addRemoteDevice(const QBluetoothDeviceInfo &remoteDeviceInfo);
+	void itemActivated(QListWidgetItem *item);
+	void displayPairingMenu(const QPoint &pos);
+	void pairingFinished(const QBluetoothAddress &address,QBluetoothLocalDevice::Pairing pairing);
+	void error(QBluetoothLocalDevice::Error error);
+
+private:
+	Ui::BtDeviceSelectionDialog *ui;
+	QBluetoothLocalDevice *localDevice;
+	QBluetoothDeviceDiscoveryAgent *remoteDeviceDiscoveryAgent;
+	QSharedPointer<QBluetoothDeviceInfo> selectedRemoteDeviceInfo;
+};
+
+#endif // BTDEVICESELECTIONDIALOG_H
diff --git a/qt-ui/btdeviceselectiondialog.ui b/qt-ui/btdeviceselectiondialog.ui
new file mode 100644
index 0000000..345cd13
--- /dev/null
+++ b/qt-ui/btdeviceselectiondialog.ui
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BtDeviceSelectionDialog</class>
+ <widget class="QDialog" name="BtDeviceSelectionDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>735</width>
+    <height>460</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Remote Bluetooth device selection</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="discoveredDevicesLabel">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="font">
+      <font>
+       <weight>75</weight>
+       <bold>true</bold>
+      </font>
+     </property>
+     <property name="text">
+      <string>Discovered devices</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <layout class="QHBoxLayout" name="dialogControls">
+     <item>
+      <widget class="QPushButton" name="save">
+       <property name="text">
+        <string>Save</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="quit">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Quit</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="0" rowspan="2">
+    <layout class="QVBoxLayout" name="remoteDevicesSection">
+     <item>
+      <widget class="QListWidget" name="discoveredDevicesList">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="scanningControls">
+       <item>
+        <widget class="QPushButton" name="scan">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string>Scan</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="clear">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string>Clear</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="1">
+    <widget class="QGroupBox" name="localDeviceDetails">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="font">
+      <font>
+       <weight>75</weight>
+       <bold>true</bold>
+      </font>
+     </property>
+     <property name="title">
+      <string>Local Bluetooth device details</string>
+     </property>
+     <property name="flat">
+      <bool>false</bool>
+     </property>
+     <layout class="QFormLayout" name="formLayout_2">
+      <item row="0" column="0">
+       <widget class="QLabel" name="deviceNameLable">
+        <property name="text">
+         <string>Name: </string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLineEdit" name="deviceName">
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="deviceAddressLable">
+        <property name="text">
+         <string>Address:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLineEdit" name="deviceAddress">
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QCheckBox" name="deviceState">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="font">
+         <font>
+          <weight>75</weight>
+          <bold>true</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Bluetooth powered on</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QPushButton" name="changeDeviceState">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="font">
+         <font>
+          <weight>50</weight>
+          <bold>false</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Turn On/Off</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="dialogStatus">
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qt-ui/downloadfromdivecomputer.cpp b/qt-ui/downloadfromdivecomputer.cpp
index 838a9e5..276e404 100644
--- a/qt-ui/downloadfromdivecomputer.cpp
+++ b/qt-ui/downloadfromdivecomputer.cpp
@@ -99,6 +99,8 @@ DownloadFromDCWidget::DownloadFromDCWidget(QWidget *parent, Qt::WindowFlags f) :
 	ui.ok->setEnabled(false);
 	ui.downloadCancelRetryButton->setEnabled(true);
 	ui.downloadCancelRetryButton->setText(tr("Download"));
+
+	btDeviceSelectionDialog = 0;
 	ui.chooseBluetoothDevice->setEnabled(ui.bluetoothMode->isChecked());
 	connect(ui.bluetoothMode, SIGNAL(stateChanged(int)), this, SLOT(enableBluetoothMode(int)));
 	connect(ui.chooseBluetoothDevice, SIGNAL(clicked()), this, SLOT(selectRemoteBluetoothDevice()));
@@ -311,7 +313,11 @@ void DownloadFromDCWidget::on_downloadCancelRetryButton_clicked()
 
 	data.vendor = strdup(ui.vendor->currentText().toUtf8().data());
 	data.product = strdup(ui.product->currentText().toUtf8().data());
-	if (same_string(data.vendor, "Uemis")) {
+	data.bluetooth_mode = ui.bluetoothMode->isChecked();
+	if (data.bluetooth_mode) {
+		// Get the selected device address
+		data.devname = strdup(btDeviceSelectionDialog->getSelectedDeviceAddress().toUtf8().data());
+	} else if (same_string(data.vendor, "Uemis")) {
 		char *colon;
 		char *devname = strdup(ui.device->currentText().toUtf8().data());
 
@@ -523,8 +529,24 @@ void DownloadFromDCWidget::markChildrenAsEnabled()
 
 void DownloadFromDCWidget::selectRemoteBluetoothDevice()
 {
-	qWarning() << "Selecting a remote Bluetooth device...";
-	//TODO add implementation
+	if (!btDeviceSelectionDialog) {
+		btDeviceSelectionDialog = new BtDeviceSelectionDialog(this);
+		connect(btDeviceSelectionDialog, SIGNAL(finished(int)),
+			this, SLOT(bluetoothSelectionDialogIsFinished(int)));
+	}
+
+	btDeviceSelectionDialog->show();
+}
+
+void DownloadFromDCWidget::bluetoothSelectionDialogIsFinished(int result)
+{
+	if (result == QDialog::Accepted) {
+		/* Make the selected Bluetooth device default */
+		ui.device->setCurrentText(btDeviceSelectionDialog->getSelectedDeviceName());
+	} else if (result == QDialog::Rejected){
+		/* Disable Bluetooth download mode */
+		ui.bluetoothMode->setChecked(false);
+	}
 }
 
 void DownloadFromDCWidget::enableBluetoothMode(int state)
diff --git a/qt-ui/downloadfromdivecomputer.h b/qt-ui/downloadfromdivecomputer.h
index 0b63d28..734e5f7 100644
--- a/qt-ui/downloadfromdivecomputer.h
+++ b/qt-ui/downloadfromdivecomputer.h
@@ -10,6 +10,7 @@
 #include "libdivecomputer.h"
 #include "configuredivecomputerdialog.h"
 #include "ui_downloadfromdivecomputer.h"
+#include "btdeviceselectiondialog.h"
 
 class QStringListModel;
 
@@ -81,6 +82,7 @@ slots:
 	void pickDumpFile();
 	void pickLogFile();
 	void selectRemoteBluetoothDevice();
+	void bluetoothSelectionDialogIsFinished(int result);
 
 private:
 	void markChildrenAsDisabled();
@@ -106,6 +108,7 @@ private:
 	bool dumpWarningShown;
 	OstcFirmwareCheck *ostcFirmwareCheck;
 	DiveImportedModel *diveImportedModel;
+	BtDeviceSelectionDialog *btDeviceSelectionDialog;
 
 public:
 	bool preferDownloaded();
-- 
2.1.4

From 36dd814685a84aeb040bf6167539813607b220b1 Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sun, 28 Jun 2015 22:41:12 +0300
Subject: [PATCH 3/4] 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.

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.clau...@ymail.com>
---
 CMakeLists.txt                   |   1 +
 configuredivecomputerthreads.cpp |  10 ++
 libdivecomputer.c                |  22 +++-
 libdivecomputer.h                |   1 +
 qtserialbluetooth.cpp            | 221 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 252 insertions(+), 3 deletions(-)
 create mode 100644 qtserialbluetooth.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 92d9f81..d218778 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -288,6 +288,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 5c610db..e074660 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 ef6e4f8..9c5eaf3 100644
--- a/libdivecomputer.c
+++ b/libdivecomputer.c
@@ -917,14 +917,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 f5c0cad..649d898 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 0000000..329907b
--- /dev/null
+++ b/qtserialbluetooth.cpp
@@ -0,0 +1,221 @@
+#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;
+	timer.setSingleShot(true);
+	loop.connect(&timer, SIGNAL(timeout()), SLOT(quit()));
+
+	// Try to connect to the Serial Port Profile service
+	serial_port->socket->connectToService(QBluetoothAddress(devaddr), QBluetoothUuid::SerialPort);
+	timer.start(5000);
+	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)
+			    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;
+}
+}
-- 
2.1.4

From 200925b3767675b8980e03e264fbf9f4e9b84afc Mon Sep 17 00:00:00 2001
From: Claudiu Olteanu <olteanu.clau...@ymail.com>
Date: Sun, 28 Jun 2015 23:18:35 +0300
Subject: [PATCH 4/4] Enable QtBluetooth logging

This patch increases the verbosity level for QtBluetooth API.

Signed-off-by: Claudiu Olteanu <olteanu.clau...@ymail.com>
---
 main.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/main.cpp b/main.cpp
index b05b802..747d2b2 100644
--- a/main.cpp
+++ b/main.cpp
@@ -15,6 +15,7 @@
 
 #include <QStringList>
 #include <QApplication>
+#include <QLoggingCategory>
 #include <git2.h>
 
 QTranslator *qtTranslator, *ssrfTranslator;
@@ -23,7 +24,7 @@ int main(int argc, char **argv)
 {
 	int i;
 	bool no_filenames = true;
-
+	QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
 	QApplication *application = new QApplication(argc, argv);
 	QStringList files;
 	QStringList importedFiles;
-- 
2.1.4

_______________________________________________
subsurface mailing list
subsurface@subsurface-divelog.org
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface

Reply via email to