diff --git a/examples/oceanic_veo250_test.c b/examples/oceanic_veo250_test.c index 898d00c..9bc7d68 100644 --- a/examples/oceanic_veo250_test.c +++ b/examples/oceanic_veo250_test.c @@ -66,6 +66,14 @@ test_dump_memory (const char* name, const char* filename) fclose (fp); } + message ("device_foreach\n"); + rc = device_foreach (device, NULL, NULL); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read dives."); + device_close (device); + return rc; + } + message ("device_close\n"); rc = device_close (device); if (rc != DEVICE_STATUS_SUCCESS) { diff --git a/src/oceanic_veo250.c b/src/oceanic_veo250.c index 6c89249..c63168b 100644 --- a/src/oceanic_veo250.c +++ b/src/oceanic_veo250.c @@ -45,6 +45,23 @@ #define ACK 0x5A #define NAK 0xA5 +#define CF_POINTERS 0x0040 + +#define RB_LOGBOOK_EMPTY 0x03F0 +#define RB_LOGBOOK_BEGIN 0x0400 +#define RB_LOGBOOK_END 0x0600 +#define RB_LOGBOOK_DISTANCE(a,b) ringbuffer_distance (a, b, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END) + +#define RB_PROFILE_EMPTY 0x05F0 +#define RB_PROFILE_BEGIN 0x0600 +#define RB_PROFILE_END 0x8000 +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, RB_PROFILE_BEGIN, RB_PROFILE_END) + +#define PT_LOGBOOK_FIRST(x) ( (x)[4] + ((x)[5] << 8) ) +#define PT_LOGBOOK_LAST(x) ( (x)[6] + ((x)[7] << 8) ) + +#define PT_PROFILE_FIRST(x) ( (x)[4] + (((x)[5] & 0x0F) << 8) ) +#define PT_PROFILE_LAST(x) ( (x)[6] + (((x)[7] & 0x0F) << 8) ) typedef struct oceanic_veo250_device_t oceanic_veo250_device_t; @@ -57,6 +74,7 @@ struct oceanic_veo250_device_t { static device_status_t oceanic_veo250_device_version (device_t *abstract, unsigned char data[], unsigned int size); static device_status_t oceanic_veo250_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); static device_status_t oceanic_veo250_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result); +static device_status_t oceanic_veo250_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata); static device_status_t oceanic_veo250_device_close (device_t *abstract); static const device_backend_t oceanic_veo250_device_backend = { @@ -66,7 +84,7 @@ static const device_backend_t oceanic_veo250_device_backend = { oceanic_veo250_device_read, /* read */ NULL, /* write */ oceanic_veo250_device_dump, /* dump */ - NULL, /* foreach */ + oceanic_veo250_device_foreach, /* foreach */ oceanic_veo250_device_close /* close */ }; @@ -420,6 +438,33 @@ oceanic_veo250_device_read (device_t *abstract, unsigned int address, unsigned c } +static device_status_t +oceanic_veo250_read_ringbuffer (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size, unsigned int begin, unsigned int end) +{ + assert (address >= begin && address < end); + assert (size <= end - begin); + + if (address + size > end) { + unsigned int a = end - address; + unsigned int b = size - a; + + device_status_t rc = oceanic_veo250_device_read (abstract, address, data, a); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + rc = oceanic_veo250_device_read (abstract, begin, data + a, b); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + } else { + device_status_t rc = oceanic_veo250_device_read (abstract, address, data, size); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + } + + return DEVICE_STATUS_SUCCESS; +} + + static device_status_t oceanic_veo250_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result) { @@ -438,3 +483,87 @@ oceanic_veo250_device_dump (device_t *abstract, unsigned char data[], unsigned i return DEVICE_STATUS_SUCCESS; } + + +static device_status_t +oceanic_veo250_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) +{ + if (! device_is_oceanic_veo250 (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + // Read the pointer data. + unsigned char pointers[OCEANIC_VEO250_PACKET_SIZE] = {0}; + device_status_t rc = oceanic_veo250_device_read (abstract, CF_POINTERS, pointers, OCEANIC_VEO250_PACKET_SIZE); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read pointers."); + return rc; + } + + // Get the logbook pointers. + unsigned int logbook_first = PT_LOGBOOK_FIRST (pointers); + unsigned int logbook_last = PT_LOGBOOK_LAST (pointers); + message ("logbook: first=%04x, last=%04x\n", logbook_first, logbook_last); + + // Calculate the total number of logbook entries. + // In a typical ringbuffer implementation (with only two pointers), + // there is no distinction between an empty and a full ringbuffer. + // However, the VEO250 sets the pointers to a fixed (invalid) value + // to indicate an empty buffer. With this knowledge, we can detect + // the difference between both cases correctly. + if (logbook_first == RB_LOGBOOK_EMPTY && logbook_last == RB_LOGBOOK_EMPTY) + return DEVICE_STATUS_SUCCESS; + + unsigned int logbook_count = RB_LOGBOOK_DISTANCE (logbook_first, logbook_last) / + (OCEANIC_VEO250_PACKET_SIZE / 2); + message ("logbook: count=%u\n", logbook_count); + + // Align the pointers to the packet size. + unsigned int logbook_page_offset = logbook_first % OCEANIC_VEO250_PACKET_SIZE; + unsigned int logbook_page_first = (logbook_first / OCEANIC_VEO250_PACKET_SIZE) * OCEANIC_VEO250_PACKET_SIZE; + unsigned int logbook_page_last = (logbook_last / OCEANIC_VEO250_PACKET_SIZE) * OCEANIC_VEO250_PACKET_SIZE; + unsigned int logbook_page_len = RB_LOGBOOK_DISTANCE (logbook_page_first, logbook_page_last) + OCEANIC_VEO250_PACKET_SIZE; + message ("logbook: first=%04x, last=%04x, len=%u, offset=%u\n", + logbook_page_first, logbook_page_last, logbook_page_len, logbook_page_offset); + + // Read the logbook data. + unsigned char logbooks[RB_LOGBOOK_END - RB_LOGBOOK_BEGIN] = {0}; + rc = oceanic_veo250_read_ringbuffer (abstract, logbook_page_first, logbooks, logbook_page_len, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read dive logbooks."); + return rc; + } + + // Traverse the logbook ringbuffer backwards to retrieve the most recent + // dives first. The logbook ringbuffer is linearized at this point, so + // we do not have to take into account any memory wrapping near the end + // of the memory buffer. + unsigned char *current = logbooks + logbook_page_offset + (logbook_count - 1) * (OCEANIC_VEO250_PACKET_SIZE / 2); + for (unsigned int i = 0; i < logbook_count; ++i) { + message ("logbook: index=%u\n", i); + + // Get the profile pointers. + unsigned int profile_first = PT_PROFILE_FIRST (current) * OCEANIC_VEO250_PACKET_SIZE; + unsigned int profile_last = PT_PROFILE_LAST (current) * OCEANIC_VEO250_PACKET_SIZE; + unsigned int profile_len = RB_PROFILE_DISTANCE (profile_first, profile_last) + OCEANIC_VEO250_PACKET_SIZE; + message ("profile: first=%04x, last=%04x, len=%u\n", profile_first, profile_last, profile_len); + + // Read the profile data. + unsigned char profile[RB_PROFILE_END - RB_PROFILE_BEGIN + 8] = {0}; + rc = oceanic_veo250_read_ringbuffer (abstract, profile_first, profile + 8, profile_len, RB_PROFILE_BEGIN, RB_PROFILE_END); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read dive profiles."); + return rc; + } + + // Copy the logbook data to the profile. + memcpy (profile, current, 8); + + if (callback && !callback (profile, profile_len + 8, userdata)) + return DEVICE_STATUS_SUCCESS; + + // Advance to the next logbook entry. + current -= (OCEANIC_VEO250_PACKET_SIZE / 2); + } + + return DEVICE_STATUS_SUCCESS; +}