Merge branch 'eonsteel'
This commit is contained in:
commit
7c95581826
@ -75,6 +75,9 @@ struct directory_entry {
|
||||
#define CMD_SET_DATE 0x0203
|
||||
#define CMD_GET_DATE 0x0303
|
||||
|
||||
#define PACKET_SIZE 64
|
||||
#define HEADER_SIZE 12
|
||||
|
||||
static dc_status_t suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t suunto_eonsteel_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime);
|
||||
@ -142,146 +145,97 @@ static void put_le32(unsigned int val, unsigned char *p)
|
||||
*
|
||||
* The maximum payload is 62 bytes.
|
||||
*/
|
||||
#define PACKET_SIZE 64
|
||||
static int receive_packet(suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||
static dc_status_t
|
||||
suunto_eonsteel_receive(suunto_eonsteel_device_t *device, unsigned char data[], unsigned int size, unsigned int *actual)
|
||||
{
|
||||
unsigned char buf[64];
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
unsigned char buf[PACKET_SIZE];
|
||||
size_t transferred = 0;
|
||||
int len;
|
||||
unsigned int len = 0;
|
||||
|
||||
rc = dc_iostream_read(eon->iostream, buf, PACKET_SIZE, &transferred);
|
||||
rc = dc_iostream_read(device->iostream, buf, sizeof(buf), &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "read interrupt transfer failed");
|
||||
return -1;
|
||||
ERROR(device->base.context, "Failed to receive the packet.");
|
||||
return rc;
|
||||
}
|
||||
if (transferred != PACKET_SIZE) {
|
||||
ERROR(eon->base.context, "incomplete read interrupt transfer (got " DC_PRINTF_SIZE ", expected %d)", transferred, PACKET_SIZE);
|
||||
return -1;
|
||||
|
||||
if (transferred < 2) {
|
||||
ERROR(device->base.context, "Invalid packet length (" DC_PRINTF_SIZE ").", transferred);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
if (buf[0] != 0x3f) {
|
||||
ERROR(eon->base.context, "read interrupt transfer returns wrong report type (%d)", buf[0]);
|
||||
return -1;
|
||||
ERROR(device->base.context, "Invalid report type (%02x).", buf[0]);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
len = buf[1];
|
||||
if (len > PACKET_SIZE-2) {
|
||||
ERROR(eon->base.context, "read interrupt transfer reports bad length (%d)", len);
|
||||
return -1;
|
||||
if (len + 2 > transferred) {
|
||||
ERROR(device->base.context, "Invalid payload length (%u).", len);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
if (len > size) {
|
||||
ERROR(eon->base.context, "receive_packet result buffer too small - truncating");
|
||||
len = size;
|
||||
ERROR(device->base.context, "Insufficient buffer space available.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf+2, len);
|
||||
memcpy(buffer, buf+2, len);
|
||||
return len;
|
||||
|
||||
HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 2, len);
|
||||
|
||||
memcpy(data, buf + 2, len);
|
||||
|
||||
if (actual)
|
||||
*actual = len;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int send_cmd(suunto_eonsteel_device_t *eon,
|
||||
static dc_status_t
|
||||
suunto_eonsteel_send(suunto_eonsteel_device_t *device,
|
||||
unsigned short cmd,
|
||||
unsigned int len,
|
||||
const unsigned char *buffer)
|
||||
const unsigned char data[],
|
||||
unsigned int size)
|
||||
{
|
||||
unsigned char buf[64];
|
||||
unsigned short seq = eon->seq;
|
||||
unsigned int magic = eon->magic;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
unsigned char buf[PACKET_SIZE];
|
||||
size_t transferred = 0;
|
||||
|
||||
// Two-byte packet header, followed by 12 bytes of extended header
|
||||
if (len > sizeof(buf)-2-12) {
|
||||
ERROR(eon->base.context, "send command with too much long");
|
||||
return -1;
|
||||
if (size + 2 + HEADER_SIZE > sizeof(buf)) {
|
||||
ERROR(device->base.context, "Insufficient buffer space available.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
buf[0] = 0x3f;
|
||||
buf[1] = len + 12;
|
||||
buf[1] = size + HEADER_SIZE;
|
||||
|
||||
// 2-byte LE command word
|
||||
put_le16(cmd, buf+2);
|
||||
put_le16(cmd, buf + 2);
|
||||
|
||||
// 4-byte LE magic value (starts at 1)
|
||||
put_le32(magic, buf+4);
|
||||
put_le32(device->magic, buf + 4);
|
||||
|
||||
// 2-byte LE sequence number;
|
||||
put_le16(seq, buf+8);
|
||||
put_le16(device->seq, buf + 8);
|
||||
|
||||
// 4-byte LE length
|
||||
put_le32(len, buf+10);
|
||||
put_le32(size, buf + 10);
|
||||
|
||||
// .. followed by actual data
|
||||
if (len) {
|
||||
memcpy(buf+14, buffer, len);
|
||||
if (size) {
|
||||
memcpy(buf + 14, data, size);
|
||||
}
|
||||
|
||||
rc = dc_iostream_write(eon->iostream, buf, sizeof(buf), &transferred);
|
||||
rc = dc_iostream_write(device->iostream, buf, sizeof(buf), &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "write interrupt transfer failed");
|
||||
return -1;
|
||||
ERROR(device->base.context, "Failed to send the command.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
// dump every outgoing packet?
|
||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "cmd", buf+2, len+12);
|
||||
return 0;
|
||||
}
|
||||
HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "cmd", buf + 2, size + HEADER_SIZE);
|
||||
|
||||
struct eon_hdr {
|
||||
unsigned short cmd;
|
||||
unsigned int magic;
|
||||
unsigned short seq;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
static int receive_header(suunto_eonsteel_device_t *eon, struct eon_hdr *hdr, unsigned char *buffer, int size)
|
||||
{
|
||||
int ret;
|
||||
unsigned char header[64];
|
||||
|
||||
ret = receive_packet(eon, header, sizeof(header));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if (ret < 12) {
|
||||
ERROR(eon->base.context, "short reply packet (%d)", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Unpack the 12-byte header */
|
||||
hdr->cmd = array_uint16_le(header);
|
||||
hdr->magic = array_uint32_le(header+2);
|
||||
hdr->seq = array_uint16_le(header+6);
|
||||
hdr->len = array_uint32_le(header+8);
|
||||
|
||||
ret -= 12;
|
||||
if (ret > size) {
|
||||
ERROR(eon->base.context, "receive_header result data buffer too small (%d vs %d)", ret, size);
|
||||
return -1;
|
||||
}
|
||||
memcpy(buffer, header+12, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int receive_data(suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (size > 0) {
|
||||
int len;
|
||||
|
||||
len = receive_packet(eon, buffer + ret, size);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
size -= len;
|
||||
ret += len;
|
||||
|
||||
/* Was it not a full packet of data? We're done, regardless of expectations */
|
||||
if (len < PACKET_SIZE-2)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -291,96 +245,144 @@ static int receive_data(suunto_eonsteel_device_t *eon, unsigned char *buffer, in
|
||||
* against the command, and then only returns the actual reply
|
||||
* data itself.
|
||||
*
|
||||
* Also note that "receive_data()" itself will have removed the
|
||||
* per-packet handshake bytes, so unlike "send_cmd()", this does
|
||||
* not see the two initial 0x3f 0x?? bytes, and this the offsets
|
||||
* for the cmd/magic/seq/len are off by two compared to the
|
||||
* send_cmd() side. The offsets are the same in the actual raw
|
||||
* packet.
|
||||
* Also note that receive() function itself will have removed the
|
||||
* per-packet handshake bytes, so unlike the send() function, this
|
||||
* functon does not see the two initial 0x3f 0x?? bytes, and thus the
|
||||
* offsets for the cmd/magic/seq/len are off by two compared to the
|
||||
* send() side. The offsets are the same in the actual raw packet.
|
||||
*/
|
||||
static int send_receive(suunto_eonsteel_device_t *eon,
|
||||
static dc_status_t
|
||||
suunto_eonsteel_transfer(suunto_eonsteel_device_t *device,
|
||||
unsigned short cmd,
|
||||
unsigned int len_out, const unsigned char *out,
|
||||
unsigned int len_in, unsigned char *in)
|
||||
const unsigned char data[], unsigned int size,
|
||||
unsigned char answer[], unsigned int asize,
|
||||
unsigned int *actual)
|
||||
{
|
||||
int len, actual, max;
|
||||
unsigned char buf[2048];
|
||||
struct eon_hdr hdr;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
unsigned char header[PACKET_SIZE];
|
||||
unsigned int len = 0;
|
||||
|
||||
if (send_cmd(eon, cmd, len_out, out) < 0)
|
||||
return -1;
|
||||
// Send the command.
|
||||
rc = suunto_eonsteel_send(device, cmd, data, size);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
/* Get the header and the first part of the data */
|
||||
len = receive_header(eon, &hdr, in, len_in);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
// Receive the header and the first part of the data.
|
||||
rc = suunto_eonsteel_receive(device, header, sizeof(header), &len);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
/* Verify the header data */
|
||||
if (hdr.cmd != cmd) {
|
||||
ERROR(eon->base.context, "command reply doesn't match command");
|
||||
return -1;
|
||||
}
|
||||
if (hdr.magic != eon->magic + 5) {
|
||||
ERROR(eon->base.context, "command reply doesn't match magic (got %08x, expected %08x)", hdr.magic, eon->magic + 5);
|
||||
return -1;
|
||||
}
|
||||
if (hdr.seq != eon->seq) {
|
||||
ERROR(eon->base.context, "command reply doesn't match sequence number");
|
||||
return -1;
|
||||
}
|
||||
actual = hdr.len;
|
||||
if (actual < len) {
|
||||
ERROR(eon->base.context, "command reply length mismatch (got %d, claimed %d)", len, actual);
|
||||
return -1;
|
||||
}
|
||||
if (actual > len_in) {
|
||||
ERROR(eon->base.context, "command reply too big for result buffer - truncating");
|
||||
actual = len_in;
|
||||
// Verify the header length.
|
||||
if (len < HEADER_SIZE) {
|
||||
ERROR(device->base.context, "Invalid packet length (%u).", len);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
/* Get the rest of the data */
|
||||
len += receive_data(eon, in + len, actual - len);
|
||||
if (len != actual) {
|
||||
ERROR(eon->base.context, "command reply returned unexpected amoutn of data (got %d, expected %d)", len, actual);
|
||||
return -1;
|
||||
// Unpack the 12 byte header.
|
||||
unsigned int reply = array_uint16_le(header);
|
||||
unsigned int magic = array_uint32_le(header + 2);
|
||||
unsigned int seq = array_uint16_le(header + 6);
|
||||
unsigned int length = array_uint32_le(header + 8);
|
||||
|
||||
if (cmd != CMD_INIT) {
|
||||
// Verify the command reply.
|
||||
if (reply != cmd) {
|
||||
ERROR(device->base.context, "Unexpected command reply (received %04x, expected %04x).", reply, cmd);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
// Verify the magic value.
|
||||
if (magic != device->magic + 5) {
|
||||
ERROR(device->base.context, "Unexpected magic value (received %08x, expected %08x).", magic, device->magic + 5);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
||||
// Successful command - increment sequence number
|
||||
eon->seq++;
|
||||
return len;
|
||||
// Verify the sequence number.
|
||||
if (seq != device->seq) {
|
||||
ERROR(device->base.context, "Unexpected sequence number (received %04x, expected %04x).", seq, device->seq);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
// Verify the length.
|
||||
if (length > asize) {
|
||||
ERROR(device->base.context, "Insufficient buffer space available.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
// Verify the initial payload length.
|
||||
unsigned int nbytes = len - HEADER_SIZE;
|
||||
if (nbytes > length) {
|
||||
ERROR(device->base.context, "Unexpected number of bytes (received %u, expected %u).", nbytes, length);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
// Copy the payload data.
|
||||
memcpy(answer, header + HEADER_SIZE, nbytes);
|
||||
|
||||
// Receive the remainder of the data.
|
||||
while (nbytes < length) {
|
||||
rc = suunto_eonsteel_receive(device, answer + nbytes, length - nbytes, &len);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
nbytes += len;
|
||||
|
||||
if (len < PACKET_SIZE - 2)
|
||||
break;
|
||||
}
|
||||
|
||||
// Verify the total payload length.
|
||||
if (nbytes != length) {
|
||||
ERROR(device->base.context, "Unexpected number of bytes (received %u, expected %u).", nbytes, length);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
// Remember the magic number.
|
||||
if (cmd == CMD_INIT) {
|
||||
device->magic = (magic & 0xffff0000) | 0x0005;
|
||||
}
|
||||
|
||||
// Increment the sequence number.
|
||||
device->seq++;
|
||||
|
||||
if (actual)
|
||||
*actual = nbytes;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buffer_t *buf)
|
||||
static dc_status_t
|
||||
read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buffer_t *buf)
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
unsigned char result[2560];
|
||||
unsigned char cmdbuf[64];
|
||||
unsigned int size, offset;
|
||||
int rc, len;
|
||||
unsigned int size, offset, len;
|
||||
unsigned int n = 0;
|
||||
|
||||
memset(cmdbuf, 0, sizeof(cmdbuf));
|
||||
len = strlen(filename) + 1;
|
||||
if (len + 4 > sizeof(cmdbuf)) {
|
||||
ERROR(eon->base.context, "too long filename: %s", filename);
|
||||
return -1;
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
memcpy(cmdbuf+4, filename, len);
|
||||
rc = send_receive(eon, CMD_FILE_OPEN,
|
||||
len+4, cmdbuf,
|
||||
sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_FILE_OPEN,
|
||||
cmdbuf, len + 4, result, sizeof(result), &n);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "unable to look up %s", filename);
|
||||
return -1;
|
||||
return rc;
|
||||
}
|
||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "lookup", result, rc);
|
||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "lookup", result, n);
|
||||
|
||||
rc = send_receive(eon, CMD_FILE_STAT,
|
||||
0, NULL,
|
||||
sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_FILE_STAT,
|
||||
NULL, 0, result, sizeof(result), &n);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "unable to stat %s", filename);
|
||||
return -1;
|
||||
return rc;
|
||||
}
|
||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "stat", result, rc);
|
||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "stat", result, n);
|
||||
|
||||
size = array_uint32_le(result+4);
|
||||
offset = 0;
|
||||
@ -393,54 +395,52 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf
|
||||
ask = 1024;
|
||||
put_le32(1234, cmdbuf+0); // Not file offset, after all
|
||||
put_le32(ask, cmdbuf+4); // Size of read
|
||||
rc = send_receive(eon, CMD_FILE_READ,
|
||||
8, cmdbuf,
|
||||
sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_FILE_READ,
|
||||
cmdbuf, 8, result, sizeof(result), &n);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "unable to read %s", filename);
|
||||
return -1;
|
||||
return rc;
|
||||
}
|
||||
if (rc < 8) {
|
||||
if (n < 8) {
|
||||
ERROR(eon->base.context, "got short read reply for %s", filename);
|
||||
return -1;
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
// Not file offset, just stays unmodified.
|
||||
at = array_uint32_le(result);
|
||||
if (at != 1234) {
|
||||
ERROR(eon->base.context, "read of %s returned different offset than asked for (%d vs %d)", filename, at, offset);
|
||||
return -1;
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
// Number of bytes actually read
|
||||
got = array_uint32_le(result+4);
|
||||
if (!got)
|
||||
break;
|
||||
if (rc < 8 + got) {
|
||||
if (n < 8 + got) {
|
||||
ERROR(eon->base.context, "odd read size reply for offset %d of file %s", offset, filename);
|
||||
return -1;
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
if (got > size)
|
||||
got = size;
|
||||
if (!dc_buffer_append (buf, result + 8, got)) {
|
||||
ERROR (eon->base.context, "Insufficient buffer space available.");
|
||||
return -1;
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
offset += got;
|
||||
size -= got;
|
||||
}
|
||||
|
||||
rc = send_receive(eon, CMD_FILE_CLOSE,
|
||||
0, NULL,
|
||||
sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_FILE_CLOSE,
|
||||
NULL, 0, result, sizeof(result), &n);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "cmd CMD_FILE_CLOSE failed");
|
||||
return -1;
|
||||
return rc;
|
||||
}
|
||||
HEXDUMP(eon->base.context, DC_LOGLEVEL_DEBUG, "close", result, rc);
|
||||
HEXDUMP(eon->base.context, DC_LOGLEVEL_DEBUG, "close", result, n);
|
||||
|
||||
return offset;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -448,7 +448,7 @@ static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buf
|
||||
* with the last dirent first. That's intentional: for dives,
|
||||
* we will want to look up the last dive first.
|
||||
*/
|
||||
static struct directory_entry *parse_dirent(suunto_eonsteel_device_t *eon, int nr, const unsigned char *p, int len, struct directory_entry *old)
|
||||
static struct directory_entry *parse_dirent(suunto_eonsteel_device_t *eon, int nr, const unsigned char *p, unsigned int len, struct directory_entry *old)
|
||||
{
|
||||
while (len > 8) {
|
||||
unsigned int type = array_uint32_le(p);
|
||||
@ -475,96 +475,62 @@ static struct directory_entry *parse_dirent(suunto_eonsteel_device_t *eon, int n
|
||||
return old;
|
||||
}
|
||||
|
||||
static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry **res)
|
||||
static dc_status_t
|
||||
get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry **res)
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
struct directory_entry *de = NULL;
|
||||
unsigned char cmd[64];
|
||||
unsigned char result[2048];
|
||||
int rc, cmdlen;
|
||||
unsigned int n = 0;
|
||||
unsigned int cmdlen;
|
||||
|
||||
|
||||
*res = NULL;
|
||||
put_le32(0, cmd);
|
||||
memcpy(cmd + 4, dive_directory, sizeof(dive_directory));
|
||||
cmdlen = 4 + sizeof(dive_directory);
|
||||
rc = send_receive(eon, CMD_DIR_OPEN,
|
||||
cmdlen, cmd,
|
||||
sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_DIR_OPEN,
|
||||
cmd, cmdlen, result, sizeof(result), &n);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "cmd DIR_LOOKUP failed");
|
||||
return -1;
|
||||
return rc;
|
||||
}
|
||||
HEXDUMP(eon->base.context, DC_LOGLEVEL_DEBUG, "DIR_LOOKUP", result, rc);
|
||||
HEXDUMP(eon->base.context, DC_LOGLEVEL_DEBUG, "DIR_LOOKUP", result, n);
|
||||
|
||||
for (;;) {
|
||||
unsigned int nr, last;
|
||||
|
||||
rc = send_receive(eon, CMD_DIR_READDIR,
|
||||
0, NULL,
|
||||
sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_DIR_READDIR,
|
||||
NULL, 0, result, sizeof(result), &n);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "readdir failed");
|
||||
return -1;
|
||||
file_list_free(de);
|
||||
return rc;
|
||||
}
|
||||
if (rc < 8) {
|
||||
if (n < 8) {
|
||||
ERROR(eon->base.context, "short readdir result");
|
||||
return -1;
|
||||
file_list_free(de);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
nr = array_uint32_le(result);
|
||||
last = array_uint32_le(result+4);
|
||||
HEXDUMP(eon->base.context, DC_LOGLEVEL_DEBUG, "dir packet", result, 8);
|
||||
|
||||
de = parse_dirent(eon, nr, result+8, rc-8, de);
|
||||
de = parse_dirent(eon, nr, result+8, n-8, de);
|
||||
if (last)
|
||||
break;
|
||||
}
|
||||
|
||||
rc = send_receive(eon, CMD_DIR_CLOSE,
|
||||
0, NULL,
|
||||
sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_DIR_CLOSE,
|
||||
NULL, 0, result, sizeof(result), NULL);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR(eon->base.context, "dir close failed");
|
||||
file_list_free(de);
|
||||
return rc;
|
||||
}
|
||||
|
||||
*res = de;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
|
||||
{
|
||||
const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00};
|
||||
unsigned char buf[64];
|
||||
struct eon_hdr hdr;
|
||||
|
||||
dc_iostream_set_timeout(eon->iostream, 10);
|
||||
|
||||
/* Get rid of any pending stale input first */
|
||||
for (;;) {
|
||||
size_t transferred = 0;
|
||||
|
||||
dc_status_t rc = dc_iostream_read(eon->iostream, buf, sizeof(buf), &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
break;
|
||||
if (!transferred)
|
||||
break;
|
||||
}
|
||||
|
||||
dc_iostream_set_timeout(eon->iostream, 5000);
|
||||
|
||||
if (send_cmd(eon, CMD_INIT, sizeof(init), init)) {
|
||||
ERROR(eon->base.context, "Failed to send initialization command");
|
||||
return -1;
|
||||
}
|
||||
if (receive_header(eon, &hdr, eon->version, sizeof(eon->version)) < 0) {
|
||||
ERROR(eon->base.context, "Failed to receive initial reply");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Don't ask
|
||||
eon->magic = (hdr.magic & 0xffff0000) | 0x0005;
|
||||
// Increment the sequence number for every command sent
|
||||
eon->seq++;
|
||||
return 0;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
@ -599,9 +565,17 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, unsigned i
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
if (initialize_eonsteel(eon) < 0) {
|
||||
status = dc_iostream_set_timeout(eon->iostream, 5000);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to set the timeout.");
|
||||
goto error_close;
|
||||
}
|
||||
|
||||
const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00};
|
||||
status = suunto_eonsteel_transfer(eon, CMD_INIT,
|
||||
init, sizeof(init), eon->version, sizeof(eon->version), NULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR(context, "unable to initialize device");
|
||||
status = DC_STATUS_IO;
|
||||
goto error_close;
|
||||
}
|
||||
|
||||
@ -635,7 +609,9 @@ suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned ch
|
||||
static dc_status_t
|
||||
suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
|
||||
{
|
||||
int skip = 0, rc;
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
int skip = 0;
|
||||
struct directory_entry *de;
|
||||
suunto_eonsteel_device_t *eon = (suunto_eonsteel_device_t *) abstract;
|
||||
dc_buffer_t *file;
|
||||
@ -651,8 +627,9 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
|
||||
devinfo.serial = array_convert_str2num(eon->version + 0x10, 16);
|
||||
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
|
||||
|
||||
if (get_file_list(eon, &de) < 0)
|
||||
return DC_STATUS_IO;
|
||||
rc = get_file_list(eon, &de);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
if (de == NULL) {
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -705,8 +682,10 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
|
||||
const unsigned char *data = NULL;
|
||||
unsigned int size = 0;
|
||||
|
||||
if (device_is_cancelled(abstract))
|
||||
if (device_is_cancelled(abstract)) {
|
||||
dc_status_set_error(&status, DC_STATUS_CANCELLED);
|
||||
skip = 1;
|
||||
}
|
||||
|
||||
switch (de->type) {
|
||||
case DIRTYPE_DIR:
|
||||
@ -715,30 +694,39 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
|
||||
case DIRTYPE_FILE:
|
||||
if (skip)
|
||||
break;
|
||||
if (sscanf(de->name, "%x.LOG", &time) != 1)
|
||||
|
||||
if (sscanf(de->name, "%x.LOG", &time) != 1) {
|
||||
dc_status_set_error(&status, DC_STATUS_PROTOCOL);
|
||||
break;
|
||||
}
|
||||
|
||||
put_le32(time, buf);
|
||||
|
||||
if (memcmp (buf, eon->fingerprint, sizeof (eon->fingerprint)) == 0) {
|
||||
skip = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
len = snprintf(pathname, sizeof(pathname), "%s/%s", dive_directory, de->name);
|
||||
if (len >= sizeof(pathname))
|
||||
if (len < 0 || (unsigned int) len >= sizeof(pathname)) {
|
||||
dc_status_set_error(&status, DC_STATUS_PROTOCOL);
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset the membuffer, put the 4-byte length at the head.
|
||||
dc_buffer_clear(file);
|
||||
put_le32(time, buf);
|
||||
dc_buffer_append(file, buf, 4);
|
||||
|
||||
// Then read the filename into the rest of the buffer
|
||||
rc = read_file(eon, pathname, file);
|
||||
if (rc < 0)
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
dc_status_set_error(&status, rc);
|
||||
break;
|
||||
}
|
||||
|
||||
data = dc_buffer_get_data(file);
|
||||
size = dc_buffer_get_size(file);
|
||||
|
||||
if (memcmp (data, eon->fingerprint, sizeof (eon->fingerprint)) == 0) {
|
||||
skip = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (callback && !callback(data, size, data, sizeof(eon->fingerprint), userdata))
|
||||
skip = 1;
|
||||
}
|
||||
@ -750,16 +738,16 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
|
||||
}
|
||||
dc_buffer_free(file);
|
||||
|
||||
return device_is_cancelled(abstract) ? DC_STATUS_CANCELLED : DC_STATUS_SUCCESS;
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_status_t suunto_eonsteel_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
{
|
||||
suunto_eonsteel_device_t *eon = (suunto_eonsteel_device_t *) abstract;
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
unsigned char result[64], cmd[8];
|
||||
unsigned int year, month, day;
|
||||
unsigned int hour, min, msec;
|
||||
int rc;
|
||||
|
||||
year = datetime->year;
|
||||
month = datetime->month;
|
||||
@ -777,14 +765,14 @@ static dc_status_t suunto_eonsteel_device_timesync(dc_device_t *abstract, const
|
||||
cmd[6] = msec & 0xFF;
|
||||
cmd[7] = msec >> 8;
|
||||
|
||||
rc = send_receive(eon, CMD_SET_TIME, sizeof(cmd), cmd, sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
return DC_STATUS_IO;
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_SET_TIME, cmd, sizeof(cmd), result, sizeof(result), NULL);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = send_receive(eon, CMD_SET_DATE, sizeof(cmd), cmd, sizeof(result), result);
|
||||
if (rc < 0) {
|
||||
return DC_STATUS_IO;
|
||||
rc = suunto_eonsteel_transfer(eon, CMD_SET_DATE, cmd, sizeof(cmd), result, sizeof(result), NULL);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -64,7 +64,7 @@ enum eon_sample {
|
||||
#define EON_MAX_GROUP 16
|
||||
|
||||
struct type_desc {
|
||||
const char *desc, *format, *mod;
|
||||
char *desc, *format, *mod;
|
||||
unsigned int size;
|
||||
enum eon_sample type[EON_MAX_GROUP];
|
||||
};
|
||||
@ -290,9 +290,9 @@ static void
|
||||
desc_free (struct type_desc desc[], unsigned int count)
|
||||
{
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
free((void *)desc[i].desc);
|
||||
free((void *)desc[i].format);
|
||||
free((void *)desc[i].mod);
|
||||
free(desc[i].desc);
|
||||
free(desc[i].format);
|
||||
free(desc[i].mod);
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,8 +463,8 @@ struct sample_data {
|
||||
dc_sample_callback_t callback;
|
||||
void *userdata;
|
||||
unsigned int time;
|
||||
const char *state_type, *notify_type;
|
||||
const char *warning_type, *alarm_type;
|
||||
char *state_type, *notify_type;
|
||||
char *warning_type, *alarm_type;
|
||||
|
||||
/* We gather up deco and cylinder pressure information */
|
||||
int gasnr;
|
||||
@ -613,7 +613,7 @@ static void sample_gas_switch_event(struct sample_data *info, unsigned short idx
|
||||
*
|
||||
* "enum:0=NoFly Time,1=Depth,2=Surface Time,3=..."
|
||||
*/
|
||||
static const char *lookup_enum(const struct type_desc *desc, unsigned char value)
|
||||
static char *lookup_enum(const struct type_desc *desc, unsigned char value)
|
||||
{
|
||||
const char *str = desc->format;
|
||||
unsigned char c;
|
||||
@ -674,6 +674,7 @@ static const char *lookup_enum(const struct type_desc *desc, unsigned char value
|
||||
*/
|
||||
static void sample_event_state_type(const struct type_desc *desc, struct sample_data *info, unsigned char type)
|
||||
{
|
||||
free(info->state_type);
|
||||
info->state_type = lookup_enum(desc, type);
|
||||
}
|
||||
|
||||
@ -705,6 +706,7 @@ static void sample_event_state_value(const struct type_desc *desc, struct sample
|
||||
|
||||
static void sample_event_notify_type(const struct type_desc *desc, struct sample_data *info, unsigned char type)
|
||||
{
|
||||
free(info->notify_type);
|
||||
info->notify_type = lookup_enum(desc, type);
|
||||
}
|
||||
|
||||
@ -747,6 +749,7 @@ static void sample_event_notify_value(const struct type_desc *desc, struct sampl
|
||||
|
||||
static void sample_event_warning_type(const struct type_desc *desc, struct sample_data *info, unsigned char type)
|
||||
{
|
||||
free(info->warning_type);
|
||||
info->warning_type = lookup_enum(desc, type);
|
||||
}
|
||||
|
||||
@ -785,6 +788,7 @@ static void sample_event_warning_value(const struct type_desc *desc, struct samp
|
||||
|
||||
static void sample_event_alarm_type(const struct type_desc *desc, struct sample_data *info, unsigned char type)
|
||||
{
|
||||
free(info->alarm_type);
|
||||
info->alarm_type = lookup_enum(desc, type);
|
||||
}
|
||||
|
||||
@ -819,7 +823,7 @@ static void sample_event_alarm_value(const struct type_desc *desc, struct sample
|
||||
static void sample_setpoint_type(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||
{
|
||||
dc_sample_value_t sample = {0};
|
||||
const char *type = lookup_enum(desc, value);
|
||||
char *type = lookup_enum(desc, value);
|
||||
|
||||
if (!type) {
|
||||
DEBUG(info->eon->base.context, "sample_setpoint_type(%u) did not match anything in %s", value, desc->format);
|
||||
@ -834,12 +838,12 @@ static void sample_setpoint_type(const struct type_desc *desc, struct sample_dat
|
||||
sample.ppo2 = info->eon->cache.customsetpoint;
|
||||
else {
|
||||
DEBUG(info->eon->base.context, "sample_setpoint_type(%u) unknown type '%s'", value, type);
|
||||
free((void *)type);
|
||||
free(type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->callback) info->callback(DC_SAMPLE_SETPOINT, sample, info->userdata);
|
||||
free((void *)type);
|
||||
free(type);
|
||||
}
|
||||
|
||||
// uint32
|
||||
@ -1013,6 +1017,12 @@ suunto_eonsteel_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback
|
||||
struct sample_data data = { eon, callback, userdata, 0 };
|
||||
|
||||
traverse_data(eon, traverse_samples, &data);
|
||||
|
||||
free(data.state_type);
|
||||
free(data.notify_type);
|
||||
free(data.warning_type);
|
||||
free(data.alarm_type);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -1148,7 +1158,7 @@ static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *d
|
||||
{
|
||||
int idx = eon->cache.ngases;
|
||||
dc_tankvolume_t tankinfo = DC_TANKVOLUME_METRIC;
|
||||
const char *name;
|
||||
char *name;
|
||||
|
||||
if (idx >= MAXGASES)
|
||||
return 0;
|
||||
@ -1170,7 +1180,7 @@ static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *d
|
||||
|
||||
eon->cache.initialized |= 1 << DC_FIELD_GASMIX_COUNT;
|
||||
eon->cache.initialized |= 1 << DC_FIELD_TANK_COUNT;
|
||||
free((void *)name);
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user