From 2efa83eeee5bfcffcd1da30344828cd11e4a3824 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 17 Aug 2019 11:53:25 -0700 Subject: [PATCH] Deepblu Cosmiq+: start downloading dive data This actually seems to download all the data. It just doesn't parse any of it yet, so you get just empty dives. But the basic packet and data transfer seems to work. Now I need to fill in the details on the actual dive parsing side, and I should see a profile. Signed-off-by: Linus Torvalds --- src/deepblu.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 2 deletions(-) diff --git a/src/deepblu.c b/src/deepblu.c index 51ce069..af40171 100644 --- a/src/deepblu.c +++ b/src/deepblu.c @@ -20,12 +20,32 @@ */ #include +#include #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_58 0x58 // Send empty byte, get single byte back ?? (0x52) +#define CMD_59 0x59 // Send empty byte, get six bytes back (00 00 07 00 00 00) +#define CMD_5b 0x5b // Send empty byte, get six bytes back (00 21 00 14 00 01) +#define CMD_5c 0x5c // Send empty byte, get six bytes back (13 88 00 46 20 00) +#define CMD_5d 0x5d // Send empty byte, get six bytes back (19 00 23 0C 02 0E) +#define CMD_5f 0x5f // Send empty byte, get six bytes back (00 00 07 00 00 00) + typedef struct deepblu_device_t { dc_device_t base; dc_iostream_t *iostream; @@ -34,6 +54,7 @@ typedef struct 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 = { @@ -44,7 +65,7 @@ static const dc_device_vtable_t deepblu_device_vtable = { NULL, /* write */ NULL, /* dump */ deepblu_device_foreach, /* foreach */ - NULL, /* timesync */ + deepblu_device_timesync, /* timesync */ deepblu_device_close, /* close */ }; @@ -245,6 +266,50 @@ deepblu_recv_data(deepblu_device_t *device, const unsigned char expected, unsign 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) { @@ -287,6 +352,36 @@ deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[ 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) @@ -297,11 +392,64 @@ deepblu_device_close (dc_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; + + 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(profile_len); + if (!profile) { + ERROR (device->base.context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + status = deepblu_recv_bulk(device, RSP_DIVEPROF, profile, profile_len); + if (status != DC_STATUS_SUCCESS) + return status; + + if (callback) + callback(profile, profile_len, header, header_len, userdata); + + return DC_STATUS_SUCCESS; +} + static dc_status_t deepblu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { 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; + + for (i = 1; i <= nrdives; i++) { + status = deepblu_download_dive(device, i, callback, userdata); + if (status != DC_STATUS_SUCCESS) + return status; + } - ERROR (device->base.context, "Deepblu Cosmiq+ device_foreach called"); return DC_STATUS_SUCCESS; }