mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 05:00:20 +00:00
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>
This commit is contained in:
parent
dff4e5f33e
commit
198cc41959
5 changed files with 271 additions and 3 deletions
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
240
qtserialbluetooth.cpp
Normal file
240
qtserialbluetooth.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue