Add support for the Deepblu Cosmiq+ dive computer
Somewhat basic support, but the data the Deepblu Cosmiq+ reports is pretty basic. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
4e9e94d9f8
commit
8594445088
@ -91,6 +91,7 @@ static const backend_table_t g_backends[] = {
|
|||||||
{"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0},
|
{"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0},
|
||||||
{"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0},
|
{"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0},
|
||||||
{"descentmk1", DC_FAMILY_GARMIN, 0},
|
{"descentmk1", DC_FAMILY_GARMIN, 0},
|
||||||
|
{"cosmiq", DC_FAMILY_DEEPBLU, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const transport_table_t g_transports[] = {
|
static const transport_table_t g_transports[] = {
|
||||||
|
|||||||
@ -110,6 +110,8 @@ typedef enum dc_family_t {
|
|||||||
DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16),
|
DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16),
|
||||||
/* Garmin */
|
/* Garmin */
|
||||||
DC_FAMILY_GARMIN = (16 << 16),
|
DC_FAMILY_GARMIN = (16 << 16),
|
||||||
|
/* Deepblu */
|
||||||
|
DC_FAMILY_DEEPBLU = (17 << 16),
|
||||||
} dc_family_t;
|
} dc_family_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -506,6 +506,14 @@
|
|||||||
RelativePath="..\src\garmin_parser.c"
|
RelativePath="..\src\garmin_parser.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\src\deepblu.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\src\deepblu_parser.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\src\field-cache.c"
|
RelativePath="..\src\field-cache.c"
|
||||||
>
|
>
|
||||||
@ -856,6 +864,10 @@
|
|||||||
RelativePath="..\src\garmin.h"
|
RelativePath="..\src\garmin.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\src\deepblu.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\src\timer.h"
|
RelativePath="..\src\timer.h"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -74,6 +74,7 @@ libdivecomputer_la_SOURCES = \
|
|||||||
cochran_commander.h cochran_commander.c cochran_commander_parser.c \
|
cochran_commander.h cochran_commander.c cochran_commander_parser.c \
|
||||||
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
|
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
|
||||||
garmin.h garmin.c garmin_parser.c \
|
garmin.h garmin.c garmin_parser.c \
|
||||||
|
deepblu.h deepblu.c deepblu_parser.c \
|
||||||
socket.h socket.c \
|
socket.h socket.c \
|
||||||
irda.c \
|
irda.c \
|
||||||
usbhid.c \
|
usbhid.c \
|
||||||
|
|||||||
500
src/deepblu.c
Normal file
500
src/deepblu.c
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
/*
|
||||||
|
* Deepblu Cosmiq+ downloading
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 Linus Torvalds
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "deepblu.h"
|
||||||
|
#include "context-private.h"
|
||||||
|
#include "device-private.h"
|
||||||
|
#include "array.h"
|
||||||
|
|
||||||
|
// "Write state"
|
||||||
|
#define CMD_SETTIME 0x20 // Send 6 byte date-time, get single-byte 00x00 ack
|
||||||
|
#define CMD_23 0x23 // Send 00/01 byte, get ack back? Some metric/imperial setting?
|
||||||
|
|
||||||
|
// "Read dives"?
|
||||||
|
#define CMD_GETDIVENR 0x40 // Send empty byte, get single-byte number of dives back
|
||||||
|
#define CMD_GETDIVE 0x41 // Send dive number (1-nr) byte, get dive stat length byte back
|
||||||
|
#define RSP_DIVESTAT 0x42 // .. followed by packets of dive stat for that dive of that length
|
||||||
|
#define CMD_GETPROFILE 0x43 // Send dive number (1-nr) byte, get dive profile length BE word back
|
||||||
|
#define RSP_DIVEPROF 0x44 // .. followed by packets of dive profile of that length
|
||||||
|
|
||||||
|
// "Read state"
|
||||||
|
#define CMD_GETTIME 0x50 // Send empty byte, get six-byte bcd date-time back
|
||||||
|
#define CMD_51 0x51 // Send empty byte, get four bytes back (03 dc 00 e3)
|
||||||
|
#define CMD_52 0x52 // Send empty byte, get two bytes back (bf 8d)
|
||||||
|
#define CMD_53 0x53 // Send empty byte, get six bytes back (0e 81 00 03 00 00)
|
||||||
|
#define CMD_54 0x54 // Send empty byte, get byte back (00)
|
||||||
|
#define CMD_55 0x55 // Send empty byte, get byte back (00)
|
||||||
|
#define CMD_56 0x56 // Send empty byte, get byte back (00)
|
||||||
|
#define CMD_57 0x57 // Send empty byte, get byte back (00)
|
||||||
|
#define CMD_58 0x58 // Send empty byte, get byte back (52)
|
||||||
|
#define CMD_59 0x59 // Send empty byte, get six bytes back (00 00 07 00 00 00)
|
||||||
|
// (00 00 00 00 00 00)
|
||||||
|
#define CMD_5a 0x5a // Send empty byte, get six bytes back (23 1b 09 d8 37 c0)
|
||||||
|
#define CMD_5b 0x5b // Send empty byte, get six bytes back (00 21 00 14 00 01)
|
||||||
|
// (00 00 00 14 00 01)
|
||||||
|
#define CMD_5c 0x5c // Send empty byte, get six bytes back (13 88 00 46 20 00)
|
||||||
|
// (13 88 00 3c 15 00)
|
||||||
|
#define CMD_5d 0x5d // Send empty byte, get six bytes back (19 00 23 0C 02 0E)
|
||||||
|
// (14 14 14 0c 01 0e)
|
||||||
|
#define CMD_5f 0x5f // Send empty byte, get six bytes back (00 00 07 00 00 00)
|
||||||
|
|
||||||
|
#define COSMIQ_HDR_SIZE 36
|
||||||
|
|
||||||
|
typedef struct deepblu_device_t {
|
||||||
|
dc_device_t base;
|
||||||
|
dc_iostream_t *iostream;
|
||||||
|
unsigned char fingerprint[COSMIQ_HDR_SIZE];
|
||||||
|
} deepblu_device_t;
|
||||||
|
|
||||||
|
static dc_status_t deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||||
|
static dc_status_t deepblu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||||
|
static dc_status_t deepblu_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime);
|
||||||
|
static dc_status_t deepblu_device_close (dc_device_t *abstract);
|
||||||
|
|
||||||
|
static const dc_device_vtable_t deepblu_device_vtable = {
|
||||||
|
sizeof(deepblu_device_t),
|
||||||
|
DC_FAMILY_DEEPBLU,
|
||||||
|
deepblu_device_set_fingerprint, /* set_fingerprint */
|
||||||
|
NULL, /* read */
|
||||||
|
NULL, /* write */
|
||||||
|
NULL, /* dump */
|
||||||
|
deepblu_device_foreach, /* foreach */
|
||||||
|
deepblu_device_timesync, /* timesync */
|
||||||
|
deepblu_device_close, /* close */
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maximum data in a packet. It's actually much
|
||||||
|
// less than this, since BLE packets are small and
|
||||||
|
// with the 7 bytes of headers and final newline
|
||||||
|
// and the HEX encoding, the actual maximum is
|
||||||
|
// just something like 6 bytes.
|
||||||
|
//
|
||||||
|
// But in theory the data could be done over
|
||||||
|
// multiple packets. That doesn't seem to be
|
||||||
|
// the case in anything I've seen so far.
|
||||||
|
//
|
||||||
|
// Pick something small and easy to use for
|
||||||
|
// stack buffers.
|
||||||
|
#define MAX_DATA 20
|
||||||
|
|
||||||
|
static char *
|
||||||
|
write_hex_byte(unsigned char data, char *p)
|
||||||
|
{
|
||||||
|
static const char hex[16] = "0123456789ABCDEF";
|
||||||
|
*p++ = hex[data >> 4];
|
||||||
|
*p++ = hex[data & 0xf];
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send a cmd packet.
|
||||||
|
//
|
||||||
|
// The format of the cmd on the "wire" is:
|
||||||
|
// - byte '#'
|
||||||
|
// - HEX char of cmd
|
||||||
|
// - HEX char two's complement modular sum of packet data (including cmd/size)
|
||||||
|
// - HEX char size of data as encoded in HEX
|
||||||
|
// - n * HEX char data
|
||||||
|
// - byte '\n'
|
||||||
|
// so you end up having 8 bytes of header/trailer overhead, and two bytes
|
||||||
|
// for every byte of data sent due to the HEX encoding.
|
||||||
|
//
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_send_cmd(deepblu_device_t *device, const unsigned char cmd, const unsigned char data[], size_t size)
|
||||||
|
{
|
||||||
|
char buffer[8+2*MAX_DATA], *p;
|
||||||
|
unsigned char csum;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (size > MAX_DATA)
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
// Calculate packet csum
|
||||||
|
csum = cmd + 2*size;
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
csum += data[i];
|
||||||
|
csum = -csum;
|
||||||
|
|
||||||
|
// Fill the data buffer
|
||||||
|
p = buffer;
|
||||||
|
*p++ = '#';
|
||||||
|
p = write_hex_byte(cmd, p);
|
||||||
|
p = write_hex_byte(csum, p);
|
||||||
|
p = write_hex_byte(size*2, p);
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
p = write_hex_byte(data[i], p);
|
||||||
|
*p++ = '\n';
|
||||||
|
|
||||||
|
// .. and send it out
|
||||||
|
return dc_iostream_write(device->iostream, buffer, p-buffer, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Receive one 'line' of data
|
||||||
|
//
|
||||||
|
// The deepblu BLE protocol is ASCII line based and packetized.
|
||||||
|
// Normally one packet is one line, but it looks like the Nordic
|
||||||
|
// Semi BLE chip will sometimes send packets early (some internal
|
||||||
|
// serial buffer timeout?) with incompete data.
|
||||||
|
//
|
||||||
|
// So read packets until you get newline.
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_recv_line(deepblu_device_t *device, unsigned char *buf, size_t size)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
unsigned char buffer[20];
|
||||||
|
size_t transferred = 0;
|
||||||
|
dc_status_t status;
|
||||||
|
|
||||||
|
status = dc_iostream_read(device->iostream, buffer, sizeof(buffer), &transferred);
|
||||||
|
if (status != DC_STATUS_SUCCESS) {
|
||||||
|
ERROR(device->base.context, "Failed to receive Deepblu reply packet.");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
if (transferred > size) {
|
||||||
|
ERROR(device->base.context, "Deepblu reply packet with too much data (got %zu, expected %zu)", transferred, size);
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
if (!transferred) {
|
||||||
|
ERROR(device->base.context, "Empty Deepblu reply packet");
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
memcpy(buf, buffer, transferred);
|
||||||
|
buf += transferred;
|
||||||
|
size -= transferred;
|
||||||
|
if (buf[-1] == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf[-1] = 0;
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hex_nibble(char c)
|
||||||
|
{
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
read_hex_byte(char *p)
|
||||||
|
{
|
||||||
|
// This is negative if either of the nibbles is invalid
|
||||||
|
return (hex_nibble(p[0]) << 4) | hex_nibble(p[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Receive a reply packet
|
||||||
|
//
|
||||||
|
// The reply packet has the same format as the cmd packet we
|
||||||
|
// send, except the first byte is '$' instead of '#'.
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_recv_data(deepblu_device_t *device, const unsigned char expected, unsigned char *buf, size_t size, size_t *received)
|
||||||
|
{
|
||||||
|
int len, i;
|
||||||
|
dc_status_t status;
|
||||||
|
char buffer[8+2*MAX_DATA];
|
||||||
|
int cmd, csum, ndata;
|
||||||
|
|
||||||
|
status = deepblu_recv_line(device, buffer, sizeof(buffer));
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// deepblu_recv_line() always zero-terminates the result
|
||||||
|
// if it returned success, and has removed the final newline.
|
||||||
|
len = strlen(buffer);
|
||||||
|
HEXDUMP(device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buffer, len);
|
||||||
|
|
||||||
|
// A valid reply should always be at least 7 characters: the
|
||||||
|
// initial '$' and the three header HEX bytes.
|
||||||
|
if (len < 8 || buffer[0] != '$') {
|
||||||
|
ERROR(device->base.context, "Invalid Deepblu reply packet");
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = read_hex_byte(buffer+1);
|
||||||
|
csum = read_hex_byte(buffer+3);
|
||||||
|
ndata = read_hex_byte(buffer+5);
|
||||||
|
if ((cmd | csum | ndata) < 0) {
|
||||||
|
ERROR(device->base.context, "non-hex Deepblu reply packet header");
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the data length: it's the size of the HEX data,
|
||||||
|
// and should also match the line length we got (the 7
|
||||||
|
// is for the header data we already decoded above).
|
||||||
|
if ((ndata & 1) || ndata != len - 7) {
|
||||||
|
ERROR(device->base.context, "Deepblu reply packet data length does not match (claimed %d, got %d)", ndata, len-7);
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ndata >> 1 > size) {
|
||||||
|
ERROR(device->base.context, "Deepblu reply packet too big for buffer (ndata=%d, size=%zu)", ndata, size);
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
csum += cmd + ndata;
|
||||||
|
|
||||||
|
for (i = 7; i < len; i += 2) {
|
||||||
|
int byte = read_hex_byte(buffer + i);
|
||||||
|
if (byte < 0) {
|
||||||
|
ERROR(device->base.context, "Deepblu reply packet data not valid hex");
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
*buf++ = byte;
|
||||||
|
csum += byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (csum & 255) {
|
||||||
|
ERROR(device->base.context, "Deepblu reply packet csum not valid (%x)", csum);
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
*received = ndata >> 1;
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common communication pattern: send a command, expect data back with the same
|
||||||
|
// command byte.
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_send_recv(deepblu_device_t *device, const unsigned char cmd,
|
||||||
|
const unsigned char *data, size_t data_size,
|
||||||
|
unsigned char *result, size_t result_size)
|
||||||
|
{
|
||||||
|
dc_status_t status;
|
||||||
|
size_t got;
|
||||||
|
|
||||||
|
status = deepblu_send_cmd(device, cmd, data, data_size);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
status = deepblu_recv_data(device, cmd, result, result_size, &got);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
if (got != result_size) {
|
||||||
|
ERROR(device->base.context, "Deepblu result size didn't match expected (expected %zu, got %zu)",
|
||||||
|
result_size, got);
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_recv_bulk(deepblu_device_t *device, const unsigned char cmd, unsigned char *buf, size_t len)
|
||||||
|
{
|
||||||
|
while (len) {
|
||||||
|
dc_status_t status;
|
||||||
|
size_t got;
|
||||||
|
|
||||||
|
status = deepblu_recv_data(device, cmd, buf, len, &got);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
if (got > len) {
|
||||||
|
ERROR(device->base.context, "Deepblu bulk receive overflow");
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
buf += got;
|
||||||
|
len -= got;
|
||||||
|
}
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
deepblu_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
|
||||||
|
{
|
||||||
|
deepblu_device_t *device;
|
||||||
|
|
||||||
|
if (out == NULL)
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
// Allocate memory.
|
||||||
|
device = (deepblu_device_t *) dc_device_allocate (context, &deepblu_device_vtable);
|
||||||
|
if (device == NULL) {
|
||||||
|
ERROR (context, "Failed to allocate memory.");
|
||||||
|
return DC_STATUS_NOMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default values.
|
||||||
|
device->iostream = iostream;
|
||||||
|
memset(device->fingerprint, 0, sizeof(device->fingerprint));
|
||||||
|
|
||||||
|
*out = (dc_device_t *) device;
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
|
||||||
|
{
|
||||||
|
deepblu_device_t *device = (deepblu_device_t *)abstract;
|
||||||
|
|
||||||
|
HEXDUMP(device->base.context, DC_LOGLEVEL_DEBUG, "set_fingerprint", data, size);
|
||||||
|
|
||||||
|
if (size && size != sizeof (device->fingerprint))
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
|
||||||
|
else
|
||||||
|
memset (device->fingerprint, 0, sizeof (device->fingerprint));
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char bcd(int val)
|
||||||
|
{
|
||||||
|
if (val >= 0 && val < 100) {
|
||||||
|
int high = val / 10;
|
||||||
|
int low = val % 10;
|
||||||
|
return (high << 4) | low;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||||
|
{
|
||||||
|
deepblu_device_t *device = (deepblu_device_t *)abstract;
|
||||||
|
unsigned char result[1], data[6];
|
||||||
|
dc_status_t status;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
data[0] = bcd(datetime->year - 2000);
|
||||||
|
data[1] = bcd(datetime->month);
|
||||||
|
data[2] = bcd(datetime->day);
|
||||||
|
data[3] = bcd(datetime->hour);
|
||||||
|
data[4] = bcd(datetime->minute);
|
||||||
|
data[5] = bcd(datetime->second);
|
||||||
|
|
||||||
|
// Maybe also check that we received one zero byte (ack?)
|
||||||
|
return deepblu_send_recv(device, CMD_SETTIME,
|
||||||
|
data, sizeof(data),
|
||||||
|
result, sizeof(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_device_close (dc_device_t *abstract)
|
||||||
|
{
|
||||||
|
deepblu_device_t *device = (deepblu_device_t *) abstract;
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char zero[MAX_DATA];
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_download_dive(deepblu_device_t *device, unsigned char nr, dc_dive_callback_t callback, void *userdata)
|
||||||
|
{
|
||||||
|
unsigned char header_len;
|
||||||
|
unsigned char profilebytes[2];
|
||||||
|
unsigned int profile_len;
|
||||||
|
dc_status_t status;
|
||||||
|
char header[256];
|
||||||
|
unsigned char *profile;
|
||||||
|
|
||||||
|
status = deepblu_send_recv(device, CMD_GETDIVE, &nr, 1, &header_len, 1);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
status = deepblu_recv_bulk(device, RSP_DIVESTAT, header, header_len);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
memset(header + header_len, 0, 256 - header_len);
|
||||||
|
|
||||||
|
/* The header is the fingerprint. If we've already seen this header, we're done */
|
||||||
|
if (memcmp(header, device->fingerprint, sizeof (device->fingerprint)) == 0)
|
||||||
|
return DC_STATUS_DONE;
|
||||||
|
|
||||||
|
status = deepblu_send_recv(device, CMD_GETPROFILE, &nr, 1, profilebytes, sizeof(profilebytes));
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
profile_len = (profilebytes[0] << 8) | profilebytes[1];
|
||||||
|
|
||||||
|
profile = malloc(256 + profile_len);
|
||||||
|
if (!profile) {
|
||||||
|
ERROR (device->base.context, "Insufficient buffer space available.");
|
||||||
|
return DC_STATUS_NOMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We make the dive data be 256 bytes of header, followed by the profile data
|
||||||
|
memcpy(profile, header, 256);
|
||||||
|
|
||||||
|
status = deepblu_recv_bulk(device, RSP_DIVEPROF, profile+256, profile_len);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
if (!callback(profile, profile_len+256, header, header_len, userdata))
|
||||||
|
return DC_STATUS_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
|
||||||
|
{
|
||||||
|
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||||
|
deepblu_device_t *device = (deepblu_device_t *) abstract;
|
||||||
|
unsigned char nrdives, val;
|
||||||
|
dc_status_t status;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
val = 0;
|
||||||
|
status = deepblu_send_recv(device, CMD_GETDIVENR, &val, 1, &nrdives, 1);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (!nrdives)
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
progress.maximum = nrdives;
|
||||||
|
progress.current = 0;
|
||||||
|
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
|
||||||
|
|
||||||
|
for (i = 1; i <= nrdives; i++) {
|
||||||
|
if (device_is_cancelled(abstract)) {
|
||||||
|
dc_status_set_error(&status, DC_STATUS_CANCELLED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = deepblu_download_dive(device, i, callback, userdata);
|
||||||
|
switch (status) {
|
||||||
|
case DC_STATUS_DONE:
|
||||||
|
i = nrdives;
|
||||||
|
break;
|
||||||
|
case DC_STATUS_SUCCESS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
progress.current = i;
|
||||||
|
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
43
src/deepblu.h
Normal file
43
src/deepblu.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Deepblu Cosmiq+ downloading/parsing
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Linus Torvalds
|
||||||
|
*
|
||||||
|
* 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 DEEPBLU_H
|
||||||
|
#define DEEPBLU_H
|
||||||
|
|
||||||
|
#include <libdivecomputer/context.h>
|
||||||
|
#include <libdivecomputer/iostream.h>
|
||||||
|
#include <libdivecomputer/device.h>
|
||||||
|
#include <libdivecomputer/parser.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
deepblu_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
deepblu_parser_create (dc_parser_t **parser, dc_context_t *context);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
#endif /* DEEPBLU_H */
|
||||||
278
src/deepblu_parser.c
Normal file
278
src/deepblu_parser.c
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* Deeplu Cosmiq+ parsing
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 Linus Torvalds
|
||||||
|
*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "deepblu.h"
|
||||||
|
#include "context-private.h"
|
||||||
|
#include "parser-private.h"
|
||||||
|
#include "array.h"
|
||||||
|
#include "field-cache.h"
|
||||||
|
|
||||||
|
#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
|
||||||
|
|
||||||
|
#define MAXFIELDS 128
|
||||||
|
|
||||||
|
struct msg_desc;
|
||||||
|
|
||||||
|
typedef struct deepblu_parser_t {
|
||||||
|
dc_parser_t base;
|
||||||
|
|
||||||
|
dc_sample_callback_t callback;
|
||||||
|
void *userdata;
|
||||||
|
|
||||||
|
// 20 sec for scuba, 1 sec for freedives
|
||||||
|
int sample_interval;
|
||||||
|
|
||||||
|
// Common fields
|
||||||
|
struct dc_field_cache cache;
|
||||||
|
} deepblu_parser_t;
|
||||||
|
|
||||||
|
static dc_status_t deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||||
|
static dc_status_t deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||||
|
static dc_status_t deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||||
|
static dc_status_t deepblu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||||
|
|
||||||
|
static const dc_parser_vtable_t deepblu_parser_vtable = {
|
||||||
|
sizeof(deepblu_parser_t),
|
||||||
|
DC_FAMILY_DEEPBLU,
|
||||||
|
deepblu_parser_set_data, /* set_data */
|
||||||
|
deepblu_parser_get_datetime, /* datetime */
|
||||||
|
deepblu_parser_get_field, /* fields */
|
||||||
|
deepblu_parser_samples_foreach, /* samples_foreach */
|
||||||
|
NULL /* destroy */
|
||||||
|
};
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
deepblu_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||||
|
{
|
||||||
|
deepblu_parser_t *parser = NULL;
|
||||||
|
|
||||||
|
if (out == NULL)
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
// Allocate memory.
|
||||||
|
parser = (deepblu_parser_t *) dc_parser_allocate (context, &deepblu_parser_vtable);
|
||||||
|
if (parser == NULL) {
|
||||||
|
ERROR (context, "Failed to allocate memory.");
|
||||||
|
return DC_STATUS_NOMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = (dc_parser_t *) parser;
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
pressure_to_depth(unsigned int mbar)
|
||||||
|
{
|
||||||
|
// Specific weight of seawater (millibar to cm)
|
||||||
|
const double specific_weight = 1.024 * 0.980665;
|
||||||
|
|
||||||
|
// Absolute pressure, subtract surface pressure
|
||||||
|
if (mbar < 1013)
|
||||||
|
return 0.0;
|
||||||
|
mbar -= 1013;
|
||||||
|
return mbar / specific_weight / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||||
|
{
|
||||||
|
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||||
|
const unsigned char *hdr = data;
|
||||||
|
const unsigned char *profile = data + 256;
|
||||||
|
unsigned int divetime, maxpressure;
|
||||||
|
dc_gasmix_t gasmix = {0, };
|
||||||
|
|
||||||
|
if (size < 256)
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
|
||||||
|
deepblu->callback = NULL;
|
||||||
|
deepblu->userdata = NULL;
|
||||||
|
memset(&deepblu->cache, 0, sizeof(deepblu->cache));
|
||||||
|
|
||||||
|
// LE16 at 0 is 'dive number'
|
||||||
|
|
||||||
|
// LE16 at 12 is the dive time
|
||||||
|
// It's in seconds for freedives, minutes for scuba/gauge
|
||||||
|
divetime = hdr[12] + 256*hdr[13];
|
||||||
|
|
||||||
|
// Byte at 2 is 'activity type' (2 = scuba, 3 = gauge, 4 = freedive)
|
||||||
|
// Byte at 3 is O2 percentage
|
||||||
|
switch (data[2]) {
|
||||||
|
case 2:
|
||||||
|
// SCUBA - divetime in minutes
|
||||||
|
divetime *= 60;
|
||||||
|
gasmix.oxygen = data[3] / 100.0;
|
||||||
|
DC_ASSIGN_IDX(deepblu->cache, GASMIX, 0, gasmix);
|
||||||
|
DC_ASSIGN_FIELD(deepblu->cache, GASMIX_COUNT, 1);
|
||||||
|
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_OC);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// GAUGE - divetime in minutes
|
||||||
|
divetime *= 60;
|
||||||
|
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_GAUGE);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
// FREEDIVE - divetime in seconds
|
||||||
|
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_FREEDIVE);
|
||||||
|
deepblu->sample_interval = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ERROR (abstract->context, "Deepblu: unknown activity type '%02x'", data[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seems to be fixed at 20s for scuba, 1s for freedive
|
||||||
|
deepblu->sample_interval = hdr[26];
|
||||||
|
|
||||||
|
maxpressure = hdr[22] + 256*hdr[23]; // Maxpressure in millibar
|
||||||
|
|
||||||
|
DC_ASSIGN_FIELD(deepblu->cache, DIVETIME, divetime);
|
||||||
|
DC_ASSIGN_FIELD(deepblu->cache, MAXDEPTH, pressure_to_depth(maxpressure));
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The layout of the header in the 'data' is
|
||||||
|
// 0: LE16 dive number
|
||||||
|
// 2: dive type byte?
|
||||||
|
// 3: O2 percentage byte
|
||||||
|
// 4: unknown
|
||||||
|
// 5: unknown
|
||||||
|
// 6: LE16 year
|
||||||
|
// 8: day of month
|
||||||
|
// 9: month
|
||||||
|
// 10: minute
|
||||||
|
// 11: hour
|
||||||
|
// 12: LE16 dive time
|
||||||
|
// 14: LE16 ??
|
||||||
|
// 16: LE16 surface pressure?
|
||||||
|
// 18: LE16 ??
|
||||||
|
// 20: LE16 ??
|
||||||
|
// 22: LE16 max depth pressure
|
||||||
|
// 24: LE16 water temp
|
||||||
|
// 26: LE16 ??
|
||||||
|
// 28: LE16 ??
|
||||||
|
// 30: LE16 ??
|
||||||
|
// 32: LE16 ??
|
||||||
|
// 34: LE16 ??
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||||
|
{
|
||||||
|
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||||
|
const unsigned char *data = deepblu->base.data;
|
||||||
|
int len = deepblu->base.size;
|
||||||
|
|
||||||
|
if (len < 256)
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
datetime->year = data[6] + (data[7] << 8);
|
||||||
|
datetime->day = data[8];
|
||||||
|
datetime->month = data[9];
|
||||||
|
datetime->minute = data[10];
|
||||||
|
datetime->hour = data[11];
|
||||||
|
datetime->second = 0;
|
||||||
|
datetime->timezone = DC_TIMEZONE_NONE;
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||||
|
{
|
||||||
|
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
/* This whole sequence should be standardized */
|
||||||
|
if (!(deepblu->cache.initialized & (1 << type)))
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case DC_FIELD_DIVETIME:
|
||||||
|
return DC_FIELD_VALUE(deepblu->cache, value, DIVETIME);
|
||||||
|
case DC_FIELD_MAXDEPTH:
|
||||||
|
return DC_FIELD_VALUE(deepblu->cache, value, MAXDEPTH);
|
||||||
|
case DC_FIELD_AVGDEPTH:
|
||||||
|
return DC_FIELD_VALUE(deepblu->cache, value, AVGDEPTH);
|
||||||
|
case DC_FIELD_GASMIX_COUNT:
|
||||||
|
case DC_FIELD_TANK_COUNT:
|
||||||
|
return DC_FIELD_VALUE(deepblu->cache, value, GASMIX_COUNT);
|
||||||
|
case DC_FIELD_GASMIX:
|
||||||
|
if (flags >= MAXGASES)
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
return DC_FIELD_INDEX(deepblu->cache, value, GASMIX, flags);
|
||||||
|
case DC_FIELD_SALINITY:
|
||||||
|
return DC_FIELD_VALUE(deepblu->cache, value, SALINITY);
|
||||||
|
case DC_FIELD_ATMOSPHERIC:
|
||||||
|
return DC_FIELD_VALUE(deepblu->cache, value, ATMOSPHERIC);
|
||||||
|
case DC_FIELD_DIVEMODE:
|
||||||
|
return DC_FIELD_VALUE(deepblu->cache, value, DIVEMODE);
|
||||||
|
case DC_FIELD_TANK:
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
case DC_FIELD_STRING:
|
||||||
|
return dc_field_get_string(&deepblu->cache, flags, (dc_field_string_t *)value);
|
||||||
|
default:
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
deepblu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||||
|
{
|
||||||
|
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||||
|
const unsigned char *data = deepblu->base.data;
|
||||||
|
int len = deepblu->base.size, i;
|
||||||
|
|
||||||
|
deepblu->callback = callback;
|
||||||
|
deepblu->userdata = userdata;
|
||||||
|
|
||||||
|
// Skip the header information
|
||||||
|
if (len < 256)
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
data += 256;
|
||||||
|
len -= 256;
|
||||||
|
|
||||||
|
// The rest should be samples every 20s with temperature and depth
|
||||||
|
for (i = 0; i < len/4; i++) {
|
||||||
|
dc_sample_value_t sample = {0};
|
||||||
|
unsigned int temp = data[0]+256*data[1];
|
||||||
|
unsigned int pressure = data[2]+256*data[3];
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
sample.time = (i+1)*deepblu->sample_interval;
|
||||||
|
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||||
|
|
||||||
|
sample.depth = pressure_to_depth(pressure);
|
||||||
|
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||||
|
|
||||||
|
sample.temperature = temp / 10.0;
|
||||||
|
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
@ -49,6 +49,7 @@ static int dc_filter_garmin (dc_transport_t transport, const void *userdata);
|
|||||||
static int dc_filter_mares (dc_transport_t transport, const void *userdata);
|
static int dc_filter_mares (dc_transport_t transport, const void *userdata);
|
||||||
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata);
|
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata);
|
||||||
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata);
|
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata);
|
||||||
|
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata);
|
||||||
|
|
||||||
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
||||||
|
|
||||||
@ -379,6 +380,8 @@ static const dc_descriptor_t g_descriptors[] = {
|
|||||||
{"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving},
|
{"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving},
|
||||||
/* Garmin */
|
/* Garmin */
|
||||||
{"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
|
{"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
|
||||||
|
/* Deepblu */
|
||||||
|
{"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU, 0, DC_TRANSPORT_BLE, dc_filter_deepblu},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -645,6 +648,19 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata)
|
||||||
|
{
|
||||||
|
static const char * const bluetooth[] = {
|
||||||
|
"COSMIQ",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (transport == DC_TRANSPORT_BLE) {
|
||||||
|
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
dc_descriptor_iterator (dc_iterator_t **out)
|
dc_descriptor_iterator (dc_iterator_t **out)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -58,6 +58,7 @@
|
|||||||
#include "cochran_commander.h"
|
#include "cochran_commander.h"
|
||||||
#include "tecdiving_divecomputereu.h"
|
#include "tecdiving_divecomputereu.h"
|
||||||
#include "garmin.h"
|
#include "garmin.h"
|
||||||
|
#include "deepblu.h"
|
||||||
|
|
||||||
#include "device-private.h"
|
#include "device-private.h"
|
||||||
#include "context-private.h"
|
#include "context-private.h"
|
||||||
@ -215,6 +216,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
|||||||
case DC_FAMILY_GARMIN:
|
case DC_FAMILY_GARMIN:
|
||||||
rc = garmin_device_open (&device, context, iostream);
|
rc = garmin_device_open (&device, context, iostream);
|
||||||
break;
|
break;
|
||||||
|
case DC_FAMILY_DEEPBLU:
|
||||||
|
rc = deepblu_device_open (&device, context, iostream);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_INVALIDARGS;
|
return DC_STATUS_INVALIDARGS;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,6 +58,7 @@
|
|||||||
#include "cochran_commander.h"
|
#include "cochran_commander.h"
|
||||||
#include "tecdiving_divecomputereu.h"
|
#include "tecdiving_divecomputereu.h"
|
||||||
#include "garmin.h"
|
#include "garmin.h"
|
||||||
|
#include "deepblu.h"
|
||||||
|
|
||||||
#include "context-private.h"
|
#include "context-private.h"
|
||||||
#include "parser-private.h"
|
#include "parser-private.h"
|
||||||
@ -176,6 +177,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
|||||||
case DC_FAMILY_GARMIN:
|
case DC_FAMILY_GARMIN:
|
||||||
rc = garmin_parser_create (&parser, context);
|
rc = garmin_parser_create (&parser, context);
|
||||||
break;
|
break;
|
||||||
|
case DC_FAMILY_DEEPBLU:
|
||||||
|
rc = deepblu_parser_create (&parser, context);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_INVALIDARGS;
|
return DC_STATUS_INVALIDARGS;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user