- More localization of variables - Variable name changes to be more consistent with other backends - Removed special PTY serial_setup case - Avoided potential double-free situations - Consolidated serial queue flushing - Verified heartbeat bytes - Used goto-style error handling on _open - Bounds checking on pointers - Removed rounding reads to nearest 8k. - Issue DEVINFO event in foreach - Separated dive data from device_t - switched to date-based fingerprint - Many other small bugs and style fixes. --- src/cochran_commander.c | 553 +++++++++++++++++++---------------------- src/cochran_commander.h | 52 ++-- src/cochran_commander_parser.c | 14 +- 3 files changed, 283 insertions(+), 336 deletions(-)
diff --git a/src/cochran_commander.c b/src/cochran_commander.c index c457b3a..127518d 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -49,28 +49,29 @@ */
// Cochran Commander Nitrox -cochran_device_info_t cochran_cmdr_device_info = { +const cochran_device_info_t cochran_cmdr_device_info = { COCHRAN_MODEL_COMMANDER_AIR_NITROX, // model - ADDRESS_24_BIT, // address_size + 24, // address_bits ENDIAN_BE, // endian 115200, // high_baud 0x00000000, // rb_logbook_begin 0x00020000, // rb_lobook_end 0x00020000, // rb_profile_begin 0x00100000, // rb_profile_end - 256, // log_size - 512, // max_log + 256, // logbook_entry_size + 512, // logbook_entry_count 2, // max_gas SAMPLE_CMDR, // sample_format };
-cochran_conf_offsets_t cochran_cmdr_conf_offsets = { +const cochran_conf_offsets_t cochran_cmdr_conf_offsets = { 0x046, // dive_count 0x06e, // last_log 0x071, // last_interdive + 0x0aa, // serial_number };
-cochran_log_offsets_t cochran_cmdr_log_offsets = { +const cochran_log_offsets_t cochran_cmdr_log_offsets = { 1, 0, 3, 2, 5, 4, // sec, min, hour, day, mon, year, 1 byte each 6, // profile_begin_offset, 4 bytes 24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea) @@ -91,7 +92,7 @@ cochran_log_offsets_t cochran_cmdr_log_offsets = { };
// inter-dive event lengths -struct event_size cmdr_event_bytes[15] = { +const struct event_size cmdr_event_bytes[15] = { {0x00, 17}, {0x01, 21}, {0x02, 18}, {0x03, 17}, {0x06, 19}, {0x07, 19}, {0x08, 19}, {0x09, 19}, {0x0a, 19}, @@ -100,63 +101,64 @@ struct event_size cmdr_event_bytes[15] = { { -1, 1} };
// Cochran EMC-14 -cochran_device_info_t cochran_emc14_device_info = { +const cochran_device_info_t cochran_emc14_device_info = { COCHRAN_MODEL_EMC_14, // model - ADDRESS_32_BIT, // address_size + 32, // address_bits ENDIAN_LE, // endian 806400, // high_baud 0x00000000, // rb_logbook_begin 0x00020000, // rb_lobook_end 0x00022000, // rb_profile_begin 0x00200000, // rb_profile_end - 512, // log_size - 256, // max_log + 512, // logbook_entry_size + 256, // logbook_entry_count 2, // max_gas SAMPLE_EMC, // sample_format };
// Cochran EMC-16 -cochran_device_info_t cochran_emc16_device_info = { +const cochran_device_info_t cochran_emc16_device_info = { COCHRAN_MODEL_EMC_16, // model - ADDRESS_32_BIT, // address_size + 32, // address_bits ENDIAN_LE, // endian 806400, // high_baud 0x00000000, // rb_logbook_begin 0x00080000, // rb_lobook_end 0x00094000, // rb_profile_begin 0x00800000, // rb_profile_end - 512, // log_size - 1024, // max_log + 512, // logbook_entry_size + 1024, // logbook_entry_count 2, // max_gas SAMPLE_EMC, // sample_format };
// Cochran EMC-20 -cochran_device_info_t cochran_emc20_device_info = { +const cochran_device_info_t cochran_emc20_device_info = { COCHRAN_MODEL_EMC_20, // model - ADDRESS_32_BIT, // address_size + 32, // address_bits ENDIAN_LE, // endian 806400, // high_baud 0x00000000, // rb_logbook_begin 0x00080000, // rb_lobook_end 0x00094000, // rb_profile_begin 0x01000000, // rb_profile_end - 512, // log_size - 1024, // max_log + 512, // logbook_entry_size + 1024, // logbook_entry_count 2, // max_gas SAMPLE_EMC, // sample_format };
// Common EMC offsets -cochran_conf_offsets_t cochran_emc_conf_offsets = { +const cochran_conf_offsets_t cochran_emc_conf_offsets = { 0x0d2, // dive_count 0x13e, // last_log 0x142, // last_interdive + 0x1e6, // serial_number };
-cochran_log_offsets_t cochran_emc_log_offsets = { +const cochran_log_offsets_t cochran_emc_log_offsets = { 0, 1, 2, 3, 4, 5, // sec, min, hour, day, mon, year, 1 byte each 6, // profile_begin_offset, 4 bytes 24, // water_conductivity, 1 byte 0=low(fresh), 2=high(sea) @@ -176,7 +178,7 @@ cochran_log_offsets_t cochran_emc_log_offsets = { 407, // max_temp, 1 byte, /2+20=F };
-struct event_size emc_event_bytes[15] = { +const struct event_size emc_event_bytes[15] = { {0x00, 19}, {0x01, 23}, {0x02, 20}, {0x03, 19}, {0x06, 21}, {0x07, 21}, {0x0a, 21}, {0x0b, 21}, {0x0f, 19}, @@ -196,12 +198,12 @@ static const dc_device_vtable_t cochran_commander_device_vtable = {
static dc_status_t -cochran_read_id(dc_device_t *device); +cochran_read_id(dc_device_t *device, unsigned char *id, int size); static dc_status_t cochran_commander_read (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int address, unsigned char data[], unsigned int size); static void -cochran_check_profiles(dc_device_t *abstract); +cochran_check_profiles(dc_device_t *abstract, cochran_data_t *data);
static dc_status_t @@ -210,17 +212,18 @@ cochran_packet (cochran_device_t *device, dc_event_progress_t *progress, unsigned char answer[], unsigned int asize, int high_speed) { dc_device_t *abstract = (dc_device_t *) device; - unsigned int bytes_read = 0, n, read_size; - unsigned int ptr; - int rc;
if (device_is_cancelled (abstract)) return DC_STATUS_CANCELLED;
// Send the command to the device, one byte at a time - for (ptr = 0; ptr < csize; ptr++) { - if (ptr) serial_sleep(device->port, 16); // 16 ms - n = serial_write(device->port, command + ptr, 1); + // If sent all at once the command is ignored. It's like the DC + // has no buffering. + for (int i = 0; i < csize; i++) { + // Give the DC time to read the character. + if (i) serial_sleep(device->port, 16); // 16 ms + + unsigned int n = serial_write(device->port, command + i, 1); if (n != 1) { ERROR (abstract->context, "Failed to send the command."); return EXITCODE (n); @@ -228,38 +231,40 @@ cochran_packet (cochran_device_t *device, dc_event_progress_t *progress, }
if (high_speed) { + int rc = 0; + + // Give the DC time to process the command. + // This is required. serial_sleep(device->port, 45);
// Rates are odd, like 806400 for the EMC, 115200 for commander rc = serial_configure(device->port, device->info->high_baud, 8, SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE); if (rc == -1) { - // Assume we are talking to a pty, a simulator - rc = serial_configure(device->port, 115200, 8, SERIAL_PARITY_NONE, - 2, SERIAL_FLOWCONTROL_NONE); - if (rc == -1) { - ERROR (abstract->context, "Failed to set the high baud rate."); - return DC_STATUS_IO; - } + ERROR (abstract->context, "Failed to set the high baud rate."); + return DC_STATUS_IO; } }
// Receive the answer from the device. // Use 1024 byte "packets" so we can display progress. - while (bytes_read < asize) { - if (asize - bytes_read > 1024) - read_size = 1024; + unsigned int nbytes = 0; + while (nbytes < asize) { + unsigned int len = 0; + + if (asize - nbytes > 1024) + len = 1024; else - read_size = asize - bytes_read; + len = asize - nbytes;
- n = serial_read (device->port, answer + bytes_read, read_size); - if (n != read_size) { + int n = serial_read (device->port, answer + nbytes, len); + if (n != len) { ERROR (abstract->context, "Failed to receive data, expected %u," - "read %u.", read_size, n); + "read %u.",len, n); return EXITCODE (n); }
- bytes_read += n; + nbytes += n;
if (progress) { progress->current += n; @@ -281,51 +286,39 @@ cochran_commander_serial_setup (cochran_device_t *device) 2, SERIAL_FLOWCONTROL_NONE); if (rc == -1) { ERROR (device->base.context, "Failed to set the terminal attributes."); - serial_close (device->port); return DC_STATUS_IO; }
- serial_set_queue_size(device->port, 4096, 4096); - - // Make sure everything is in a sane state. - serial_flush (device->port, SERIAL_QUEUE_OUTPUT); - serial_flush (device->port, SERIAL_QUEUE_INPUT); - - serial_set_break(device->port, 1); - serial_sleep(device->port, 16); - - serial_set_break(device->port, 0); - // Set the timeout for receiving data (5000 ms). if (serial_set_timeout (device->port, 5000) == -1) { ERROR (device->base.context, "Failed to set the timeout."); - serial_close (device->port); return DC_STATUS_IO; }
+ // Wake up DC and trigger heartbeat + serial_set_break(device->port, 1); + serial_sleep(device->port, 16); + serial_set_break(device->port, 0); + + // Clear old heartbeats + serial_flush (device->port, SERIAL_QUEUE_BOTH); + // Wait for heartbeat byte before send - char answer[1]; - int n = serial_read(device->port, answer, 1); + unsigned char answer = 0; + int n = serial_read(device->port, &answer, 1); if (n != 1) { + serial_close (device->port); ERROR (device->base.context, "Failed to receive device heartbeat."); return EXITCODE (n); }
- return DC_STATUS_SUCCESS; -} - - -dc_status_t -cochran_commander_serial_open(cochran_device_t *device) -{ - // Open the device. - int rc = serial_open (&device->port, device->base.context, device->name); - if (rc == -1) { - ERROR (device->base.context, "Failed to open the serial port."); - return DC_STATUS_IO; + if (answer != 0xAA) { + serial_close (device->port); + ERROR (device->base.context, "Received bad hearbeat byte (%02x).", answer); + return DC_STATUS_PROTOCOL; }
- return cochran_commander_serial_setup(device); + return DC_STATUS_SUCCESS; }
@@ -334,7 +327,7 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name) { cochran_device_t *device = NULL; - dc_status_t rc; + dc_status_t status;
if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -348,29 +341,31 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context,
// Set the default values. device->port = NULL; - device->name = name; - device->data.logbook = NULL; - device->data.sample = NULL; cochran_commander_device_set_fingerprint((dc_device_t *) device, NULL, 0);
- rc = cochran_commander_serial_open(device); - if (rc != DC_STATUS_SUCCESS) { - dc_device_deallocate((dc_device_t *) device); - return rc; + // Open the device. + int rc = serial_open (&device->port, device->base.context, name); + if (rc == -1) { + ERROR (device->base.context, "Failed to open the serial port."); + status = DC_STATUS_IO; + goto error_free; }
+ rc = cochran_commander_serial_setup(device); + if (rc != DC_STATUS_SUCCESS) + return rc; + // Read ID from the device - rc = cochran_read_id((dc_device_t *) device); + unsigned char id[67]; + status = cochran_read_id((dc_device_t *) device, id, sizeof(id));
- if (rc != DC_STATUS_SUCCESS) { + if (status != DC_STATUS_SUCCESS) { ERROR (context, "Device not responding."); - serial_close (device->port); - dc_device_deallocate((dc_device_t *) device); - return rc; + goto error_close; }
- memcpy(device->model_string, device->data.id + 0x3b, 8); + memcpy(device->model_string, id + 0x3b, 8); device->model_string[8] = 0;
device->base.devinfo.model = cochran_commander_model_id(device->model_string); @@ -383,9 +378,8 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, device->model_string[2], device->model_string[3], device->model_string[4], device->model_string[5], device->model_string[6], device->model_string[7]); - serial_close (device->port); - dc_device_deallocate((dc_device_t *) device); - return DC_STATUS_UNSUPPORTED; + status = DC_STATUS_UNSUPPORTED; + goto error_close; }
// Get model capability, protocol and data format @@ -395,24 +389,27 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, *out = (dc_device_t *) device;
return DC_STATUS_SUCCESS; + +error_close: + serial_close (device->port); +error_free: + dc_device_deallocate ((dc_device_t *) device); + return status; }
dc_status_t cochran_commander_device_close (dc_device_t *abstract) { + dc_status_t status = DC_STATUS_SUCCESS; cochran_device_t *device = (cochran_device_t*) abstract;
// Close the device. if (serial_close (device->port) == -1) { - return DC_STATUS_IO; + dc_status_set_error( &status, DC_STATUS_IO ); }
- // Free memory. - free (device->data.logbook); - free (device->data.sample); - - return DC_STATUS_SUCCESS; + return status; }
@@ -421,15 +418,14 @@ cochran_commander_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) { cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = &device->data;
- if (size && size != sizeof (d->fingerprint)) + if (size && size != sizeof (device->fingerprint)) return DC_STATUS_INVALIDARGS;
if (size) - memcpy (&d->fingerprint, data, sizeof (d->fingerprint)); + memcpy (device->fingerprint, data, sizeof (device->fingerprint)); else - memset (&d->fingerprint, 0xFF, sizeof (d->fingerprint)); + memset (device->fingerprint, 0xFF, sizeof (device->fingerprint));
return DC_STATUS_SUCCESS; } @@ -453,9 +449,9 @@ cochran_commander_read (dc_device_t *abstract, dc_event_progress_t *progress, unsigned char command[10]; unsigned char command_size;
- switch (device->info->address_size) + switch (device->info->address_bits) { - case ADDRESS_32_BIT: + case 32: // EMC uses 32 bit addressing command[0] = 0x15; command[1] = (address ) & 0xff; @@ -469,7 +465,7 @@ cochran_commander_read (dc_device_t *abstract, dc_event_progress_t *progress, command[9] = 0x05; command_size = 10; break; - case ADDRESS_24_BIT: + case 24: // Commander uses 24 byte addressing command[0] = 0x15; command[1] = (address ) & 0xff; @@ -523,7 +519,7 @@ cochran_commander_model_id(const char *model)
// Get device capability, protocol info and data format for a device dc_status_t -cochran_commander_info (const int model, cochran_device_info_t **info, cochran_conf_offsets_t **conf_ptr, cochran_log_offsets_t **log_ptr, struct event_size (**event_bytes)[15]) +cochran_commander_info (const int model, const cochran_device_info_t **info, const cochran_conf_offsets_t **conf_ptr, const cochran_log_offsets_t **log_ptr, const struct event_size (**event_bytes)[15]) { // Determine model if (model == COCHRAN_MODEL_EMC_20) @@ -564,99 +560,51 @@ cochran_commander_info (const int model, cochran_device_info_t **info, cochran_c
static dc_status_t -cochran_read_id (dc_device_t *abstract) +cochran_read_id (dc_device_t *abstract, unsigned char *id, int size) { cochran_device_t *device = (cochran_device_t *) abstract; dc_status_t rc; unsigned char command[6] = {0x05, 0x9D, 0xFF, 0x00, 0x43, 0x00};
- rc = cochran_packet(device, NULL, command, 6, device->data.id, 67, 0); + rc = cochran_packet(device, NULL, command, sizeof(command), id, size, 0); if (rc != DC_STATUS_SUCCESS) return rc;
- if (strncmp((const char *)device->data.id, "(C)", 3) != 0) { - // It's a Commander, read again - memcpy(device->data.id0, device->data.id, 67); - device->data.extra_id_flag = 1; - + if (strncmp((const char *) id, "(C)", 3) != 0) { + // It's a Commander, read a different location command[1] = 0xBD; command[2] = 0x7F;
- rc = cochran_packet(device, NULL, command, 6, device->data.id, 67, 0); + rc = cochran_packet(device, NULL, command, sizeof(command), id, size, 0); if (rc != DC_STATUS_SUCCESS) return rc; }
- return DC_STATUS_SUCCESS; -} - - -static dc_status_t -cochran_read_config (dc_device_t *abstract, dc_event_progress_t *progress) -{ - cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *data = &device->data; + // Emit ID block dc_event_vendor_t vendor; - - dc_status_t rc; - unsigned char command[2] = { 0x96, 0x00 }; - - int n; - for (n = 0; n < 2; n++) { - command[1] = n; - rc = cochran_packet(device, progress, command, 2, data->config[n], 512, 0); - if (rc != DC_STATUS_SUCCESS) - return rc; - - vendor.data = data->config[n]; - vendor.size = 512; - device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - } + vendor.data = id; + vendor.size = size; + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
return DC_STATUS_SUCCESS; }
static dc_status_t -cochran_read_misc (dc_device_t *abstract, dc_event_progress_t *progress) +cochran_read_config (dc_device_t *abstract, cochran_data_t *data, dc_event_progress_t *progress) { cochran_device_t *device = (cochran_device_t *) abstract; - dc_status_t rc; dc_event_vendor_t vendor;
- unsigned char command[7] = { 0x89, 0x05, 0x00, 0x00, 0x00, 0xDC, 0x05 }; - - switch (device->info->model & 0xFF0000) - { - case COCHRAN_MODEL_COMMANDER_FAMILY: - command[2] = 0xCA; - command[3] = 0xFD; - break; - case COCHRAN_MODEL_EMC_FAMILY: - command[2] = 0xE0; - command[3] = 0x03; - break; - default: - return DC_STATUS_UNSUPPORTED; - } - - // Send first byte then wait for heartbeat before sending the rest - serial_write(device->port, command, 1); - - char answer[1]; - int n = serial_read(device->port, answer, 1); - if (n != 1) { - ERROR (abstract->context, "Failed to receive device heartbeat."); - return EXITCODE (n); - } - - rc = cochran_packet(device, progress, command + 1, 6, device->data.misc, 1500, 0); + dc_status_t rc; + unsigned char command[2] = { 0x96, 0x00 };
+ rc = cochran_packet(device, progress, command, sizeof(command), data->config, sizeof(data->config), 0); if (rc != DC_STATUS_SUCCESS) return rc;
- vendor.data = device->data.misc; - vendor.size = 1500; + vendor.data = data->config; + vendor.size = sizeof(data->config); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
return DC_STATUS_SUCCESS; @@ -664,18 +612,17 @@ cochran_read_misc (dc_device_t *abstract, dc_event_progress_t *progress)
static dc_status_t -cochran_read_logbook (dc_device_t *abstract, dc_event_progress_t *progress) +cochran_read_logbook (dc_device_t *abstract, cochran_data_t *data, dc_event_progress_t *progress) { cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = &device->data; dc_status_t rc;
- if (d->logbook) - free(d->logbook); + if (data->logbook) + free(data->logbook);
// Allocate space for log book. - d->logbook = (unsigned char *) malloc(d->logbook_size); - if (device == NULL) { + data->logbook = (unsigned char *) malloc(data->logbook_size); + if (data->logbook == NULL) { ERROR (abstract->context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; } @@ -683,38 +630,38 @@ cochran_read_logbook (dc_device_t *abstract, dc_event_progress_t *progress) serial_sleep(device->port, 800);
// set back to 9600 baud - cochran_commander_serial_setup(device); + rc = cochran_commander_serial_setup(device); + if (rc != DC_STATUS_SUCCESS) + return rc;
// Request log book - rc = cochran_commander_read(abstract, progress, 0, d->logbook, - d->logbook_size); + rc = cochran_commander_read(abstract, progress, 0, data->logbook, + data->logbook_size);
return rc; }
static void -cochran_find_fingerprint(dc_device_t *abstract) +cochran_find_fingerprint(dc_device_t *abstract, cochran_data_t *data) { cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &device->data;
// Skip to fingerprint to reduce time - d->fp_dive_num = d->dive_count - 1; + data->fp_dive_num = data->dive_count; + data->fp_dive_num--;
- while (d->fp_dive_num >= 0 && memcmp(&d->fingerprint, - d->logbook + d->fp_dive_num * device->info->log_size - + device->log_ptr->dive_number, - sizeof(d->fingerprint))) - d->fp_dive_num--; + while (data->fp_dive_num >= 0 && memcmp(device->fingerprint, + data->logbook + data->fp_dive_num * device->info->logbook_entry_size, + sizeof(device->fingerprint))) + data->fp_dive_num--; }
static void -cochran_get_sample_parms(dc_device_t *abstract) +cochran_get_sample_parms(dc_device_t *abstract, cochran_data_t *data) { cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &device->data; unsigned int pre_dive_offset = 0, end_dive_offset = 0; unsigned int low_offset, high_offset;
@@ -723,12 +670,27 @@ cochran_get_sample_parms(dc_device_t *abstract) high_offset = 0;
int i; - for (i = d->fp_dive_num + 1; i < d->dive_count; i++) { - pre_dive_offset = array_uint32_le (&(d->logbook[i * device->info->log_size - + device->log_ptr->profile_pre_offset])); - end_dive_offset = array_uint32_le (&(d->logbook[i * device->info->log_size - + device->log_ptr->profile_end_offset])); + for (i = data->fp_dive_num + 1; i < data->dive_count; i++) { + pre_dive_offset = array_uint32_le (data->logbook + i * device->info->logbook_entry_size + + device->log_ptr->profile_pre_offset); + end_dive_offset = array_uint32_le (data->logbook + i * device->info->logbook_entry_size + + device->log_ptr->profile_end_offset); + + // Validate offsets, allow 0xFFFFFFF for end_dive_offset + // because we handle that as a special case. + if (pre_dive_offset < device->info->rb_profile_begin + || pre_dive_offset > device->info->rb_profile_end) { + ERROR(abstract->context, "Invalid pre-dive offset (%08x) on dive %d.", pre_dive_offset, i); + continue; + }
+ if (end_dive_offset < device->info->rb_profile_begin + || (end_dive_offset > device->info->rb_profile_end + && end_dive_offset != 0xFFFFFFFF)) { + ERROR(abstract->context, "Invalid end-dive offset (%08x) on dive %d.", end_dive_offset, i); + continue; + } + // Check for ring buffer wrap-around. if (pre_dive_offset > end_dive_offset) break; @@ -740,39 +702,33 @@ cochran_get_sample_parms(dc_device_t *abstract) }
if (pre_dive_offset > end_dive_offset) { - // Since I can't tell how much memory it has, I'll round. - // I'll round to 128K, dives longer than 12 hrs aren't likely - // and memory in sizes not rounded to 128K might be odd. - high_offset = ((pre_dive_offset - 1) & 0xE0000) + 0x20000; + high_offset = device->info->rb_profile_end; low_offset = device->info->rb_profile_begin; - d->sample_data_offset = low_offset; - d->sample_size = high_offset - low_offset; + data->sample_data_offset = low_offset; + data->sample_size = high_offset - low_offset; } else if (low_offset < 0xFFFFFFFF && high_offset > 0) { - // Round offset and size to 16K boundary - d->sample_data_offset = low_offset & 0xFFFFC000; - high_offset = ((high_offset - 1) & 0xFFFFC000) + 0x4000; - d->sample_size = high_offset - d->sample_data_offset; + data->sample_data_offset = low_offset; + data->sample_size = high_offset - data->sample_data_offset; } else { - d->sample_data_offset = 0; - d->sample_size = 0; + data->sample_data_offset = 0; + data->sample_size = 0; } }
static dc_status_t -cochran_read_samples(dc_device_t *abstract, dc_event_progress_t *progress) +cochran_read_samples(dc_device_t *abstract, cochran_data_t *data, dc_event_progress_t *progress) { cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &device->data; dc_status_t rc;
- if (d->sample_size > 0) { - if (d->sample) - free(d->sample); + if (data->sample_size > 0) { + if (data->sample) + free(data->sample);
- d->sample = (unsigned char *) malloc(d->sample_size); - if (d->sample == NULL) { + data->sample = (unsigned char *) malloc(data->sample_size); + if (data->sample == NULL) { ERROR (abstract->context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; } @@ -780,11 +736,13 @@ cochran_read_samples(dc_device_t *abstract, dc_event_progress_t *progress) serial_sleep(device->port, 800);
// set back to 9600 baud - cochran_commander_serial_setup(device); + rc = cochran_commander_serial_setup(device); + if (rc != DC_STATUS_SUCCESS) + return rc;
// Read the sample data - rc = cochran_commander_read (abstract, progress, d->sample_data_offset, - d->sample, d->sample_size); + rc = cochran_commander_read (abstract, 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; @@ -796,11 +754,10 @@ cochran_read_samples(dc_device_t *abstract, dc_event_progress_t *progress)
static dc_status_t -cochran_commander_device_read_all (dc_device_t *abstract) +cochran_commander_device_read_all (dc_device_t *abstract, cochran_data_t *data) { cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &device->data; - dc_event_progress_t progress = {0}; + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
dc_status_t rc; int max_config, max_logbook, max_sample; @@ -815,39 +772,42 @@ cochran_commander_device_read_all (dc_device_t *abstract) device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Read config - rc = cochran_read_config(abstract, &progress); + rc = cochran_read_config(abstract, data, &progress); if (rc != DC_STATUS_SUCCESS) return rc;
// Determine size of dive list to read. Round up to nearest 16K if (device->info->endian == ENDIAN_LE) - d->dive_count = array_uint16_le (d->config[0] + device->conf_ptr->dive_count); + data->dive_count = array_uint16_le (data->config + device->conf_ptr->dive_count); else - d->dive_count = array_uint16_be (d->config[0] + device->conf_ptr->dive_count); + data->dive_count = array_uint16_be (data->config + device->conf_ptr->dive_count);
- d->logbook_size = ((d->dive_count * device->info->log_size) & 0xFFFFC000) - + 0x4000; + if (data->dive_count > device->info->logbook_entry_count) { + data->logbook_size = device->info->logbook_entry_count * device->info->logbook_entry_size; + } else { + data->logbook_size = data->dive_count * device->info->logbook_entry_size; + }
- progress.maximum -= max_logbook - d->logbook_size; + progress.maximum -= max_logbook - data->logbook_size; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
- rc = cochran_read_logbook(abstract, &progress); + rc = cochran_read_logbook(abstract, data, &progress); if (rc != DC_STATUS_SUCCESS) return rc;
// Determine sample memory to read - cochran_find_fingerprint(abstract); - cochran_get_sample_parms(abstract); + cochran_find_fingerprint(abstract, data); + cochran_get_sample_parms(abstract, data);
- progress.maximum -= max_sample - d->sample_size; + progress.maximum -= max_sample - data->sample_size; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
- rc = cochran_read_samples(abstract, &progress); + rc = cochran_read_samples(abstract, data, &progress); if (rc != DC_STATUS_SUCCESS) return rc;
// Determine logs that haven't had profile data overwritten - cochran_check_profiles(abstract); + cochran_check_profiles(abstract, data);
return DC_STATUS_SUCCESS; } @@ -859,64 +819,49 @@ cochran_commander_device_read_all (dc_device_t *abstract) (d)[3] = ((i) >> 24) & 0xff)
dc_status_t -cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *data) +cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *dump) { cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *d = (cochran_data_t *) &device->data; - dc_event_progress_t progress = {0}; - dc_event_vendor_t vendor; - + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; dc_status_t rc; - int size; + cochran_data_t data; + data.logbook = NULL; + data.sample = NULL;
// Make sure buffer is good. - if (!dc_buffer_clear(data)) { + if (!dc_buffer_clear(dump)) { ERROR (abstract->context, "Uninitialized buffer."); return DC_STATUS_INVALIDARGS; }
- // Determine size for progress - size = 512 + 512 + 1500 + device->info->rb_profile_end; + // Reserve space + if (!dc_buffer_resize(dump, device->info->rb_profile_end)) { + ERROR(abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + }
+ // Determine size for progress progress.current = 0; - progress.maximum = size; + progress.maximum = 512 + device->info->rb_profile_end; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
- // Emit ID blocks as vendor data - if (d->extra_id_flag) { - vendor.data = d->id0; - vendor.size = 67; - device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - } - - vendor.data = d->id; - vendor.size = 67; - device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - - rc = cochran_read_config (abstract, &progress); - if (rc != DC_STATUS_SUCCESS) - return rc; - - rc = cochran_read_misc (abstract, &progress); + rc = cochran_read_config (abstract, &data, &progress); if (rc != DC_STATUS_SUCCESS) return rc;
// Read logbook and sample memory
- // Reserve space - if (!dc_buffer_resize(data, device->info->rb_profile_end)) { - ERROR(abstract->context, "Insufficient buffer space available."); - return DC_STATUS_NOMEMORY; - } - serial_sleep(device->port, 800);
// set back to 9600 baud - cochran_commander_serial_setup(device); + rc = cochran_commander_serial_setup(device); + if (rc != DC_STATUS_SUCCESS) { + return rc; + }
// Read the sample data, from 0 to sample end will include logbook rc = cochran_commander_read (abstract, &progress, 0, - dc_buffer_get_data(data), device->info->rb_profile_end); + dc_buffer_get_data(dump), device->info->rb_profile_end); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); return rc; @@ -935,15 +880,15 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *data) static unsigned int cochran_guess_sample_end_address(cochran_device_t *device, cochran_data_t *data, unsigned int log_num) { - const unsigned char *log_entry = data->logbook + device->info->log_size * log_num; + const unsigned char *log_entry = data->logbook + device->info->logbook_entry_size * log_num;
if (log_num == data->dive_count) - // Return next usable address from config0 page - return array_uint32_le(data->config[0] + // Return next usable address from config page + return array_uint32_le(data->config + device->info->rb_profile_end);
// Next log's start address - return array_uint32_le(log_entry + device->info->log_size + 6); + return array_uint32_le(log_entry + device->info->logbook_entry_size + 6); }
@@ -952,7 +897,6 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { cochran_device_t *device = (cochran_device_t *) abstract; - cochran_data_t *data = &device->data;
unsigned int sample_start_address, sample_end_address; dc_status_t rc; @@ -960,28 +904,47 @@ cochran_commander_device_foreach (dc_device_t *abstract, unsigned char *log_entry, *fingerprint, *sample, *dive; int sample_size, dive_size;
- rc = cochran_commander_device_read_all (abstract); + cochran_data_t data; + data.logbook = NULL; + data.sample = NULL; + rc = cochran_commander_device_read_all (abstract, &data); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = abstract->devinfo.model; + devinfo.firmware = 0; // unknown + devinfo.serial = array_uint32_le(data.config + device->conf_ptr->serial_number); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
if (rc != DC_STATUS_SUCCESS) return rc;
// Loop through each dive int i; - for (i = data->dive_count - 1; i > data->fp_dive_num; i--) { - log_entry = data->logbook + i * device->info->log_size; + for (i = data.dive_count - 1; i > data.fp_dive_num; i--) { + log_entry = data.logbook + i * device->info->logbook_entry_size;
sample_start_address = array_uint32_le (log_entry + 6); - sample_end_address = array_uint32_le (log_entry + device->info->log_size / 2); + sample_end_address = array_uint32_le (log_entry + device->info->logbook_entry_size / 2); + + // Validate + if (sample_start_address < device->info->rb_profile_begin + || sample_start_address > device->info->rb_profile_end + || sample_end_address < device->info->rb_profile_begin + || (sample_end_address > device->info->rb_profile_end + && sample_end_address != 0xFFFFFFFF)) { + continue; + }
if (sample_end_address == 0xFFFFFFFF) // Corrupt dive, guess the end address - sample_end_address = cochran_guess_sample_end_address(device, data, i); + sample_end_address = cochran_guess_sample_end_address(device, &data, i);
- sample = data->sample + sample_start_address - data->sample_data_offset; + sample = data.sample + sample_start_address - data.sample_data_offset;
// Determine size of sample unsigned int dive_num =array_uint16_le(log_entry + device->log_ptr->dive_number); - if (dive_num >= data->profile_tail) + if (dive_num >= data.profile_tail) sample_size = sample_end_address - sample_start_address; else sample_size = 0; @@ -991,28 +954,28 @@ cochran_commander_device_foreach (dc_device_t *abstract, sample_size += device->info->rb_profile_end - device->info->rb_profile_begin;
- fingerprint = log_entry + device->log_ptr->dive_number; + fingerprint = log_entry; // Date bytes
// Build dive blob - dive_size = device->info->log_size + sample_size; + dive_size = device->info->logbook_entry_size + sample_size; dive = (unsigned char *) malloc(dive_size); if (dive == NULL) return DC_STATUS_NOMEMORY;
- memcpy(dive, log_entry, device->info->log_size); // log + memcpy(dive, log_entry, device->info->logbook_entry_size); // log
// Copy profile data if (sample_size) { if (sample_start_address <= sample_end_address) { - memcpy(dive + device->info->log_size, sample, + memcpy(dive + device->info->logbook_entry_size, sample, sample_size); } else { // It wrapped the buffer, copy two sections unsigned int size = device->info->rb_profile_end - sample_start_address;
- memcpy(dive + device->info->log_size, sample, size); - memcpy(dive + device->info->log_size + size, - data->sample, sample_end_address - device->info->rb_profile_begin); + memcpy(dive + device->info->logbook_entry_size, sample, size); + memcpy(dive + device->info->logbook_entry_size + size, + data.sample, sample_end_address - device->info->rb_profile_begin); } }
@@ -1038,16 +1001,16 @@ cochran_commander_device_foreach (dc_device_t *abstract, */
static void -cochran_check_profiles(dc_device_t *abstract) { +cochran_check_profiles(dc_device_t *abstract, cochran_data_t *data) { cochran_device_t *device = (cochran_device_t *) abstract;
// Find head log entry // Set handy point to first dive's dive number - unsigned char *l = device->data.logbook + device->log_ptr->dive_number; + unsigned char *l = data->logbook + device->log_ptr->dive_number; int head; - for (head = 0; head < device->info->max_log - 1; head ++) { + for (head = 0; head < device->info->logbook_entry_count - 1; head ++) { int this_dive = array_uint16_le(l); - l += device->info->log_size; + l += device->info->logbook_entry_size; int next_dive = array_uint16_le(l);
if (next_dive < this_dive || next_dive == 0xFFFF) @@ -1057,9 +1020,9 @@ cochran_check_profiles(dc_device_t *abstract) { // This is the last piece of data unsigned int head_ptr; if (device->info->endian == ENDIAN_LE) - head_ptr = array_uint32_le(device->data.config[0] + device->conf_ptr->last_log); + head_ptr = array_uint32_le(data->config + device->conf_ptr->last_log); else - head_ptr = array_uint32_be(device->data.config[0] + device->conf_ptr->last_log); + head_ptr = array_uint32_be(data->config + device->conf_ptr->last_log);
// Cochran (commander at least) erases 8k blocks, so round up head_ptr = (head_ptr & 0xfffff000) + 0x2000; @@ -1071,16 +1034,16 @@ cochran_check_profiles(dc_device_t *abstract) { int profile_wrap = 0; int n = head - 1; if (n < 0) - n = MIN(device->info->max_log - 1, head); + n = MIN(device->info->logbook_entry_count - 1, head);
while (n != head) { - unsigned int start_dive_ptr = array_uint32_le(device->data.logbook + device->info->log_size * n + device->log_ptr->profile_begin_offset); - unsigned int end_dive_ptr = array_uint32_le(device->data.logbook + device->info->log_size * n + device->log_ptr->profile_end_offset); + unsigned int start_dive_ptr = array_uint32_le(data->logbook + device->info->logbook_entry_size * n + device->log_ptr->profile_begin_offset); + unsigned int end_dive_ptr = array_uint32_le(data->logbook + device->info->logbook_entry_size * n + device->log_ptr->profile_end_offset);
if (start_dive_ptr == 0xFFFFFFFF || start_dive_ptr == 0) { n--; if (n < 0) - n = MIN(device->info->max_log - 1, head); + n = MIN(device->info->logbook_entry_count - 1, head); continue; }
@@ -1098,11 +1061,11 @@ cochran_check_profiles(dc_device_t *abstract) {
n--; if (n < 0) - n = MIN(device->info->max_log - 1, head); + n = MIN(device->info->logbook_entry_count - 1, head); }
- device->data.log_head = head; - device->data.profile_head = head; + data->log_head = head; + data->profile_head = head;
- device->data.profile_tail = tail; + data->profile_tail = tail; } diff --git a/src/cochran_commander.h b/src/cochran_commander.h index 22f17a3..f6e3921 100644 --- a/src/cochran_commander.h +++ b/src/cochran_commander.h @@ -24,30 +24,21 @@ #define COCHRAN_TIMESTAMP_OFFSET 694242000
#define COCHRAN_MODEL_SIZE 8 -#define COCHRAN_FINGERPRINT_SIZE 2 +#define COCHRAN_FINGERPRINT_SIZE 6
#define UNSUPPORTED 0xFFFFFFFF
-typedef enum cochran_model_t { - COCHRAN_MODEL_UNKNOWN = 0, - COCHRAN_MODEL_EMC_FAMILY = 1 << 16, - COCHRAN_MODEL_EMC_14, - COCHRAN_MODEL_EMC_16, - COCHRAN_MODEL_EMC_20, - COCHRAN_MODEL_COMMANDER_FAMILY = 2 << 16, - COCHRAN_MODEL_COMMANDER_AIR_NITROX, -} cochran_model_t; +#define COCHRAN_MODEL_UNKNOWN = -1, +#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0 +#define COCHRAN_MODEL_EMC_14 1 +#define COCHRAN_MODEL_EMC_16 2 +#define COCHRAN_MODEL_EMC_20 3
typedef enum cochran_endian_t { ENDIAN_LE, ENDIAN_BE, } cochran_endian_t;
-typedef enum cochran_addresssize_t { - ADDRESS_24_BIT, - ADDRESS_32_BIT, -} cochran_addresssize_t; - typedef enum cochran_sample_format_t { SAMPLE_UNKNOWN, SAMPLE_CMDR, @@ -58,8 +49,8 @@ typedef enum cochran_sample_format_t { // Used in reading the information from the device // and (in the case of max_*) used when decoding. typedef struct cochran_device_info_t { - cochran_model_t model; - cochran_addresssize_t address_size; + int model; + int address_bits; cochran_endian_t endian; int high_baud;
@@ -68,8 +59,8 @@ typedef struct cochran_device_info_t { int rb_profile_begin; int rb_profile_end;
- int log_size; - int max_log; + int logbook_entry_size; + int logbook_entry_count; int max_gas;
cochran_sample_format_t sample_format; @@ -80,6 +71,7 @@ typedef struct cochran_conf_offsets_t { int dive_count; int last_log; int last_interdive; + int serial_number; } cochran_conf_offsets_t;
// Offsets into each log block @@ -104,18 +96,11 @@ typedef struct cochran_log_offsets_t { } cochran_log_offsets_t;
typedef struct cochran_data_t { - unsigned char id0[67]; - unsigned char id[67]; - unsigned char config[2][512]; - unsigned char misc[1500]; + unsigned char config[512]; unsigned char *logbook; unsigned char *sample;
- unsigned int extra_id_flag; - unsigned int config_count; - unsigned short int dive_count; - unsigned char fingerprint[COCHRAN_FINGERPRINT_SIZE]; int fp_dive_num; int log_head, log_tail; // head and tail for log ringbuffer int profile_head, profile_tail; // Entries that have valid profiles @@ -133,14 +118,13 @@ struct event_size {
typedef struct cochran_device_t { dc_device_t base; - const char *name; // serial port name serial_t *port; unsigned char model_string[COCHRAN_MODEL_SIZE + 1]; - cochran_device_info_t *info; - cochran_conf_offsets_t *conf_ptr; - cochran_log_offsets_t *log_ptr; - struct event_size (*event_bytes)[15]; - cochran_data_t data; // dive data used in parsing + unsigned char fingerprint[COCHRAN_FINGERPRINT_SIZE]; + const cochran_device_info_t *info; + const cochran_conf_offsets_t *conf_ptr; + const cochran_log_offsets_t *log_ptr; + const struct event_size (*event_bytes)[15]; } cochran_device_t;
@@ -157,4 +141,4 @@ dc_status_t cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *data); int cochran_commander_model_id(const char *model); dc_status_t cochran_commander_info(const int model, - cochran_device_info_t **info, cochran_conf_offsets_t **conf_ptr, cochran_log_offsets_t **log_ptr, struct event_size (**event_bytes)[15]); + const cochran_device_info_t **info, const cochran_conf_offsets_t **conf_ptr, const cochran_log_offsets_t **log_ptr, const struct event_size (**event_bytes)[15]); diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 45184fe..0100a94 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -81,9 +81,9 @@ typedef struct cochran_commander_parser_t cochran_commander_parser_t; struct cochran_commander_parser_t { dc_parser_t base; unsigned int model; - cochran_device_info_t *info; - cochran_log_offsets_t *log_ptr; - struct event_size (*event_bytes)[15]; + const cochran_device_info_t *info; + const cochran_log_offsets_t *log_ptr; + const struct event_size (*event_bytes)[15]; };
static dc_parser_vtable_t cochran_commander_parser_vtable = { @@ -136,7 +136,7 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) { cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract; - cochran_log_offsets_t *l = parser->log_ptr; + const cochran_log_offsets_t *l = parser->log_ptr; const unsigned char *log_entry = abstract->data;
datetime->second = log_entry[l->sec]; @@ -220,7 +220,7 @@ cochran_commander_handle_event (dc_parser_t *abstract, * Used to find the end of a dive that has an incomplete dive-end * block. It parses backwards past inter-dive events. */ -int cochran_backparse(dc_parser_t *abstract, unsigned const char *samples, int size, struct event_size (*event_bytes)[15]) { +int cochran_backparse(dc_parser_t *abstract, unsigned const char *samples, int size, const struct event_size (*event_bytes)[15]) { int result = size, best_result = size;
for (int x = 0; (*event_bytes)[x].code != -1; x++) { @@ -351,9 +351,9 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, const cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract; const cochran_log_offsets_t *l = parser->log_ptr; const unsigned char *log_entry = abstract->data; - const unsigned char *samples = log_entry + parser->info->log_size; + const unsigned char *samples = log_entry + parser->info->logbook_entry_size;
- unsigned int size = abstract->size - parser->info->log_size; + unsigned int size = abstract->size - parser->info->logbook_entry_size;
int sampling_size; switch (parser->info->sample_format) {