Add BLE support for the Shearwater devices

The main difference with the serial communication is that the BLE
communication transmits each SLIP encoded data packet as one or more BLE
data packets. The BLE packets have an extra two byte header with the
total number of packets and the current packet number.
This commit is contained in:
Jef Driesen 2018-03-13 23:01:16 +01:00
parent 0978f8c0fa
commit 8f7abc5a2d
2 changed files with 95 additions and 53 deletions

View File

@ -284,11 +284,11 @@ static const dc_descriptor_t g_descriptors[] = {
{"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater}, {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater},
/* Shearwater Petrel */ /* Shearwater Petrel */
{"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater}, {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater},
{"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater}, {"Shearwater", "Petrel 2", DC_FAMILY_SHEARWATER_PETREL, 3, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater}, {"Shearwater", "Nerd", DC_FAMILY_SHEARWATER_PETREL, 4, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater},
{"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater}, {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6, DC_TRANSPORT_NONE, dc_filter_shearwater}, {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6, DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, DC_TRANSPORT_NONE, dc_filter_shearwater}, {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, DC_TRANSPORT_BLE, dc_filter_shearwater},
/* Dive Rite NiTek Q */ /* Dive Rite NiTek Q */
{"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL}, {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL},
/* Citizen Hyper Aqualand */ /* Citizen Hyper Aqualand */

View File

@ -25,6 +25,7 @@
#include "shearwater_common.h" #include "shearwater_common.h"
#include "context-private.h" #include "context-private.h"
#include "platform.h"
#include "array.h" #include "array.h"
#define SZ_PACKET 254 #define SZ_PACKET 254
@ -122,19 +123,33 @@ shearwater_common_decompress_xor (unsigned char *data, unsigned int size)
return 0; return 0;
} }
static dc_status_t static dc_status_t
shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned char data[], unsigned int size) shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned char data[], unsigned int size)
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
unsigned char buffer[32]; unsigned char buffer[32];
unsigned int nbytes = 0; unsigned int nbytes = 0;
#if 0 if (transport == DC_TRANSPORT_BLE) {
// Send an initial END character to flush out any data that may have // Calculate the total number of bytes.
// accumulated in the receiver due to line noise. unsigned int count = 1;
buffer[nbytes++] = END; for (unsigned int i = 0; i < size; ++i) {
#endif unsigned char c = data[i];
if (c == END || c == ESC) {
count += 2;
} else {
count++;
}
}
// Calculate the total number of frames.
unsigned int nframes = (count + sizeof(buffer) - 1) / sizeof(buffer);
buffer[0] = nframes;
buffer[1] = 0;
nbytes = 2;
}
for (unsigned int i = 0; i < size; ++i) { for (unsigned int i = 0; i < size; ++i) {
unsigned char c = data[i]; unsigned char c = data[i];
@ -151,7 +166,12 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned
return status; return status;
} }
nbytes = 0; if (transport == DC_TRANSPORT_BLE) {
buffer[1]++;
nbytes = 2;
} else {
nbytes = 0;
}
} }
// Escape the character. // Escape the character.
@ -173,7 +193,12 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned
return status; return status;
} }
nbytes = 0; if (transport == DC_TRANSPORT_BLE) {
buffer[1]++;
nbytes = 2;
} else {
nbytes = 0;
}
} }
} }
@ -195,70 +220,87 @@ static dc_status_t
shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char data[], unsigned int size, unsigned int *actual) shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char data[], unsigned int size, unsigned int *actual)
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
unsigned char buffer[256];
unsigned int escaped = 0; unsigned int escaped = 0;
unsigned int nbytes = 0; unsigned int nbytes = 0;
// Get the packet size.
size_t packetsize = (transport == DC_TRANSPORT_BLE) ? sizeof(buffer) : 1;
// Read bytes until a complete packet has been received. If the // Read bytes until a complete packet has been received. If the
// buffer runs out of space, bytes are dropped. The caller can // buffer runs out of space, bytes are dropped. The caller can
// detect this condition because the return value will be larger // detect this condition because the return value will be larger
// than the supplied buffer size. // than the supplied buffer size.
while (1) { while (1) {
unsigned char c = 0; size_t transferred = 0;
status = dc_iostream_read (device->iostream, buffer, packetsize, &transferred);
// Get a single character to process.
status = dc_iostream_read (device->iostream, &c, 1, NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (device->base.context, "Failed to receive the packet."); ERROR (device->base.context, "Failed to receive the packet.");
return status; return status;
} }
if (c == END || c == ESC) { size_t offset = 0;
if (escaped) { if (transport == DC_TRANSPORT_BLE) {
// If the END or ESC characters are escaped, then we if (transferred < 2) {
// have a protocol violation, and an error is reported. ERROR (device->base.context, "Invalid packet length (" DC_PRINTF_SIZE ").", transferred);
ERROR (device->base.context, "SLIP frame escaped the special character %02x.", c);
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
} }
if (c == END) { offset = 2;
// If it's an END character then we're done. }
// As a minor optimization, empty packets are ignored. This
// is to avoid bothering the upper layers with all the empty for (size_t i = offset; i < transferred; ++i) {
// packets generated by the duplicate END characters which unsigned char c = buffer[i];
// are sent to try to detect line noise.
if (nbytes) { if (c == END || c == ESC) {
goto done; if (escaped) {
// If the END or ESC characters are escaped, then we
// have a protocol violation, and an error is reported.
ERROR (device->base.context, "SLIP frame escaped the special character %02x.", c);
return DC_STATUS_PROTOCOL;
} }
} else {
// If it's an ESC character, get another character and then if (c == END) {
// figure out what to store in the packet based on that. // If it's an END character then we're done.
escaped = 1; // As a minor optimization, empty packets are ignored. This
// is to avoid bothering the upper layers with all the empty
// packets generated by the duplicate END characters which
// are sent to try to detect line noise.
if (nbytes) {
goto done;
}
} else {
// If it's an ESC character, get another character and then
// figure out what to store in the packet based on that.
escaped = 1;
}
continue;
} }
continue; if (escaped) {
} // If it's not one of the two escaped characters, then we
// have a protocol violation. The best bet seems to be to
// leave the byte alone and just stuff it into the packet.
switch (c) {
case ESC_END:
c = END;
break;
case ESC_ESC:
c = ESC;
break;
default:
break;
}
if (escaped) { escaped = 0;
// If it's not one of the two escaped characters, then we
// have a protocol violation. The best bet seems to be to
// leave the byte alone and just stuff it into the packet.
switch (c) {
case ESC_END:
c = END;
break;
case ESC_ESC:
c = ESC;
break;
default:
break;
} }
escaped = 0; if (nbytes < size)
data[nbytes] = c;
nbytes++;
} }
if (nbytes < size)
data[nbytes] = c;
nbytes++;
} }
done: done: