--- src/cochran_commander.c | 366 ++++++++++++++++++++++++++++-------------------- 1 file changed, 212 insertions(+), 154 deletions(-)
diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 83dd14a..4c15541 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -41,6 +41,11 @@ typedef enum cochran_endian_t { ENDIAN_BE, } cochran_endian_t;
+typedef enum cochran_profile_size_t { + PROFILE_FULL_SIZE, + PROFILE_READ_SIZE, +} cochran_profile_size_t; + typedef struct cochran_commander_model_t { unsigned char id[2 + 1]; unsigned int model; @@ -53,6 +58,7 @@ typedef struct cochran_data_t {
unsigned short int dive_count; int fp_dive_num; + int invalid_profile_dive_num;
unsigned int logbook_size;
@@ -430,23 +436,133 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t }
-static void +/* + * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF + * search for a reasonable size, e.g. using next dive start sample + * or end-of-samples to limit searching for recoverable samples + */ +static unsigned int +cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num) +{ + const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num; + + if (log_num == data->dive_count) + // Return next usable address from config page + return array_uint32_le(data->config + device->layout->rb_profile_end); + + // Next log's start address + return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin); +} + + +static int +cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, cochran_profile_size_t type) +{ + + const unsigned char *log_entry = data->logbook + dive_num * device->layout->rb_logbook_entry_size; + unsigned int sample_start_address = -1; + unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); + + if (type == PROFILE_FULL_SIZE) { + // actual size, includes pre-dive events + sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_pre); + } else if (type == PROFILE_READ_SIZE) { + // read size, only include dive profile + sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); + } + + // Validate addresses + if (sample_start_address < device->layout->rb_profile_begin || + sample_start_address > device->layout->rb_profile_end || + sample_end_address < device->layout->rb_profile_begin || + (sample_end_address > device->layout->rb_profile_end && + sample_end_address != 0xFFFFFFFF)) { + return 0; + } + + if (sample_end_address == 0xFFFFFFFF) + // Corrupt dive, guess the end address + sample_end_address = cochran_commander_guess_sample_end_address(device, data, dive_num); + + // Calculate the size of the profile only + int sample_size = sample_end_address - sample_start_address; + + if (sample_size < 0) + // Adjust for ring buffer wrap-around + sample_size += device->layout->rb_profile_end - device->layout->rb_profile_begin; + + return sample_size; +} + + +/* + * Do several things. Find the log that matches the fingerprint, + * calculate the total read size for progress indicator, + * Determine the most recent dive without profile data. + */ + +static int cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_data_t *data) { - // Skip to fingerprint to reduce time + // We track profile ringbuffer usage to determine which dives have profile data + int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin; + + int dive_count = -1; + + // Start at end of log if (data->dive_count < device->layout->rb_logbook_entry_count) - data->fp_dive_num = data->dive_count; + dive_count = data->dive_count; else - data->fp_dive_num = device->layout->rb_logbook_entry_count; - data->fp_dive_num--; + dive_count = device->layout->rb_logbook_entry_count; + dive_count--; + + int sample_read_size = 0; + data->invalid_profile_dive_num = -1; + + // Remove the pre-dive events that occur after the last dive + int rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + int last_dive_end_address = array_uint32_le(data->logbook + dive_count * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); + if (rb_head_ptr > last_dive_end_address) + profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; + + // Loop through dives to find FP, Accumulate profile data size, + // and find the last dive with invalid profile + for (int i = dive_count; i > 0; i--) { + unsigned char *log_entry = data->logbook + i * device->layout->rb_logbook_entry_size; + + // We're done if we find the fingerprint + if (!memcmp(device->fingerprint, log_entry, sizeof(device->fingerprint))) { + data->fp_dive_num = i; + break; + }
- while (data->fp_dive_num >= 0 && memcmp(device->fingerprint, - data->logbook + data->fp_dive_num * device->layout->rb_logbook_entry_size, - sizeof(device->fingerprint))) - data->fp_dive_num--; + int sample_size = cochran_commander_profile_size(device, data, i, PROFILE_FULL_SIZE); + int read_size = cochran_commander_profile_size(device, data, i, PROFILE_READ_SIZE); + + // Determine if sample exists + if (profile_capacity_remaining > 0) { + // Subtract this dive's profile size including post-dive events + profile_capacity_remaining -= sample_size; + if (profile_capacity_remaining < 0) { + // Save the last dive that is missing profile data + data->invalid_profile_dive_num = i; + } + // Accumulate read size for progress bar + sample_read_size += read_size; + } + + if (profile_capacity_remaining < 0) { + // There is no profile for this dive + sample_size = 0; + } + + } + + return sample_read_size; }
+ static void cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_data_t *data) { @@ -509,103 +625,6 @@ cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_d }
-/* - * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF - * search for a reasonable size, e.g. using next dive start sample - * or end-of-samples to limit searching for recoverable samples - */ -static unsigned int -cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num) -{ - const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num; - - if (log_num == data->dive_count) - // Return next usable address from config page - return array_uint32_le(data->config + device->layout->rb_profile_end); - - // Next log's start address - return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin); -} - - -static dc_status_t -cochran_commander_read_all (cochran_commander_device_t *device, cochran_data_t *data) -{ - dc_device_t *abstract = (dc_device_t *) device; - dc_status_t rc = DC_STATUS_SUCCESS; - - // Calculate max data sizes - unsigned int max_config = sizeof(data->config); - unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin; - unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin; - - dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - progress.maximum = max_config + max_logbook + max_sample; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - // Emit ID block - dc_event_vendor_t vendor; - vendor.data = device->id; - vendor.size = sizeof (device->id); - device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - - // Read config - rc = cochran_commander_read_config(device, &progress, data->config, sizeof(data->config)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Determine size of dive list to read. - if (device->layout->endian == ENDIAN_LE) - data->dive_count = array_uint16_le (data->config + device->layout->cf_dive_count); - else - data->dive_count = array_uint16_be (data->config + device->layout->cf_dive_count); - - if (data->dive_count > device->layout->rb_logbook_entry_count) { - data->logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; - } else { - data->logbook_size = data->dive_count * device->layout->rb_logbook_entry_size; - } - - progress.maximum -= max_logbook - data->logbook_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - // Allocate space for log book. - data->logbook = (unsigned char *) malloc(data->logbook_size); - if (data->logbook == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Request log book - rc = cochran_commander_read(device, &progress, 0, data->logbook, data->logbook_size); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Determine sample memory to read - cochran_commander_find_fingerprint(device, data); - cochran_commander_get_sample_parms(device, data); - - progress.maximum -= max_sample - data->sample_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - if (data->sample_size > 0) { - data->sample = (unsigned char *) malloc(data->sample_size); - if (data->sample == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Read the sample data - rc = cochran_commander_read (device, &progress, data->sample_data_offset, data->sample, data->sample_size); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the sample data."); - return rc; - } - } - - return DC_STATUS_SUCCESS; -} - dc_status_t cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name) { @@ -772,9 +791,64 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call cochran_data_t data; data.logbook = NULL; data.sample = NULL; - status = cochran_commander_read_all (device, &data); - if (status != DC_STATUS_SUCCESS) - goto error; + + // Calculate max data sizes + unsigned int max_config = sizeof(data.config); + unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin; + unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin; + + // setup progress indication + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = max_config + max_logbook + max_sample; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Emit ID block + dc_event_vendor_t vendor; + vendor.data = device->id; + vendor.size = sizeof (device->id); + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + + // Read config + dc_status_t rc = DC_STATUS_SUCCESS; + rc = cochran_commander_read_config(device, &progress, data.config, sizeof(data.config)); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Determine size of dive list to read. + if (device->layout->endian == ENDIAN_LE) + data.dive_count = array_uint16_le (data.config + device->layout->cf_dive_count); + else + data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count); + + if (data.dive_count > device->layout->rb_logbook_entry_count) { + data.logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; + } else { + data.logbook_size = data.dive_count * device->layout->rb_logbook_entry_size; + } + + // Update progress indicator with new maximum + progress.maximum -= max_logbook - data.logbook_size; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Allocate space for log book. + data.logbook = (unsigned char *) malloc(data.logbook_size); + if (data.logbook == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Request log book + rc = cochran_commander_read(device, &progress, 0, data.logbook, data.logbook_size); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Locate fingerprint, recent dive with invalid profile and calc read size + int profile_read_size = cochran_commander_find_fingerprint(device, &data); + // Update progress indicator with new maximum + progress.maximum -= (max_sample - profile_read_size); + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + cochran_commander_get_sample_parms(device, &data);
// Emit a device info event. dc_event_devinfo_t devinfo; @@ -792,9 +866,6 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call goto error; }
- // We track profile ringbuffer usage to determine which dives have profile data - int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin; - unsigned int dive_count = 0; if (data.dive_count < device->layout->rb_logbook_entry_count) dive_count = data.dive_count; @@ -808,44 +879,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end);
- // Validate - if (sample_start_address < device->layout->rb_profile_begin || - sample_start_address > device->layout->rb_profile_end || - sample_end_address < device->layout->rb_profile_begin || - (sample_end_address > device->layout->rb_profile_end && - sample_end_address != 0xFFFFFFFF)) { - continue; - } - - if (sample_end_address == 0xFFFFFFFF) - // Corrupt dive, guess the end address - sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); - - // Determine if sample exists - if (profile_capacity_remaining > 0) { - // Subtract this dive's profile size including post-dive events - profile_capacity_remaining -= (last_start_address - sample_start_address); - // Adjust for a dive that wraps the buffer - if (sample_start_address > last_start_address) - profile_capacity_remaining -= device->layout->rb_profile_end - device->layout->rb_profile_begin; - } - last_start_address = sample_start_address; - - unsigned char *sample = NULL; int sample_size = 0; - if (profile_capacity_remaining < 0) { - // There is no profile for this dive - sample = NULL; - sample_size = 0; - } else { - // Calculate the size of the profile only - sample = data.sample + sample_start_address - data.sample_data_offset; - sample_size = sample_end_address - sample_start_address; - - if (sample_size < 0) - // Adjust for ring buffer wrap-around - sample_size += device->layout->rb_profile_end - device->layout->rb_profile_begin; - } + + // Determine if profile exists + if (i > data.invalid_profile_dive_num) + sample_size = cochran_commander_profile_size(device, &data, i, PROFILE_READ_SIZE);
// Build dive blob unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size; @@ -857,17 +895,37 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
memcpy(dive, log_entry, device->layout->rb_logbook_entry_size); // log
- // Copy profile data + int tries = 0; + // Read profile data if (sample_size) { if (sample_start_address <= sample_end_address) { - memcpy(dive + device->layout->rb_logbook_entry_size, sample, sample_size); + do { + rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); + } while (rc != DC_STATUS_SUCCESS && tries++ < 3); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + return rc; + } } else { // It wrapped the buffer, copy two sections unsigned int size = device->layout->rb_profile_end - sample_start_address;
- memcpy(dive + device->layout->rb_logbook_entry_size, sample, size); - memcpy(dive + device->layout->rb_logbook_entry_size + size, - data.sample, sample_end_address - device->layout->rb_profile_begin); + tries = 0; + do { + rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); + } while (rc != DC_STATUS_SUCCESS && tries++ < 3); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + return rc; + } + tries = 0; + do { + rc = cochran_commander_read (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); + } while (rc != DC_STATUS_SUCCESS && tries++ < 3); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + return rc; + } } }