device_status_t suunto_common2_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) { suunto_common2_device_t *device = (suunto_common2_device_t*) abstract; // Enable progress notifications. device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; progress.maximum = RB_PROFILE_END - RB_PROFILE_BEGIN + 8 + SZ_VERSION + (SZ_MINIMUM > 4 ? SZ_MINIMUM : 4); device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // Read the version info. unsigned char version[SZ_VERSION] = {0}; device_status_t rc = suunto_common2_device_version (abstract, version, sizeof (version)); if (rc != DEVICE_STATUS_SUCCESS) { WARNING ("Cannot read memory header."); return rc; } // Update and emit a progress event. progress.current += sizeof (version); device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // Read the serial number. unsigned char serial[SZ_MINIMUM > 4 ? SZ_MINIMUM : 4] = {0}; rc = suunto_common2_device_read (abstract, 0x0023, serial, sizeof (serial)); if (rc != DEVICE_STATUS_SUCCESS) { WARNING ("Cannot read memory header."); return rc; } // Update and emit a progress event. progress.current += sizeof (serial); device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // Emit a device info event. device_devinfo_t devinfo; devinfo.model = version[0]; devinfo.firmware = array_uint24_be (version + 1); devinfo.serial = array_uint32_be (serial); device_event_emit (abstract, DEVICE_EVENT_DEVINFO, &devinfo); // Read the header bytes. unsigned char header[8] = {0}; rc = suunto_common2_device_read (abstract, 0x0190, header, sizeof (header)); if (rc != DEVICE_STATUS_SUCCESS) { WARNING ("Cannot read memory header."); return rc; } // Obtain the pointers from the header. unsigned int last = array_uint16_le (header + 0); unsigned int count = array_uint16_le (header + 2); unsigned int end = array_uint16_le (header + 4); unsigned int begin = array_uint16_le (header + 6); // Memory buffer to store all the dives. unsigned char data[SZ_MINIMUM + RB_PROFILE_END - RB_PROFILE_BEGIN] = {0}; // Calculate the total amount of bytes. unsigned int remaining = RB_PROFILE_DISTANCE (begin, end, count != 0); // Update and emit a progress event. progress.maximum -= (RB_PROFILE_END - RB_PROFILE_BEGIN) - remaining; progress.current += sizeof (header); device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // To reduce the number of read operations, we always try to read // packages with the largest possible size. As a consequence, the // last package of a dive can contain data from more than one dive. // Therefore, the remaining data of this package (and its size) // needs to be preserved for the next dive. unsigned int available = 0; // The ring buffer is traversed backwards to retrieve the most recent // dives first. This allows us to download only the new dives. unsigned int current = last; unsigned int previous = end; unsigned int address = previous; unsigned int offset = remaining + SZ_MINIMUM; while (remaining) { // Calculate the size of the current dive. unsigned int size = RB_PROFILE_DISTANCE (current, previous, 1); if (size < 4 || size > remaining) { WARNING ("Unexpected profile size."); return DEVICE_STATUS_ERROR; } unsigned int nbytes = available; while (nbytes < size) { // Handle the ringbuffer wrap point. if (address == RB_PROFILE_BEGIN) address = RB_PROFILE_END; // Calculate the package size. Try with the largest possible // size first, and adjust when the end of the ringbuffer or // the end of the profile data is reached. unsigned int len = SZ_PACKET; if (RB_PROFILE_BEGIN + len > address) len = address - RB_PROFILE_BEGIN; // End of ringbuffer. if (nbytes + len > remaining) len = remaining - nbytes; // End of profile. /*if (nbytes + len > size) len = size - nbytes;*/ // End of dive (for testing only). // Move to the begin of the current package. offset -= len; address -= len; // Always read at least the minimum amount of bytes, because // reading fewer bytes is unreliable. The memory buffer is // large enough to prevent buffer overflows, and the extra // bytes are automatically ignored (due to reading backwards). unsigned int extra = 0; if (len < SZ_MINIMUM) extra = SZ_MINIMUM - len; // Read the package. rc = suunto_common2_device_read (abstract, address - extra, data + offset - extra, len + extra); if (rc != DEVICE_STATUS_SUCCESS) { WARNING ("Cannot read memory."); return rc; } // Update and emit a progress event. progress.current += len; device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); // Next package. nbytes += len; } // The last package of the current dive contains the previous and // next pointers (in a continuous memory area). It can also contain // a number of bytes from the next dive. remaining -= size; available = nbytes - size; unsigned char *p = data + offset + available; unsigned int prev = array_uint16_le (p + 0); unsigned int next = array_uint16_le (p + 2); if (next != previous) { WARNING ("Profiles are not continuous."); return DEVICE_STATUS_ERROR; } // Next dive. previous = current; current = prev; unsigned int fp_offset = FP_OFFSET; if (devinfo.model == 0x15) fp_offset += 6; // HelO2 if (memcmp (p + fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) return DEVICE_STATUS_SUCCESS; if (callback && !callback (p + 4, size - 4, p + fp_offset, sizeof (device->fingerprint), userdata)) return DEVICE_STATUS_SUCCESS; } return DEVICE_STATUS_SUCCESS; }
static dc_status_t hw_frog_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { hw_frog_device_t *device = (hw_frog_device_t *) abstract; // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + (RB_PROFILE_END - RB_PROFILE_BEGIN); device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Download the version data. unsigned char id[SZ_VERSION] = {0}; dc_status_t rc = hw_frog_device_version (abstract, id, sizeof (id)); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the version."); return rc; } // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = 0; devinfo.firmware = array_uint16_be (id + 2); devinfo.serial = array_uint16_le (id + 0); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate memory. unsigned char *header = (unsigned char *) malloc (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); if (header == NULL) { ERROR (abstract->context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; } // Download the logbook headers. rc = hw_frog_transfer (device, &progress, HEADER, NULL, 0, header, RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the header."); free (header); return rc; } // Locate the most recent dive. // The device maintains an internal counter which is incremented for every // dive, and the current value at the time of the dive is stored in the // dive header. Thus the most recent dive will have the highest value. unsigned int count = 0; unsigned int latest = 0; unsigned int maximum = 0; for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { unsigned int offset = i * RB_LOGBOOK_SIZE; // Ignore uninitialized header entries. if (array_isequal (header + offset, RB_LOGBOOK_SIZE, 0xFF)) break; // Get the internal dive number. unsigned int current = array_uint16_le (header + offset + 52); if (current > maximum) { maximum = current; latest = i; } count++; } // Calculate the total and maximum size. unsigned int ndives = 0; unsigned int size = 0; unsigned int maxsize = 0; for (unsigned int i = 0; i < count; ++i) { unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; unsigned int offset = idx * RB_LOGBOOK_SIZE; // Get the ringbuffer pointers. unsigned int begin = array_uint24_le (header + offset + 2); unsigned int end = array_uint24_le (header + offset + 5); if (begin < RB_PROFILE_BEGIN || begin >= RB_PROFILE_END || end < RB_PROFILE_BEGIN || end >= RB_PROFILE_END) { ERROR (abstract->context, "Invalid ringbuffer pointer detected."); free (header); return DC_STATUS_DATAFORMAT; } // Calculate the profile length. unsigned int length = RB_LOGBOOK_SIZE + RB_PROFILE_DISTANCE (begin, end) - 6; // Check the fingerprint data. if (memcmp (header + offset + 9, device->fingerprint, sizeof (device->fingerprint)) == 0) break; if (length > maxsize) maxsize = length; size += length; ndives++; } // Update and emit a progress event. progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + size; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Finish immediately if there are no dives available. if (ndives == 0) { free (header); return DC_STATUS_SUCCESS; } // Allocate enough memory for the largest dive. unsigned char *profile = (unsigned char *) malloc (maxsize); if (profile == NULL) { ERROR (abstract->context, "Failed to allocate memory."); free (header); return DC_STATUS_NOMEMORY; } // Download the dives. for (unsigned int i = 0; i < ndives; ++i) { unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; unsigned int offset = idx * RB_LOGBOOK_SIZE; // Get the ringbuffer pointers. unsigned int begin = array_uint24_le (header + offset + 2); unsigned int end = array_uint24_le (header + offset + 5); // Calculate the profile length. unsigned int length = RB_LOGBOOK_SIZE + RB_PROFILE_DISTANCE (begin, end) - 6; // Download the dive. unsigned char number[1] = {idx}; rc = hw_frog_transfer (device, &progress, DIVE, number, sizeof (number), profile, length); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the dive."); free (profile); free (header); return rc; } // Verify the header in the logbook and profile are identical. if (memcmp (profile, header + offset, RB_LOGBOOK_SIZE) != 0) { ERROR (abstract->context, "Unexpected profile header."); free (profile); free (header); return rc; } if (callback && !callback (profile, length, profile + 9, sizeof (device->fingerprint), userdata)) break; } free (profile); free (header); return DC_STATUS_SUCCESS; }
dc_status_t suunto_common_extract_dives (suunto_common_device_t *device, const suunto_common_layout_t *layout, const unsigned char data[], dc_dive_callback_t callback, void *userdata) { assert (layout != NULL); unsigned int eop; if (layout->eop) { // Get the end-of-profile pointer directly from the header. eop = array_uint16_be (data + layout->eop); } else { // Get the end-of-profile pointer by searching for the // end-of-profile marker in the profile ringbuffer. eop = layout->rb_profile_begin; while (eop < layout->rb_profile_end) { if (data[eop] == 0x82) break; eop++; } } // Validate the end-of-profile pointer. if (eop < layout->rb_profile_begin || eop >= layout->rb_profile_end || data[eop] != 0x82) { return DC_STATUS_DATAFORMAT; } // Memory buffer for the profile ringbuffer. unsigned int length = layout->rb_profile_end - layout->rb_profile_begin; unsigned char *buffer = (unsigned char *) malloc (length); if (buffer == NULL) return DC_STATUS_NOMEMORY; unsigned int current = eop; unsigned int previous = eop; for (unsigned int i = 0; i < length; ++i) { // Move backwards through the ringbuffer. if (current == layout->rb_profile_begin) current = layout->rb_profile_end; current--; // Check for an end of profile marker. if (data[current] == 0x82) break; // Check for an end of dive marker (of the next dive), // to find the start of the current dive. unsigned int idx = RB_PROFILE_PEEK (current, layout); if (data[idx] == 0x80) { unsigned int len = RB_PROFILE_DISTANCE (current, previous, layout); if (current + len > layout->rb_profile_end) { unsigned int a = layout->rb_profile_end - current; unsigned int b = (current + len) - layout->rb_profile_end; memcpy (buffer + 0, data + current, a); memcpy (buffer + a, data + layout->rb_profile_begin, b); } else { memcpy (buffer, data + current, len); } if (device && memcmp (buffer + layout->fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) { free (buffer); return DC_STATUS_SUCCESS; } if (callback && !callback (buffer, len, buffer + layout->fp_offset, sizeof (device->fingerprint), userdata)) { free (buffer); return DC_STATUS_SUCCESS; } previous = current; } } free (buffer); if (data[current] != 0x82) return DC_STATUS_DATAFORMAT; return DC_STATUS_SUCCESS; }
dc_status_t cressi_leonardo_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) { cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) abstract; dc_context_t *context = (abstract ? abstract->context : NULL); if (abstract && !ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS; if (size < SZ_MEMORY) return DC_STATUS_DATAFORMAT; // Locate the most recent dive. // The device maintains an internal counter which is incremented for every // dive, and the current value at the time of the dive is stored in the // dive header. Thus the most recent dive will have the highest value. unsigned int count = 0; unsigned int latest = 0; unsigned int maximum = 0; for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { unsigned int offset = RB_LOGBOOK_BEGIN + i * RB_LOGBOOK_SIZE; // Ignore uninitialized header entries. if (array_isequal (data + offset, RB_LOGBOOK_SIZE, 0xFF)) break; // Get the internal dive number. unsigned int current = array_uint16_le (data + offset); if (current > maximum) { maximum = current; latest = i; } count++; } unsigned char *buffer = (unsigned char *) malloc (RB_LOGBOOK_SIZE + RB_PROFILE_END - RB_PROFILE_BEGIN); if (buffer == NULL) { ERROR (context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; } for (unsigned int i = 0; i < count; ++i) { unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; unsigned int offset = RB_LOGBOOK_BEGIN + idx * RB_LOGBOOK_SIZE; // Get the ringbuffer pointers. unsigned int header = array_uint16_le (data + offset + 2); unsigned int footer = array_uint16_le (data + offset + 4); if (header < RB_PROFILE_BEGIN || header + 2 > RB_PROFILE_END || footer < RB_PROFILE_BEGIN || footer + 2 > RB_PROFILE_END) { ERROR (abstract->context, "Invalid ringbuffer pointer detected."); free (buffer); return DC_STATUS_DATAFORMAT; } // Get the same pointers from the profile. unsigned int header2 = array_uint16_le (data + footer); unsigned int footer2 = array_uint16_le (data + header); if (header2 != header || footer2 != footer) { ERROR (abstract->context, "Invalid ringbuffer pointer detected."); free (buffer); return DC_STATUS_DATAFORMAT; } // Calculate the profile address and length. unsigned int address = header + 2; unsigned int length = RB_PROFILE_DISTANCE (header, footer) - 2; // Check the fingerprint data. if (device && memcmp (data + offset + 8, device->fingerprint, sizeof (device->fingerprint)) == 0) break; // Copy the logbook entry. memcpy (buffer, data + offset, RB_LOGBOOK_SIZE); // Copy the profile data. if (address + length > RB_PROFILE_END) { unsigned int len_a = RB_PROFILE_END - address; unsigned int len_b = length - len_a; memcpy (buffer + RB_LOGBOOK_SIZE, data + address, len_a); memcpy (buffer + RB_LOGBOOK_SIZE + len_a, data + RB_PROFILE_BEGIN, len_b); } else { memcpy (buffer + RB_LOGBOOK_SIZE, data + address, length); } if (callback && !callback (buffer, RB_LOGBOOK_SIZE + length, buffer + 8, sizeof (device->fingerprint), userdata)) { break; } } free (buffer); return DC_STATUS_SUCCESS; }