devel
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
December 2014
- 6 participants
- 13 discussions
This changes the data passed back by the foreach function and the data
that is passed to the parser. It creates a structure containing everything
needed to parse the dive. Pointers were removed so that the data can be
stored and parsed later.
Fixed a typo in COCHRAN_TIMESTAMP_OFFSET.
Fixed a bug that occured in calculating NDL and Deco time if an event
occurred between samples.
Added start temperature to Commander parse.
Made corrupt dive recovery cleaner.
Cleaned up many compiler warnings (-Wall)
Cleaned up some whitespace
Re-wrapped some lines.
Removed extraneous parenthesis not needed with & operator.
Seperated device configuration from device handle (more or less)
Cleaned up sample buffer wrap-around logic and used better var naming.
Fixed typo in CMD_START_OFFSET
---
src/cochran_cmdr_parser.c | 66 +++++-----
src/cochran_commander.c | 288 ++++++++++++++++++++++++-----------------
src/cochran_commander.h | 59 +++++----
src/cochran_commander_parser.c | 57 +++++---
src/cochran_commander_parser.h | 2 +
src/cochran_emc_parser.c | 182 ++++++++++++--------------
6 files changed, 353 insertions(+), 301 deletions(-)
diff --git a/src/cochran_cmdr_parser.c b/src/cochran_cmdr_parser.c
index de25912..b2b9748 100644
--- a/src/cochran_cmdr_parser.c
+++ b/src/cochran_cmdr_parser.c
@@ -35,15 +35,12 @@
#include "cochran_commander_parser.h"
-#define SZ_SAMPLE 2
-
-
dc_status_t
cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
unsigned int flags, void *value)
{
- cochran_data_t *data = (cochran_data_t *) abstract->data;
- unsigned char *log = data->current_log;
+ const unsigned char *data = abstract->data;
+ const unsigned char *log = data + COCHRAN_MODEL_SIZE;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value;
@@ -53,31 +50,35 @@ cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
case DC_FIELD_TEMPERATURE_SURFACE:
*((unsigned int*) value) = ((float) log[CMD_START_TEMP] - 32) / 1.8;
case DC_FIELD_TEMPERATURE_MINIMUM:
- *((unsigned int*) value) = ((float) log[CMD_MIN_TEMP] / 2 + 20 - 32) / 1.8;
+ *((unsigned int*) value) = ((float) log[CMD_MIN_TEMP] / 2
+ + 20 - 32) / 1.8;
case DC_FIELD_TEMPERATURE_MAXIMUM:
- *((unsigned int*) value) = ((float) log[CMD_MAX_TEMP] / 2 + 20 - 32) / 1.8;
+ *((unsigned int*) value) = ((float) log[CMD_MAX_TEMP] / 2
+ + 20 - 32) / 1.8;
case DC_FIELD_DIVETIME:
*((unsigned int *) value) = array_uint16_le (log + EMC_BT) * 60;
break;
case DC_FIELD_MAXDEPTH:
- *((double *) value) = (float) array_uint16_le (log + CMD_MAX_DEPTH) / 4 * FEET;
+ *((double *) value) = (float) array_uint16_le (log + CMD_MAX_DEPTH)
+ / 4 * FEET;
break;
case DC_FIELD_AVGDEPTH:
- *((double *) value) = (float) array_uint16_le (log + CMD_AVG_DEPTH) / 4 * FEET;
+ *((double *) value) = (float) array_uint16_le (log + CMD_AVG_DEPTH)
+ / 4 * FEET;
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = 2;
break;
case DC_FIELD_GASMIX:
- gasmix->oxygen = (double) array_uint16_le ((char *)log
+ gasmix->oxygen = (double) array_uint16_le (log
+ CMD_O2_PERCENT + 2 * flags) / 256 / 100;
gasmix->helium = 0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_SALINITY:
// 0 = low conductivity, 1 = high, maybe there's a 2?
- water->type = ( (log[CMD_WATER_CONDUCTIVITY] & 0x3) == 0 ? DC_WATER_FRESH
- : DC_WATER_SALT );
+ water->type = ( (log[CMD_WATER_CONDUCTIVITY] & 0x3) == 0
+ ? DC_WATER_FRESH : DC_WATER_SALT );
water->density = 1000 + 12.5 * (log[CMD_WATER_CONDUCTIVITY] & 0x3);
break;
case DC_FIELD_ATMOSPHERIC:
@@ -97,11 +98,12 @@ dc_status_t
cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata)
{
- cochran_data_t *data = (cochran_data_t *)abstract->data;
- unsigned char *sdata = data->current_sample;
- unsigned char *log = data->current_log;
- unsigned int size = data->current_sample_size;
- unsigned char *s;
+ const unsigned char *log = abstract->data + COCHRAN_MODEL_SIZE;
+ const unsigned char *samples = log + COCHRAN_CMDR_LOG_SIZE;
+
+ unsigned int size = abstract->size - COCHRAN_MODEL_SIZE
+ - COCHRAN_CMDR_LOG_SIZE;
+ const unsigned char *s;
dc_sample_value_t sample = {0}, empty_sample = {0};
unsigned int time = 0, last_sample_time;
@@ -109,8 +111,10 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
double temperature;
double depth;
double ascent_rate;
- unsigned char deco_obligation = 0;
- unsigned int deco_time = 0;
+ unsigned char corrupt_dive = 0;
+
+ if (array_uint32_le(log + COCHRAN_CMDR_LOG_SIZE / 2) == 0xFFFFFFFF)
+ corrupt_dive = 1;
// Cochran samples depth every second and varies between ascent rate
// and temp ever other second.
@@ -124,8 +128,8 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
sample.depth = depth * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
- // TODO: sample.temperature = (log + CMD_START_TEMP - 32) / 1.8;
- // TODO: if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
+ sample.temperature = (float) *(log + CMD_START_TEMP) - 32 / 1.8;
+ if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
while (offset < size) {
sample = empty_sample;
@@ -137,13 +141,10 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
callback (DC_SAMPLE_TIME, sample, userdata);
}
- if (offset > data->sample_memory_end_address)
- s = sdata + offset - data->sample_memory_end_address - 1;
- else
- s = sdata + offset;
+ s = samples + offset;
// If corrupt_dive end before offset
- if (data->corrupt_dive) {
+ if (corrupt_dive) {
if ( s[0] == 0x10
|| s[0] == 0xFF
|| s[0] == 0xA8) {
@@ -155,17 +156,12 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
// Check for event
if (s[0] & 0x80) {
- offset += cochran_commander_handle_event(abstract, callback, userdata,
- s[0], offset, time);
- if (s[0] == 0xC5)
- deco_obligation = 1; // Deco obligation begins
- else if (s[0] == 0xC8)
- deco_obligation = 0; // Deco obligation ends
-
+ offset += cochran_commander_handle_event(abstract, callback,
+ userdata, s[0], offset, time);
continue;
}
-
+
// Depth is logged as change in feet, bit 0x40 means negative depth
depth += (float) (s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
sample.depth = depth * FEET;
@@ -186,7 +182,7 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
}
time ++;
- offset += SZ_SAMPLE;
+ offset += COCHRAN_CMDR_SAMPLE_SIZE;
}
return DC_STATUS_SUCCESS;
diff --git a/src/cochran_commander.c b/src/cochran_commander.c
index e8884c1..fdcac67 100644
--- a/src/cochran_commander.c
+++ b/src/cochran_commander.c
@@ -42,13 +42,17 @@ static const dc_device_vtable_t cochran_commander_device_vtable = {
DC_FAMILY_COCHRAN_COMMANDER,
cochran_commander_device_set_fingerprint, /* set_fingerprint */
cochran_commander_device_read, /* read */
- NULL, /* write */
+ NULL, /* write */
cochran_commander_device_dump, /* dump */
cochran_commander_device_foreach, /* foreach */
cochran_commander_device_close /* close */
};
+static dc_status_t
+cochran_read_id(dc_device_t *device);
+
+
dc_status_t
cochran_packet (cochran_device_t *device, const unsigned char command[],
unsigned int csize, unsigned char answer[], unsigned int asize,
@@ -76,9 +80,9 @@ cochran_packet (cochran_device_t *device, const unsigned char command[],
// Weird but I only get the right result when I do it twice
// Rates are odd, like 825600 for the EMC, 115200 for commander
- serial_configure(device->port, device->data.high_baud, 8,
+ serial_configure(device->port, device->data.conf.high_baud, 8,
SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE);
- serial_configure(device->port, device->data.high_baud, 8,
+ serial_configure(device->port, device->data.conf.high_baud, 8,
SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE);
}
@@ -202,7 +206,8 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context,
device->progress = NULL;
device->data.logbook = NULL;
device->data.sample = NULL;
- cochran_commander_device_set_fingerprint((dc_device_t *) device, "", 0);
+ cochran_commander_device_set_fingerprint((dc_device_t *) device,
+ NULL, 0);
rc = cochran_commander_serial_open(device, context);
if (rc != DC_STATUS_SUCCESS)
@@ -219,8 +224,13 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context,
}
// Check ID
- if ((device->data.model & 0xFF0000) == COCHRAN_MODEL_UNKNOWN) {
- ERROR (context, "Device not recognized.");
+ if ((device->data.conf.model & 0xFF0000) == COCHRAN_MODEL_UNKNOWN) {
+ ERROR (context,
+ "Unknown Cochran model %02x %02x %02x %02x %02x %02x %02x %02x",
+ *(device->data.id + 0x3B), *(device->data.id + 0x3B + 1),
+ *(device->data.id + 0x3B + 2), *(device->data.id + 0x3B + 3),
+ *(device->data.id + 0x3B + 4), *(device->data.id + 0x3B + 5),
+ *(device->data.id + 0x3B + 6), *(device->data.id + 0x3B + 7));
serial_close (device->port);
free (device);
return DC_STATUS_UNSUPPORTED;
@@ -257,15 +267,15 @@ 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);
+ cochran_data_t *d = &device->data;
if (size && size != sizeof (d->fingerprint))
return DC_STATUS_INVALIDARGS;
if (size)
- memcpy (&(d->fingerprint), data, sizeof (d->fingerprint));
+ memcpy (&d->fingerprint, data, sizeof (d->fingerprint));
else
- memset (&(d->fingerprint), 0xFF, sizeof (d->fingerprint));
+ memset (&d->fingerprint, 0xFF, sizeof (d->fingerprint));
return DC_STATUS_SUCCESS;
}
@@ -281,7 +291,7 @@ cochran_commander_device_read (dc_device_t *abstract, unsigned int address,
unsigned char command[10];
unsigned char command_size;
- switch (device->data.address_length)
+ switch (device->data.conf.address_length)
{
case COCHRAN_ADDRESS_LENGTH_32:
// EMC uses 32 bit addressing
@@ -322,71 +332,65 @@ cochran_commander_device_read (dc_device_t *abstract, unsigned int address,
}
-static void
-cochran_set_device_config (cochran_device_t *device)
+void
+cochran_commander_get_config (const unsigned char *model,
+ cochran_config_t *conf)
{
- dc_device_t *abstract = (dc_device_t *) device;
// Determine model
- if (memcmp(device->data.id + 0x3B, "AM2315\xA3\x71", 8) == 0)
+ if (memcmp(model, "AM2315\xA3\x71", 8) == 0)
{
- device->data.model = COCHRAN_MODEL_EMC_20;
- device->data.log_size = 512;
- device->data.sample_memory_start_address = 0x94000;
- device->data.dive_num_ptr = 0x56;
- device->data.dive_count_ptr = 0xD2;
- device->data.dive_count_endian = COCHRAN_LE_TYPE;
- device->data.sample_end_ptr = 256;
- device->data.log_pre_dive_ptr = 30;
- device->data.log_end_dive_ptr = 256;
- device->data.last_interdive_ptr = 233;
- device->data.last_entry_ptr = 194;
- device->data.date_format = COCHRAN_DATE_FORMAT_SMHDMY;
- device->data.address_length = COCHRAN_ADDRESS_LENGTH_32;
- device->data.high_baud = 825600;
- }
- else if (memcmp(device->data.id + 0x3B, "AMA315\xC3\xC5", 8) == 0)
+ conf->model = COCHRAN_MODEL_EMC_20;
+ conf->log_size = 512;
+ conf->sample_memory_start_address = 0x94000;
+ conf->dive_num_ptr = 0x56;
+ conf->dive_count_ptr = 0xD2;
+ conf->dive_count_endian = COCHRAN_LE_TYPE;
+ conf->sample_end_ptr = 256;
+ conf->log_pre_dive_ptr = 30;
+ conf->log_end_dive_ptr = 256;
+ conf->last_interdive_ptr = 233;
+ conf->last_entry_ptr = 194;
+ conf->date_format = COCHRAN_DATE_FORMAT_SMHDMY;
+ conf->address_length = COCHRAN_ADDRESS_LENGTH_32;
+ conf->high_baud = 825600;
+ }
+ else if (memcmp(model, "AMA315\xC3\xC5", 8) == 0)
{
- device->data.model = COCHRAN_MODEL_EMC_16;
- device->data.log_size = 512;
- device->data.sample_memory_start_address = 0x94000;
- device->data.dive_num_ptr = 0x56;
- device->data.dive_count_ptr = 0xD2;
- device->data.dive_count_endian = COCHRAN_LE_TYPE;
- device->data.sample_end_ptr = 256;
- device->data.log_pre_dive_ptr = 30;
- device->data.log_end_dive_ptr = 256;
- device->data.last_interdive_ptr = 233;
- device->data.last_entry_ptr = 194;
- device->data.date_format = COCHRAN_DATE_FORMAT_SMHDMY;
- device->data.address_length = COCHRAN_ADDRESS_LENGTH_32;
- device->data.high_baud = 825600;
- }
- else if (memcmp(device->data.id + 0x3B, "AM\x11""2212\x02", 8) == 0)
+ conf->model = COCHRAN_MODEL_EMC_16;
+ conf->log_size = 512;
+ conf->sample_memory_start_address = 0x94000;
+ conf->dive_num_ptr = 0x56;
+ conf->dive_count_ptr = 0xD2;
+ conf->dive_count_endian = COCHRAN_LE_TYPE;
+ conf->sample_end_ptr = 256;
+ conf->log_pre_dive_ptr = 30;
+ conf->log_end_dive_ptr = 256;
+ conf->last_interdive_ptr = 233;
+ conf->last_entry_ptr = 194;
+ conf->date_format = COCHRAN_DATE_FORMAT_SMHDMY;
+ conf->address_length = COCHRAN_ADDRESS_LENGTH_32;
+ conf->high_baud = 825600;
+ }
+ else if (memcmp(model, "AM\x11""2212\x02", 8) == 0)
{
- device->data.model = COCHRAN_MODEL_COMMANDER_AIR_NITROX;
- device->data.log_size = 256;
- device->data.sample_memory_start_address = 0x20000;
- device->data.dive_num_ptr = 0x46;
- device->data.dive_count_ptr = 0x46;
- device->data.dive_count_endian = COCHRAN_BE_TYPE;
- device->data.sample_end_ptr = 256;
- device->data.log_pre_dive_ptr = 30;
- device->data.log_end_dive_ptr = 128;
- device->data.last_interdive_ptr = 167;
- device->data.last_entry_ptr = -1;
- device->data.date_format = COCHRAN_DATE_FORMAT_MSDHYM;
- device->data.address_length = COCHRAN_ADDRESS_LENGTH_24;
- device->data.high_baud = 115200;
+ conf->model = COCHRAN_MODEL_COMMANDER_AIR_NITROX;
+ conf->log_size = 256;
+ conf->sample_memory_start_address = 0x20000;
+ conf->dive_num_ptr = 0x46;
+ conf->dive_count_ptr = 0x46;
+ conf->dive_count_endian = COCHRAN_BE_TYPE;
+ conf->sample_end_ptr = 256;
+ conf->log_pre_dive_ptr = 30;
+ conf->log_end_dive_ptr = 128;
+ conf->last_interdive_ptr = 167;
+ conf->last_entry_ptr = -1;
+ conf->date_format = COCHRAN_DATE_FORMAT_MSDHYM;
+ conf->address_length = COCHRAN_ADDRESS_LENGTH_24;
+ conf->high_baud = 115200;
}
else
{
- device->data.model = 0;
- ERROR (abstract->context,
- "Unknown Cochran model %02x %02x %02x %02x %02x %02x %02x %02x",
- *(device->data.id + 0x3B), *(device->data.id + 0x3B + 1),
- *(device->data.id + 0x3B + 2), *(device->data.id + 0x3B + 3),
- *(device->data.id + 0x3B + 4), *(device->data.id + 0x3B + 5),
- *(device->data.id + 0x3B + 6), *(device->data.id + 0x3B + 7));
+ conf->model = 0;
}
return;
@@ -404,7 +408,7 @@ cochran_read_id (dc_device_t *abstract)
if (rc != DC_STATUS_SUCCESS)
return rc;
- if (strncmp(device->data.id, "(C)", 3) != 0) {
+ if (strncmp((const char *)device->data.id, "(C)", 3) != 0) {
// It's a Commander, read again
memcpy(device->data.id0, device->data.id, 67);
@@ -416,7 +420,8 @@ cochran_read_id (dc_device_t *abstract)
return rc;
}
- cochran_set_device_config(device);
+ cochran_commander_get_config(device->data.id + 0x3B, &device->data.conf);
+
return DC_STATUS_SUCCESS;
}
@@ -427,10 +432,11 @@ cochran_read_config (dc_device_t *abstract)
{
cochran_device_t *device = (cochran_device_t *) abstract;
cochran_data_t *data = &device->data;
+
dc_status_t rc;
unsigned char command[2] = { 0x96, 0x00 };
- if ((data->model & 0xFF0000) == COCHRAN_MODEL_EMC_FAMILY)
+ if ((data->conf.model & 0xFF0000) == COCHRAN_MODEL_EMC_FAMILY)
data->config_count = 2;
else
data->config_count = 4;
@@ -454,7 +460,7 @@ cochran_read_misc (dc_device_t *abstract)
unsigned char command[7] = { 0x89, 0x05, 0x00, 0x00, 0x00, 0xDC, 0x05 };
- switch (device->data.model & 0xFF0000)
+ switch (device->data.conf.model & 0xFF0000)
{
case COCHRAN_MODEL_COMMANDER_FAMILY:
command[2] = 0xCA;
@@ -486,7 +492,7 @@ static dc_status_t
cochran_read_logbook (dc_device_t *abstract)
{
cochran_device_t *device = (cochran_device_t *) abstract;
- cochran_data_t *d = &(device->data);
+ cochran_data_t *d = &device->data;
dc_status_t rc;
if (d->logbook)
@@ -516,7 +522,8 @@ cochran_read_logbook (dc_device_t *abstract)
cochran_commander_serial_setup(device, abstract->context);
// Request log book
- rc = cochran_commander_device_read(abstract, 0, d->logbook, d->logbook_size);
+ rc = cochran_commander_device_read(abstract, 0, d->logbook,
+ d->logbook_size);
device->progress->current = d->logbook_size;
device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress);
@@ -530,14 +537,15 @@ static void
cochran_find_fingerprint(dc_device_t *abstract)
{
cochran_device_t *device = (cochran_device_t *) abstract;
- cochran_data_t *d = (cochran_data_t *) &(device->data);
+ cochran_data_t *d = (cochran_data_t *) &device->data;
+ cochran_config_t *conf = &d->conf;
// Skip to fingerprint to reduce time
d->fp_dive_num = d->dive_count - 1;
- while (d->fp_dive_num >= 0 && memcmp(&(d->fingerprint),
- d->logbook + d->fp_dive_num * d->log_size
- + d->dive_num_ptr,
+ while (d->fp_dive_num >= 0 && memcmp(&d->fingerprint,
+ d->logbook + d->fp_dive_num * conf->log_size
+ + conf->dive_num_ptr,
sizeof(d->fingerprint)))
d->fp_dive_num--;
}
@@ -547,8 +555,9 @@ static void
cochran_get_sample_parms(dc_device_t *abstract)
{
cochran_device_t *device = (cochran_device_t *) abstract;
- cochran_data_t *d = (cochran_data_t *) &(device->data);
- unsigned int pre_dive_offset, end_dive_offset;
+ cochran_data_t *d = (cochran_data_t *) &device->data;
+ cochran_config_t *conf = &d->conf;
+ unsigned int pre_dive_offset = 0, end_dive_offset = 0;
unsigned int low_offset, high_offset;
// Find lowest and highest offsets into sample data
@@ -557,10 +566,10 @@ cochran_get_sample_parms(dc_device_t *abstract)
int i;
for (i = d->fp_dive_num + 1; i < d->dive_count; i++) {
- pre_dive_offset = array_uint32_le (&(d->logbook[i * d->log_size
- + d->log_pre_dive_ptr]));
- end_dive_offset = array_uint32_le (&(d->logbook[i * d->log_size
- + d->log_end_dive_ptr]));
+ pre_dive_offset = array_uint32_le (&(d->logbook[i * conf->log_size
+ + conf->log_pre_dive_ptr]));
+ end_dive_offset = array_uint32_le (&(d->logbook[i * conf->log_size
+ + conf->log_end_dive_ptr]));
// Check for ring buffer wrap-around.
if (pre_dive_offset > end_dive_offset)
@@ -577,8 +586,8 @@ cochran_get_sample_parms(dc_device_t *abstract)
// 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;
- d->sample_memory_end_address = high_offset;
- low_offset = d->sample_memory_start_address;
+ conf->sample_memory_end_address = high_offset;
+ low_offset = conf->sample_memory_start_address;
d->sample_data_offset = low_offset;
d->sample_size = high_offset - low_offset;
} else if (low_offset < 0xFFFFFFFF && high_offset > 0) {
@@ -597,7 +606,7 @@ static dc_status_t
cochran_read_samples(dc_device_t *abstract)
{
cochran_device_t *device = (cochran_device_t *) abstract;
- cochran_data_t *d = (cochran_data_t *) &(device->data);
+ cochran_data_t *d = (cochran_data_t *) &device->data;
dc_status_t rc;
@@ -649,7 +658,8 @@ static dc_status_t
cochran_commander_device_read_all (dc_device_t *abstract)
{
cochran_device_t *device = (cochran_device_t *) abstract;
- cochran_data_t *d = (cochran_data_t *) &(device->data);
+ cochran_data_t *d = (cochran_data_t *) &device->data;
+ cochran_config_t *conf = &d->conf;
dc_status_t rc;
@@ -663,12 +673,12 @@ cochran_commander_device_read_all (dc_device_t *abstract)
return rc;
// Determine size of dive list to read. Round up to nearest 16K
- if (d->dive_count_endian == COCHRAN_LE_TYPE)
- d->dive_count = array_uint16_le (d->config[0] + d->dive_count_ptr);
+ if (conf->dive_count_endian == COCHRAN_LE_TYPE)
+ d->dive_count = array_uint16_le (d->config[0] + conf->dive_count_ptr);
else
- d->dive_count = array_uint16_be (d->config[0] + d->dive_count_ptr);
+ d->dive_count = array_uint16_be (d->config[0] + conf->dive_count_ptr);
- d->logbook_size = ((d->dive_count * d->log_size) & 0xFFFFC000)
+ d->logbook_size = ((d->dive_count * conf->log_size) & 0xFFFFC000)
+ 0x4000;
rc = cochran_read_logbook(abstract);
@@ -696,11 +706,11 @@ dc_status_t
cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *data)
{
cochran_device_t *device = (cochran_device_t *) abstract;
- cochran_data_t *d = (cochran_data_t *) &(device->data);
+ cochran_data_t *d = (cochran_data_t *) &device->data;
int ptr = 0;
dc_status_t rc;
- char *b;
+ unsigned char *b;
int size;
rc = cochran_commander_device_read_all (abstract);
@@ -816,16 +826,42 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *data)
}
+/*
+* 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_guess_sample_end_address(cochran_data_t *data, unsigned int log_num)
+{
+ cochran_config_t *conf = &data->conf;
+ const unsigned char *log = data->logbook + data->conf.log_size * log_num;
+
+ if (log_num == data->dive_count)
+ // Return next usable address from config0 page
+ return array_uint32_le(data->config[0]
+ + conf->sample_memory_end_address);
+
+ // Next log's start address
+ return array_uint32_le(log + conf->log_size + 6);
+}
+
dc_status_t
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 *d = &(device->data);
- unsigned int sample_start_offset, sample_end_offset;
+ cochran_data_t *data = &device->data;
+ cochran_config_t *conf = &data->conf;
+
+ unsigned int sample_start_address, sample_end_address;
dc_status_t rc;
+ unsigned char *log, *fingerprint, *sample, *dive;
+ int sample_size, dive_size;
+
rc = cochran_commander_device_read_all (abstract);
if (rc != DC_STATUS_SUCCESS)
@@ -833,36 +869,54 @@ cochran_commander_device_foreach (dc_device_t *abstract,
// Loop through each dive
int i;
- for (i = d->dive_count - 1; i > d->fp_dive_num; i--) {
+ for (i = data->dive_count - 1; i > data->fp_dive_num; i--) {
+ log = data->logbook + i * conf->log_size;
- d->current_log = d->logbook + i * d->log_size;
+ sample_start_address = array_uint32_le (log + 6);
+ sample_end_address = array_uint32_le (log + conf->log_size / 2);
- sample_start_offset = array_uint32_le (d->current_log + 6);
- sample_end_offset = array_uint32_le (d->current_log
- + d->log_size/2);
+ if (sample_end_address == 0xFFFFFFFF)
+ // Corrupt dive, guess the end address
+ sample_end_address = cochran_guess_sample_end_address(data, i);
- d->current_sample = d->sample + sample_start_offset
- - d->sample_data_offset;
+ sample = data->sample + sample_start_address - data->sample_data_offset;
- // Check for corrupt post-dive section
- if (array_uint32_le(d->current_log + d->log_size/2) == 0xFFFFFFFF)
- d->corrupt_dive = 1;
- else
- d->corrupt_dive = 0;
+ // Determine size of sample
+ sample_size = sample_end_address - sample_start_address;
+ if (sample_size < 0)
+ // Adjust for ring buffer wrap-around
+ sample_size += conf->sample_memory_end_address
+ - conf->sample_memory_start_address;
- // Check for ring buffer wrap
- if (sample_start_offset > sample_end_offset)
- d->current_sample_size = d->sample_memory_end_address
- - sample_start_offset + 1 + sample_end_offset
- - d->sample_memory_start_address + 1;
- else
- d->current_sample_size = sample_end_offset - sample_start_offset;
+ fingerprint = log + conf->dive_num_ptr;
+
+ // Build dive blob
+ dive_size = COCHRAN_MODEL_SIZE + conf->log_size + sample_size;
+ dive = malloc(dive_size);
+ if (dive == NULL)
+ return DC_STATUS_NOMEMORY;
- d->current_fingerprint = d->current_log + d->dive_num_ptr;
+ memcpy(dive, data->id + 0x3B, 8); // model string
+ memcpy(dive + COCHRAN_MODEL_SIZE, log, conf->log_size);
+ if (sample_start_address <= sample_end_address) {
+ memcpy(dive + COCHRAN_MODEL_SIZE + conf->log_size, sample,
+ sample_size);
+ } else {
+ // It wrapped the buffer, copy two sections
+ unsigned int size = conf->sample_memory_end_address
+ - sample_start_address;
+ memcpy(dive + COCHRAN_MODEL_SIZE + conf->log_size, sample, size);
+ memcpy(dive + COCHRAN_MODEL_SIZE + conf->log_size + size,
+ data->sample, sample_size - size);
+ }
- if (callback && !callback ((unsigned char *) d, sizeof(device->data),
- d->current_fingerprint, sizeof (d->fingerprint), userdata))
+ if (callback && !callback ((unsigned char *) dive, dive_size,
+ fingerprint, COCHRAN_FINGERPRINT_SIZE, userdata)) {
+ free(dive);
return DC_STATUS_SUCCESS;
+ }
+
+ free(dive);
}
return DC_STATUS_SUCCESS;
diff --git a/src/cochran_commander.h b/src/cochran_commander.h
index e259977..8fa6228 100644
--- a/src/cochran_commander.h
+++ b/src/cochran_commander.h
@@ -21,7 +21,16 @@
// seconds to add to Cochran time stamps to get unix time
// Equivalent to Jan 1, 1992 00:00:00
-#define COCHRAN_TIMESTAMP_OFFSET 694224000
+#define COCHRAN_TIMESTAMP_OFFSET 694242000
+
+#define COCHRAN_MODEL_SIZE 8
+#define COCHRAN_FINGERPRINT_SIZE 2
+
+#define COCHRAN_EMC_LOG_SIZE 512
+#define COCHRAN_EMC_SAMPLE_SIZE 3
+
+#define COCHRAN_CMDR_LOG_SIZE 256
+#define COCHRAN_CMDR_SAMPLE_SIZE 2
#define COCHRAN_LE_TYPE 0
#define COCHRAN_BE_TYPE 1
@@ -42,9 +51,26 @@ typedef enum cochran_model_t {
COCHRAN_MODEL_COMMANDER_AIR_NITROX,
} cochran_model_t;
-typedef struct cochran_data_t {
+// Device configuration items
+typedef struct cochran_config_t {
cochran_model_t model;
+ int log_size;
+ int sample_memory_start_address;
+ int sample_memory_end_address;
+ int dive_num_ptr;
+ int dive_count_ptr;
+ int dive_count_endian;
+ int sample_end_ptr;
+ int log_pre_dive_ptr;
+ int log_end_dive_ptr;
+ int last_interdive_ptr;
+ int last_entry_ptr;
+ int date_format;
+ int address_length;
+ int high_baud; // baud rate to switch to for log/sample download
+} cochran_config_t;
+typedef struct cochran_data_t {
unsigned char id0[67];
unsigned char id[67];
unsigned char config[4][512];
@@ -56,38 +82,17 @@ typedef struct cochran_data_t {
unsigned int config_count;
unsigned short int dive_count;
- unsigned char fingerprint[2];
+ unsigned char fingerprint[COCHRAN_FINGERPRINT_SIZE];
int fp_dive_num;
unsigned int logbook_size;
- unsigned int current_sample_size;
unsigned int sample_data_offset;
unsigned int sample_size;
unsigned int last_interdive_offset;
unsigned int last_entry_offset;
- unsigned char *current_fingerprint;
- unsigned char *current_log;
- unsigned char *current_sample;
-
- // Config items
- int log_size;
- int sample_memory_start_address;
- int sample_memory_end_address;
- int dive_num_ptr;
- int dive_count_ptr;
- int dive_count_endian;
- int sample_end_ptr;
- int log_pre_dive_ptr;
- int log_end_dive_ptr;
- int last_interdive_ptr;
- int last_entry_ptr;
- int date_format;
- int address_length;
- int high_baud; // baud rate to switch to for log/sample download
-
- unsigned char corrupt_dive;
+ cochran_config_t conf;
} cochran_data_t;
typedef struct cochran_device_t {
@@ -106,7 +111,7 @@ typedef struct cochran_device_t {
#define CMD_DAY 2
#define CMD_MON 5
#define CMD_YEAR 4
-#define CME_START_OFFSET 6 // 4 bytes
+#define CMD_START_OFFSET 6 // 4 bytes
#define CMD_WATER_CONDUCTIVITY 24 // 1 byte, 0=low, 2=high
#define CMD_START_TEMP 45 // 1 byte, F
#define CMD_START_DEPTH 56 // 2 byte, /4=ft
@@ -153,8 +158,8 @@ dc_status_t cochran_commander_device_set_fingerprint (dc_device_t *abstract,
const unsigned char data[], unsigned int size);
dc_status_t cochran_commander_device_read (dc_device_t *abstract,
unsigned int address, unsigned char data[], unsigned int size);
-static dc_status_t cochran_read_id (dc_device_t *abstract);
dc_status_t cochran_commander_device_foreach (dc_device_t *abstract,
dc_dive_callback_t callback, void *userdata);
dc_status_t cochran_commander_device_dump (dc_device_t *abstract,
dc_buffer_t *data);
+void cochran_commander_get_config(const unsigned char *model, cochran_config_t *conf);
diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c
index 547dad4..8db87cf 100644
--- a/src/cochran_commander_parser.c
+++ b/src/cochran_commander_parser.c
@@ -107,10 +107,13 @@ dc_status_t
cochran_commander_parser_get_datetime (dc_parser_t *abstract,
dc_datetime_t *datetime)
{
- cochran_data_t *data = (cochran_data_t *) abstract->data;
- const unsigned char *log = data->current_log;
+ const unsigned char *data = abstract->data;
+ const unsigned char *log = data + COCHRAN_MODEL_SIZE;
+ cochran_config_t conf;
- if (data->date_format == COCHRAN_DATE_FORMAT_SMHDMY) {
+ cochran_commander_get_config(data, &conf);
+
+ if (conf.date_format == COCHRAN_DATE_FORMAT_SMHDMY) {
datetime->second = log[0];
datetime->minute = log[1];
datetime->hour = log[2];
@@ -133,9 +136,11 @@ static dc_status_t
cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
unsigned int flags, void *value)
{
- cochran_data_t *data = (cochran_data_t *) abstract->data;
+ cochran_config_t conf;
+
+ cochran_commander_get_config(abstract->data, &conf);
- switch (data->model & 0xFF0000)
+ switch (conf.model & 0xFF0000)
{
case COCHRAN_MODEL_COMMANDER_FAMILY:
return cochran_cmdr_parser_get_field(abstract, type, flags, value);
@@ -144,15 +149,19 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
return cochran_emc_parser_get_field(abstract, type, flags, value);
break;
}
+
+ return DC_STATUS_UNSUPPORTED;
}
static dc_status_t
cochran_commander_parser_samples_foreach (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata)
{
- cochran_data_t *data = (cochran_data_t *) abstract->data;
+ cochran_config_t conf;
- switch (data->model & 0xFF0000)
+ cochran_commander_get_config(abstract->data, &conf);
+
+ switch (conf.model & 0xFF0000)
{
case COCHRAN_MODEL_COMMANDER_FAMILY:
return cochran_cmdr_parser_samples_foreach(abstract, callback,
@@ -163,29 +172,37 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract,
userdata);
break;
}
+
+ return DC_STATUS_UNSUPPORTED;
}
+void
+cochran_commander_get_event_info(const unsigned char code,
+ cochran_events_t *event)
+{
+ cochran_events_t *events = cochran_events;
+ unsigned char i = 0;
+
+ while (events[i].code && events[i].code != code)
+ i++;
+ *event = events[i];
+}
int
cochran_commander_handle_event (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata, unsigned char code,
unsigned int offset, unsigned int time)
{
- cochran_data_t *data = (cochran_data_t *) abstract->data;
- cochran_events_t *e = cochran_events;
-
dc_sample_value_t sample = {0};
+ cochran_events_t event;
- unsigned char event_ptr = 0;
-
- while (e[event_ptr].code && e[event_ptr].code != code)
- event_ptr++;
+ cochran_commander_get_event_info(code, &event);
sample.event.time = 0;
- if (e[event_ptr].code) {
- switch (e[event_ptr].code)
+ if (event.code) {
+ switch (code)
{
case 0xAB: // Ceiling decrease
// Indicated to lower ceiling by 10 ft (deeper)
@@ -199,9 +216,9 @@ cochran_commander_handle_event (dc_parser_t *abstract,
break;
default:
// Don't send known events of type NONE
- if (! e[event_ptr].type == SAMPLE_EVENT_NONE) {
- sample.event.type = e[event_ptr].type;
- sample.event.flags = e[event_ptr].flag;
+ if (! event.type == SAMPLE_EVENT_NONE) {
+ sample.event.type = event.type;
+ sample.event.flags = event.flag;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
}
}
@@ -213,5 +230,5 @@ cochran_commander_handle_event (dc_parser_t *abstract,
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
}
- return e[event_ptr].data_bytes;
+ return event.data_bytes;
}
diff --git a/src/cochran_commander_parser.h b/src/cochran_commander_parser.h
index 314d538..44340b5 100644
--- a/src/cochran_commander_parser.h
+++ b/src/cochran_commander_parser.h
@@ -97,6 +97,8 @@ dc_status_t cochran_commander_parser_set_data (dc_parser_t *abstract,
const unsigned char *data, unsigned int size);
dc_status_t cochran_commander_parser_get_datetime (dc_parser_t *abstract,
dc_datetime_t *datetime);
+void cochran_commander_get_event_info(const unsigned char code,
+ cochran_events_t *event);
int cochran_commander_handle_event (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata, unsigned char code,
unsigned int offset, unsigned int time);
diff --git a/src/cochran_emc_parser.c b/src/cochran_emc_parser.c
index 8a554cb..5b4af81 100644
--- a/src/cochran_emc_parser.c
+++ b/src/cochran_emc_parser.c
@@ -52,13 +52,15 @@ dc_status_t
cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
unsigned int flags, void *value)
{
- cochran_data_t *data = (cochran_data_t *) abstract->data;
- unsigned char *log = data->current_log;
+ const unsigned char *log = abstract->data + COCHRAN_MODEL_SIZE;
+ unsigned char corrupt_dive = 0;
+
+ if (array_uint32_le(log + COCHRAN_EMC_LOG_SIZE / 2) == 0xFFFFFFFF)
+ corrupt_dive = 1;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value;
- unsigned int dive_time;
struct dive_stats stats;
if (value) {
@@ -67,28 +69,30 @@ cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
*((unsigned int*) value) = ((float) log[EMC_START_TEMP] - 32) / 1.8;
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
- if (data->corrupt_dive) {
+ if (corrupt_dive) {
cochran_emc_parse_dive_stats(abstract, &stats);
*((unsigned int*) value) = stats.min_temp;
} else
- *((unsigned int*) value) = ((float) log[EMC_MIN_TEMP] / 2 + 20 - 32) / 1.8;
+ *((unsigned int*) value) = ((float) log[EMC_MIN_TEMP] / 2
+ + 20 - 32) / 1.8;
break;
case DC_FIELD_TEMPERATURE_MAXIMUM:
- if (data->corrupt_dive) {
+ if (corrupt_dive) {
cochran_emc_parse_dive_stats(abstract, &stats);
*((unsigned int*) value) = stats.max_temp;
} else
- *((unsigned int*) value) = ((float) log[EMC_MAX_TEMP] / 2 + 20 - 32) / 1.8;
+ *((unsigned int*) value) = ((float) log[EMC_MAX_TEMP] / 2
+ + 20 - 32) / 1.8;
break;
case DC_FIELD_DIVETIME:
- if (data->corrupt_dive) {
+ if (corrupt_dive) {
cochran_emc_parse_dive_stats(abstract, &stats);
*((unsigned int*) value) = stats.dive_time;
} else
*((unsigned int *) value) = array_uint16_le (log + EMC_BT) * 60;
break;
case DC_FIELD_MAXDEPTH:
- if (data->corrupt_dive) {
+ if (corrupt_dive) {
cochran_emc_parse_dive_stats(abstract, &stats);
*((unsigned int*) value) = stats.max_depth;
} else
@@ -96,7 +100,7 @@ cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
* FEET;
break;
case DC_FIELD_AVGDEPTH:
- if (data->corrupt_dive) {
+ if (corrupt_dive) {
cochran_emc_parse_dive_stats(abstract, &stats);
*((unsigned int*) value) = stats.avg_depth;
} else
@@ -107,16 +111,16 @@ cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
*((unsigned int *) value) = 10;
break;
case DC_FIELD_GASMIX:
- gasmix->oxygen = (double) array_uint16_le ((char *) log
+ gasmix->oxygen = (double) array_uint16_le (log
+ EMC_O2_PERCENT + 2 * flags) / 256 / 100;
- gasmix->helium = (double) array_uint16_le ((char *) log
+ gasmix->helium = (double) array_uint16_le (log
+ EMC_HE_PERCENT + 2 * flags) / 256 / 100;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_SALINITY:
// 0 = low conductivity, 2 = high, maybe there's a 1?
- water->type = ( (log[EMC_WATER_CONDUCTIVITY] & 0x3) == 0 ? DC_WATER_FRESH
- : DC_WATER_SALT );
+ water->type = ( (log[EMC_WATER_CONDUCTIVITY] & 0x3) == 0
+ ? DC_WATER_FRESH : DC_WATER_SALT );
water->density = 1000 + 12.5 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
break;
case DC_FIELD_ATMOSPHERIC:
@@ -131,54 +135,17 @@ cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
return DC_STATUS_SUCCESS;
}
-/*
-* 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_emc_guess_sample_size(cochran_data_t *data)
-{
- unsigned int size = 0;
- unsigned int offset, next_offset, memory_start_offset, memory_end_offset;
-
- // Get memory boundries
- memory_start_offset = array_uint32_le(data->config[0]
- + data->sample_memory_start_address);
- memory_end_offset = array_uint32_le(data->config[0]
- + data->sample_memory_end_address);
-
- // see if there is a next sample
- if (data->current_log - data->logbook + 512 >= data->logbook_size) {
- // we are the last log entry, use maximum data
- size = memory_end_offset - offset + 1;
- } else {
- offset = array_uint32_le(data->current_log + 6);
- next_offset = array_uint32_le(data->current_log + 512 + 30);
-
- if (next_offset >= offset) {
- size = next_offset - offset + 1;
- } else {
- // samples wrap to begin of memory
- size = memory_end_offset - offset + next_offset
- - memory_start_offset + 2;
- }
- }
-
- return (size);
-}
-
-
dc_status_t
cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata)
{
- cochran_data_t *data = (cochran_data_t *) abstract->data;
- unsigned char *sdata = data->current_sample;
- unsigned char *log = data->current_log;
- unsigned int size = data->current_sample_size;
- unsigned char *s;
+ const unsigned char *data = abstract->data;
+ const unsigned char *log = data + COCHRAN_MODEL_SIZE;
+ const unsigned char *samples = log + COCHRAN_EMC_LOG_SIZE;
+ unsigned int size = abstract->size - COCHRAN_MODEL_SIZE
+ - COCHRAN_EMC_LOG_SIZE;
+ const unsigned char *s;
dc_sample_value_t sample = {0}, empty_sample = {0};
unsigned int time = 0, last_sample_time;
@@ -188,18 +155,18 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
double ascent_rate;
unsigned char deco_obligation = 0;
unsigned int deco_ceiling = 0;
- unsigned int deco_time = 0;
+ unsigned int deco_time;
+ unsigned char corrupt_dive = 0;
- if (data->corrupt_dive) {
- // Size of sample data is wrong, make a guess
- size = cochran_emc_guess_sample_size(data);
- }
+
+ if (array_uint32_le(log + COCHRAN_EMC_LOG_SIZE / 2) == 0xFFFFFFFF)
+ corrupt_dive = 1;
/*
* Cochran samples depth every second and varies between ascent rate
* and temp ever other second.
* In the 21st, 22nd, 23rd, 24th samples are NDL remaining,
- *deco time and ceiling
+ * deco time and ceiling
*/
// Prime with values from the dive log section
@@ -224,10 +191,10 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
callback (DC_SAMPLE_TIME, sample, userdata);
}
- s = sdata + offset;
+ s = samples + offset;
// If corrupt_dive end before offset
- if (data->corrupt_dive) {
+ if (corrupt_dive) {
if ( s[0] == 0x10
|| s[0] == 0xFF
|| s[0] == 0xA8) {
@@ -239,8 +206,8 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
// Check for event
if (s[0] & 0x80) {
- offset += cochran_commander_handle_event(abstract, callback, userdata,
- s[0], offset, time);
+ offset += cochran_commander_handle_event(abstract, callback,
+ userdata, s[0], offset, time);
switch (s[0])
{
case 0xC5: // Deco obligation begins
@@ -262,7 +229,7 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET;
- deco_time = (array_uint16_le(s + 3) + 1) * 60;
+ sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
if (callback) callback(DC_SAMPLE_DECO, sample, userdata);
break;
}
@@ -270,7 +237,7 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
continue;
}
-
+
// Depth is logged as change in feet, bit 0x40 means negative depth
depth += (float) (s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
sample.depth = depth * FEET;
@@ -290,6 +257,17 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
}
+
+ // Some samples are split over two seconds
+ // Skip over event bytes to find the next sample
+ const unsigned char *n = s + COCHRAN_EMC_SAMPLE_SIZE;
+ cochran_events_t event;
+
+ while ((*n & 0x80) && n < s + size) {
+ cochran_commander_get_event_info(*n, &event);
+ n += event.data_bytes;
+ }
+
// Tissue load is in 20 samples, then 2 samples for RBT,
// and 2 for ceiling
switch (time % 24)
@@ -297,13 +275,14 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
case 20:
if (deco_obligation) {
/* Deco time for deepest stop, unused */
- deco_time = (s[2] + s[5] * 256 + 1) * 60;
+ deco_time = (s[2] + n[2] * 256 + 1) * 60;
} else {
/* Get RBT */
- sample.rbt = s[2] + s[5] * 256 + 1;
+ sample.rbt = s[2] + n[2] * 256 + 1;
if (callback) callback (DC_SAMPLE_RBT, sample, userdata);
/* Send deco NDL sample */
- sample.deco.time = sample.rbt * 60; // seconds
+ sample.deco.type = DC_DECO_NDL;
+ sample.deco.time = (s[2] + n[2] * 256 + 1) * 60; // seconds
sample.deco.depth = 0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
}
@@ -313,14 +292,14 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
if (deco_obligation) {
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET;
- sample.deco.time = (s[2] + s[5] * 256 + 1) * 60; // minutes
+ sample.deco.time = (s[2] + n[2] * 256 + 1) * 60; // minutes
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
}
break;
}
time ++;
- offset += 3;
+ offset += COCHRAN_EMC_SAMPLE_SIZE;
}
return DC_STATUS_SUCCESS;
@@ -329,51 +308,50 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
void cochran_emc_parse_dive_stats (dc_parser_t *abstract,
struct dive_stats *stats)
{
- cochran_data_t *data = (cochran_data_t *) abstract->data;
- unsigned char *sdata = data->current_sample;
- unsigned char *log = data->current_log;
- unsigned int size = data->current_sample_size;
- unsigned char *s;
+ const unsigned char *log = abstract->data + COCHRAN_MODEL_SIZE;
+ const unsigned char *samples = log + COCHRAN_EMC_LOG_SIZE;
+ unsigned int size = abstract->size - COCHRAN_MODEL_SIZE
+ - COCHRAN_EMC_LOG_SIZE;
+ const unsigned char *s;
+
unsigned int offset = 0;
- cochran_events_t *e = cochran_events;
- unsigned char event_ptr;
float depth_m = 0, depth, temp;
unsigned int time = 0;
+ unsigned char corrupt_dive = 0;
- // Check for corrupt dive
- if (data->corrupt_dive)
- {
- // It's corrupt, guess at the sample size
- size = cochran_emc_guess_sample_size(data);
- }
+ if (array_uint32_le(log + COCHRAN_EMC_LOG_SIZE / 2) == 0xFFFFFFFF)
+ corrupt_dive = 1;
+
+ // Prime with values from the dive log section
+ depth = array_uint16_le (log + EMC_START_DEPTH) / 256;
- stats->max_depth = 0;
+ stats->max_depth = depth;
stats->min_temp = 999;
stats->avg_depth = 0;
stats->max_temp = 0;
while (offset < size) {
- s = sdata + offset;
+ s = samples + offset;
- if ( s[0] == 0x10
- || s[0] == 0xFF
- || s[0] == 0xA8) {
+ if (corrupt_dive) {
+ if (s[0] == 0x10
+ || s[0] == 0xFF
+ || s[0] == 0xA8) {
// End corrupted dive
break;
+ }
+ if (time > 1 && (s[0] == 0xE3 || s[0] == 0xF3))
+ break;
}
- if (time > 1 && (s[0] == 0xE3 || s[0] == 0xF3))
- break;
-
// Check for event
if (s[0] & 0x80) {
- event_ptr = 0;
- while (e[event_ptr].code && e[event_ptr].code != s[0])
- event_ptr++;
+ cochran_events_t event;
+ cochran_commander_get_event_info(s[0], &event);
// Advance some bytes
- if (e[event_ptr].code)
- offset += e[event_ptr].data_bytes;
+ if (event.code)
+ offset += event.data_bytes;
else
offset ++;
@@ -390,7 +368,7 @@ void cochran_emc_parse_dive_stats (dc_parser_t *abstract,
if (time % 2 != 0) {
// Temperature logged in half degrees F above 20
- temp = s[1] / 2 + 20;
+ temp = (float) s[1] / 2 + 20;
temp = (temp - 32.0) / 1.8;
if (temp < stats->min_temp) stats->min_temp = temp;
@@ -398,7 +376,7 @@ void cochran_emc_parse_dive_stats (dc_parser_t *abstract,
}
time ++;
- offset += 3;
+ offset += COCHRAN_EMC_SAMPLE_SIZE;
}
stats->dive_time = time - 1;
--
1.8.3.1
2
11
Hi.
I know that Jef cares about msvc compatibility, and thus we are jumping
hoops to try to keep libdivecomputer building with old msvc versions.
As far as i read from the project file, its targeting msvc 9.0 aka
VS 2008 which is quite old. I just had a chat with some friends of mine
who works (fights) with VS on a daily basis, and according to them VS
2013 works quite fine, and is a pretty standard compliant c99 compiler.
Is it intentionally libdivecomputer is targeting msvc 9.0 or can we bump
that version to msvc 12.0 and stop (at least to lesser degree) jumping
hoops for msvc support?
//Anton
--
Anton Lundin +46702-161604
1
0
On Mon, Dec 22, 2014 at 2:48 PM, Jef Driesen <jef(a)libdivecomputer.org>
wrote:
> On 22-12-14 20:33, John Van Ostrand wrote:
>
>> On Mon, Dec 22, 2014 at 10:38 AM, Jef Driesen <jef(a)libdivecomputer.org>
>> wrote:
>>
>> On 19-12-14 21:23, John Van Ostrand wrote:
>>>
>>> I did more investigating of setting commands and encoding. I've updated
>>>> the
>>>> trac wiki with the full information. It should be complete but it's not
>>>> tested or strongly verified. There may be mistakes.
>>>>
>>>> I did some testing today reading raw data to see if I could download a
>>>> usable memory dump for the dump function and avoid all that work done in
>>>> _dump and eliminate the need for cochran_download.c.
>>>>
>>>> The read command (0x15 <start> <bytes> 0x05) only reads logbook and
>>>> sample
>>>> data. I suspected that the gap between logbook and sample contained
>>>> config
>>>> or other data but that isn't the case. There is no gap between Commander
>>>> logbook and samples and the EMC has no data of significance there.
>>>>
>>>> Also, when asked to read past the end of data on the Commander, It
>>>> returns
>>>> 0xFF for data that doesn't seem to be there. For example, the Commander
>>>> I
>>>> have has rolled the sample data buffer so all the memory has been
>>>> written.
>>>> Sample data stops at 0x100000 (1MB). When I asked for 8MB of data I got
>>>> 7MB
>>>> of 0xFF.
>>>>
>>>> Unless i discover a new command to read data I won't be able to deliver
>>>> a
>>>> raw memory dump.
>>>>
>>>> I'm also no closer to deciphering the feature data from the computer so
>>>> I
>>>> don't know how much memory is present and enabled.
>>>>
>>>>
>>> I'm not sure I'm understand what you're saying here. First you say you
>>> can
>>> download 1MB of data. But then you say you can't download a memory dump?
>>> I
>>> don't know what you consider a memory dump, but I would say it's that 1MB
>>> of data! Combined with the id, misc and config blobs that has everything
>>> we
>>> need. I have no idea what else you want.
>>>
>>>
>> What I meant was that I could not produce a contiguous segment of memory
>> that contained everything, which seemed to be what you thought would be
>> ideal for _dump().
>>
>
> Now I'm even more confused. If I'm right, then for the commander the
> logbook ringbuffer is from address 0x0 to 0x20000. And the profile
> ringbuffer is from address 0x20000 to 0x100000. So that's one continuous
> block of 1M in size. That would be perfect for a memory dump.
>
> Am I missing something here?
>
When I say "everything" I mean info, config and misc data too. That can't
be read the same way log and sample data is.
>
> PS: Can you send me the data from your Commander too. If you have data
>>> from the other models, I would like to have a look at that too.
>>>
>>>
>> I'll send a separate email.
>>
>
> Thanks.
>
> If you're interested in the source code for my libdivecomputer simulator,
> then let me know and I'll package it for you.
>
>
I would be interested.
> Is it possible to install the Analyst software on my side, and make it
> talk to the simulator, or load the corresponding CAN files? That would help
> a lot. But I can only find a demo version, which seems to have the
> communication disabled
The software is an extra charge (with cable) and I recall but it seemed to
call home to authorize or log an activation.
Do you want screen dumps? I can also run a USB log and export to CSV. The
file would include lots of collateral data but it's parseable. The CAN
files are weaklly encrypted and I finished and submitted the Subsurface
code to read CAN files. The CAN file contains log entries followed by the
dive sample data in raw format. Since the addresses in the log don't apply
to the relocated sample data my code uses something similar to corrupt dive
parsing.
--
John Van Ostrand
At large on sabbatical
2
1
25 Dec '14
The emc14 sample memory start address had been set to 0x94000 and should
be 0x20000.
---
src/cochran_commander.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cochran_commander.c b/src/cochran_commander.c
index 895c859..a1be22f 100644
--- a/src/cochran_commander.c
+++ b/src/cochran_commander.c
@@ -382,7 +382,7 @@ cochran_commander_get_config (const unsigned char *model,
{
conf->model = COCHRAN_MODEL_EMC_14;
conf->log_size = 512;
- conf->sample_memory_start_address = 0x94000;
+ conf->sample_memory_start_address = 0x20000;
conf->dive_num_ptr = 0x56;
conf->dive_count_ptr = 0xD2;
conf->dive_count_endian = COCHRAN_LE_TYPE;
--
1.9.3
1
1
Hi Jef,
I have been looking for a port of libdivecomputer for C#. I'm very interested in the wrapper you have created (or were working on), is there a location from where I can download it? In the ideal case I would like to port the entire library to C#, but for now that might be a bit ambitious.
Thanks,
Vincent
4
4
On 2014-12-17 23:11, Anton Lundin wrote:
> +dc_status_t
> +hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename)
> +{
> + [...]
> +
> + // Make sure the device is in service mode
> + if (device->state == OPEN) {
> + rc = hw_ostc3_device_init_service(abstract);
> + if (rc != DC_STATUS_SUCCESS) {
> + free (firmware);
> + return rc;
> + }
> + } else if (device->state != SERVICE) {
> + free (firmware);
> + return DC_STATUS_INVALIDARGS;
> + }
I've been playing with the idea of merging this functionality into the
hw_ostc3_check_state_or_init() function. This is what I came up with:
static dc_status_t
hw_ostc3_device_init (hw_ostc3_device_t *device, hw_ostc3_state_t state)
{
dc_status_t rc = DC_STATUS_SUCCESS;
if (device->state == state) {
// No change.
rc = DC_STATUS_SUCCESS;
} else if (device->state == OPEN) {
// Change to download/or service mode.
if (state == DOWNLOAD) {
rc = hw_ostc3_device_init_download(device);
} else if (state == SERVICE) {
rc = hw_ostc3_device_init_service(device);
} else {
rc = DC_STATUS_INVALIDARGS;
}
} else if (device->state == SERVICE && state == DOWNLOAD) {
// Switching between service and download mode is not possible.
// But in service mode, all download commands are supported too,
// so there is no need to change the state.
rc = DC_STATUS_SUCCESS;
} else {
// Not supported.
rc = DC_STATUS_INVALIDARGS;
}
return rc;
}
What do you think? From my point of view, the main advantage is that all
state changes are now done in a single function, where it's easy to see
what kind of state transitions are allowed or not.
> + // Programing done!
> + progress.current++;
> + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
> +
> + // Finished!
> + return DC_STATUS_SUCCESS;
> +}
There is a memory leak here. You forgot to free the firmware structure.
PS: I have already implemented most of the improvements and bugfixes I
mentioned in this and the 3 other emails. So there is no need to spend
any time on them. If you're fine with those changes, just tell me, and
I'll just squash them onto your commits.
I fixed a few style issues too (e.g. whitespace, signed vs unsigned int,
and dc_device_t vs hw_ostc3_device_t in function parameters).
Jef
2
4
This imports Tiny AES128 from https://github.com/kokke/tiny-AES128-C for
use in the decoding of OSTC3 firmwares.
This aes-code is released into the public domain.
Signed-off-by: Anton Lundin <glance(a)acc.umu.se>
---
msvc/libdivecomputer.vcproj | 6 +
src/Makefile.am | 1 +
src/aes.c | 583 ++++++++++++++++++++++++++++++++++++++++++++
src/aes.h | 40 +++
4 files changed, 630 insertions(+)
create mode 100644 src/aes.c
create mode 100644 src/aes.h
diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj
index 2fea2b9..c8a2a8b 100644
--- a/msvc/libdivecomputer.vcproj
+++ b/msvc/libdivecomputer.vcproj
@@ -255,6 +255,9 @@
>
</File>
<File
+ RelativePath="..\src\aes.c"
+ >
+ <File
RelativePath="..\src\hw_ostc3.c"
>
</File>
@@ -561,6 +564,9 @@
>
</File>
<File
+ RelativePath="..\src\aes.h"
+ >
+ <File
RelativePath="..\include\libdivecomputer\hw_ostc3.h"
>
</File>
diff --git a/src/Makefile.am b/src/Makefile.am
index 1f52841..821bc1b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -44,6 +44,7 @@ libdivecomputer_la_SOURCES = \
ihex.h ihex.c \
hw_ostc.c hw_ostc_parser.c \
hw_frog.c \
+ aes.h aes.c \
hw_ostc3.c \
cressi_edy.c cressi_edy_parser.c \
cressi_leonardo.c cressi_leonardo_parser.c \
diff --git a/src/aes.c b/src/aes.c
new file mode 100644
index 0000000..4c7645f
--- /dev/null
+++ b/src/aes.c
@@ -0,0 +1,583 @@
+/*
+
+This is an implementation of the AES128 algorithm, specifically ECB and CBC mode.
+
+The implementation is verified against the test vectors in:
+ National Institute of Standards and Technology Special Publication 800-38A 2001 ED
+
+ECB-AES128
+----------
+
+ plain-text:
+ 6bc1bee22e409f96e93d7e117393172a
+ ae2d8a571e03ac9c9eb76fac45af8e51
+ 30c81c46a35ce411e5fbc1191a0a52ef
+ f69f2445df4f9b17ad2b417be66c3710
+
+ key:
+ 2b7e151628aed2a6abf7158809cf4f3c
+
+ resulting cipher
+ 3ad77bb40d7a3660a89ecaf32466ef97
+ f5d3d58503b9699de785895a96fdbaaf
+ 43b1cd7f598ece23881b00e3ed030688
+ 7b0c785e27e8ad3f8223207104725dd4
+
+
+NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
+ You should pad the end of the string with zeros if this is not the case.
+
+*/
+
+
+/*****************************************************************************/
+/* Includes: */
+/*****************************************************************************/
+#include <stdint.h>
+#include <string.h> // CBC mode, for memset
+#include "aes.h"
+
+
+/*****************************************************************************/
+/* Defines: */
+/*****************************************************************************/
+// The number of columns comprising a state in AES. This is a constant in AES. Value=4
+#define Nb 4
+// The number of 32 bit words in a key.
+#define Nk 4
+// Key length in bytes [128 bit]
+#define KEYLEN 16
+// The number of rounds in AES Cipher.
+#define Nr 10
+
+// jcallan@github points out that declaring Multiply as a function
+// reduces code size considerably with the Keil ARM compiler.
+// See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3
+#ifndef MULTIPLY_AS_A_FUNCTION
+ #define MULTIPLY_AS_A_FUNCTION 0
+#endif
+
+
+/*****************************************************************************/
+/* Private variables: */
+/*****************************************************************************/
+// state - array holding the intermediate results during decryption.
+typedef uint8_t state_t[4][4];
+static state_t* state;
+
+// The array that stores the round keys.
+static uint8_t RoundKey[176];
+
+// The Key input to the AES Program
+static const uint8_t* Key;
+
+#if defined(CBC) && CBC
+ // Initial Vector used only for CBC mode
+ static uint8_t* Iv;
+#endif
+
+// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
+// The numbers below can be computed dynamically trading ROM for RAM -
+// This can be useful in (embedded) bootloader applications, where ROM is often limited.
+static const uint8_t sbox[256] = {
+ //0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
+
+static const uint8_t rsbox[256] =
+{ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
+
+
+// The round constant word array, Rcon[i], contains the values given by
+// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+// Note that i starts at 1, not 0).
+static const uint8_t Rcon[255] = {
+ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
+ 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
+ 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
+ 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
+ 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
+ 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
+ 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
+ 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
+ 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
+ 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
+ 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
+ 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
+ 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
+ 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
+ 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb };
+
+
+/*****************************************************************************/
+/* Private functions: */
+/*****************************************************************************/
+static uint8_t getSBoxValue(uint8_t num)
+{
+ return sbox[num];
+}
+
+static uint8_t getSBoxInvert(uint8_t num)
+{
+ return rsbox[num];
+}
+
+// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
+static void KeyExpansion(void)
+{
+ uint32_t i, j, k;
+ uint8_t tempa[4]; // Used for the column/row operations
+
+ // The first round key is the key itself.
+ for(i = 0; i < Nk; ++i)
+ {
+ RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
+ RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
+ RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
+ RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
+ }
+
+ // All other round keys are found from the previous round keys.
+ for(; (i < (Nb * (Nr + 1))); ++i)
+ {
+ for(j = 0; j < 4; ++j)
+ {
+ tempa[j]=RoundKey[(i-1) * 4 + j];
+ }
+ if (i % Nk == 0)
+ {
+ // This function rotates the 4 bytes in a word to the left once.
+ // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+
+ // Function RotWord()
+ {
+ k = tempa[0];
+ tempa[0] = tempa[1];
+ tempa[1] = tempa[2];
+ tempa[2] = tempa[3];
+ tempa[3] = k;
+ }
+
+ // SubWord() is a function that takes a four-byte input word and
+ // applies the S-box to each of the four bytes to produce an output word.
+
+ // Function Subword()
+ {
+ tempa[0] = getSBoxValue(tempa[0]);
+ tempa[1] = getSBoxValue(tempa[1]);
+ tempa[2] = getSBoxValue(tempa[2]);
+ tempa[3] = getSBoxValue(tempa[3]);
+ }
+
+ tempa[0] = tempa[0] ^ Rcon[i/Nk];
+ }
+ else if (Nk > 6 && i % Nk == 4)
+ {
+ // Function Subword()
+ {
+ tempa[0] = getSBoxValue(tempa[0]);
+ tempa[1] = getSBoxValue(tempa[1]);
+ tempa[2] = getSBoxValue(tempa[2]);
+ tempa[3] = getSBoxValue(tempa[3]);
+ }
+ }
+ RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0];
+ RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1];
+ RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2];
+ RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3];
+ }
+}
+
+// This function adds the round key to state.
+// The round key is added to the state by an XOR function.
+static void AddRoundKey(uint8_t round)
+{
+ uint8_t i,j;
+ for(i=0;i<4;++i)
+ {
+ for(j = 0; j < 4; ++j)
+ {
+ (*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j];
+ }
+ }
+}
+
+// The SubBytes Function Substitutes the values in the
+// state matrix with values in an S-box.
+static void SubBytes(void)
+{
+ uint8_t i, j;
+ for(i = 0; i < 4; ++i)
+ {
+ for(j = 0; j < 4; ++j)
+ {
+ (*state)[j][i] = getSBoxValue((*state)[j][i]);
+ }
+ }
+}
+
+// The ShiftRows() function shifts the rows in the state to the left.
+// Each row is shifted with different offset.
+// Offset = Row number. So the first row is not shifted.
+static void ShiftRows(void)
+{
+ uint8_t temp;
+
+ // Rotate first row 1 columns to left
+ temp = (*state)[0][1];
+ (*state)[0][1] = (*state)[1][1];
+ (*state)[1][1] = (*state)[2][1];
+ (*state)[2][1] = (*state)[3][1];
+ (*state)[3][1] = temp;
+
+ // Rotate second row 2 columns to left
+ temp = (*state)[0][2];
+ (*state)[0][2] = (*state)[2][2];
+ (*state)[2][2] = temp;
+
+ temp = (*state)[1][2];
+ (*state)[1][2] = (*state)[3][2];
+ (*state)[3][2] = temp;
+
+ // Rotate third row 3 columns to left
+ temp = (*state)[0][3];
+ (*state)[0][3] = (*state)[3][3];
+ (*state)[3][3] = (*state)[2][3];
+ (*state)[2][3] = (*state)[1][3];
+ (*state)[1][3] = temp;
+}
+
+static uint8_t xtime(uint8_t x)
+{
+ return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
+}
+
+// MixColumns function mixes the columns of the state matrix
+static void MixColumns(void)
+{
+ uint8_t i;
+ uint8_t Tmp,Tm,t;
+ for(i = 0; i < 4; ++i)
+ {
+ t = (*state)[i][0];
+ Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
+ Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
+ Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
+ Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
+ Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
+ }
+}
+
+// Multiply is used to multiply numbers in the field GF(2^8)
+#if MULTIPLY_AS_A_FUNCTION
+static uint8_t Multiply(uint8_t x, uint8_t y)
+{
+ return (((y & 1) * x) ^
+ ((y>>1 & 1) * xtime(x)) ^
+ ((y>>2 & 1) * xtime(xtime(x))) ^
+ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
+ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))));
+ }
+#else
+#define Multiply(x, y) \
+ ( ((y & 1) * x) ^ \
+ ((y>>1 & 1) * xtime(x)) ^ \
+ ((y>>2 & 1) * xtime(xtime(x))) ^ \
+ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
+ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
+
+#endif
+
+// MixColumns function mixes the columns of the state matrix.
+// The method used to multiply may be difficult to understand for the inexperienced.
+// Please use the references to gain more information.
+static void InvMixColumns(void)
+{
+ int i;
+ uint8_t a,b,c,d;
+ for(i=0;i<4;++i)
+ {
+ a = (*state)[i][0];
+ b = (*state)[i][1];
+ c = (*state)[i][2];
+ d = (*state)[i][3];
+
+ (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
+ (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
+ (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
+ (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
+ }
+}
+
+
+// The SubBytes Function Substitutes the values in the
+// state matrix with values in an S-box.
+static void InvSubBytes(void)
+{
+ uint8_t i,j;
+ for(i=0;i<4;++i)
+ {
+ for(j=0;j<4;++j)
+ {
+ (*state)[j][i] = getSBoxInvert((*state)[j][i]);
+ }
+ }
+}
+
+static void InvShiftRows(void)
+{
+ uint8_t temp;
+
+ // Rotate first row 1 columns to right
+ temp=(*state)[3][1];
+ (*state)[3][1]=(*state)[2][1];
+ (*state)[2][1]=(*state)[1][1];
+ (*state)[1][1]=(*state)[0][1];
+ (*state)[0][1]=temp;
+
+ // Rotate second row 2 columns to right
+ temp=(*state)[0][2];
+ (*state)[0][2]=(*state)[2][2];
+ (*state)[2][2]=temp;
+
+ temp=(*state)[1][2];
+ (*state)[1][2]=(*state)[3][2];
+ (*state)[3][2]=temp;
+
+ // Rotate third row 3 columns to right
+ temp=(*state)[0][3];
+ (*state)[0][3]=(*state)[1][3];
+ (*state)[1][3]=(*state)[2][3];
+ (*state)[2][3]=(*state)[3][3];
+ (*state)[3][3]=temp;
+}
+
+
+// Cipher is the main function that encrypts the PlainText.
+static void Cipher(void)
+{
+ uint8_t round = 0;
+
+ // Add the First round key to the state before starting the rounds.
+ AddRoundKey(0);
+
+ // There will be Nr rounds.
+ // The first Nr-1 rounds are identical.
+ // These Nr-1 rounds are executed in the loop below.
+ for(round = 1; round < Nr; ++round)
+ {
+ SubBytes();
+ ShiftRows();
+ MixColumns();
+ AddRoundKey(round);
+ }
+
+ // The last round is given below.
+ // The MixColumns function is not here in the last round.
+ SubBytes();
+ ShiftRows();
+ AddRoundKey(Nr);
+}
+
+static void InvCipher(void)
+{
+ uint8_t round=0;
+
+ // Add the First round key to the state before starting the rounds.
+ AddRoundKey(Nr);
+
+ // There will be Nr rounds.
+ // The first Nr-1 rounds are identical.
+ // These Nr-1 rounds are executed in the loop below.
+ for(round=Nr-1;round>0;round--)
+ {
+ InvShiftRows();
+ InvSubBytes();
+ AddRoundKey(round);
+ InvMixColumns();
+ }
+
+ // The last round is given below.
+ // The MixColumns function is not here in the last round.
+ InvShiftRows();
+ InvSubBytes();
+ AddRoundKey(0);
+}
+
+static void BlockCopy(uint8_t* output, uint8_t* input)
+{
+ uint8_t i;
+ for (i=0;i<KEYLEN;++i)
+ {
+ output[i] = input[i];
+ }
+}
+
+
+
+/*****************************************************************************/
+/* Public functions: */
+/*****************************************************************************/
+#if defined(ECB) && ECB
+
+
+void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t* output)
+{
+ // Copy input to output, and work in-memory on output
+ BlockCopy(output, input);
+ state = (state_t*)output;
+
+ Key = key;
+ KeyExpansion();
+
+ // The next function call encrypts the PlainText with the Key using AES algorithm.
+ Cipher();
+}
+
+void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output)
+{
+ // Copy input to output, and work in-memory on output
+ BlockCopy(output, input);
+ state = (state_t*)output;
+
+ // The KeyExpansion routine must be called before encryption.
+ Key = key;
+ KeyExpansion();
+
+ InvCipher();
+}
+
+
+#endif // #if defined(ECB) && ECB
+
+
+
+
+
+#if defined(CBC) && CBC
+
+
+static void XorWithIv(uint8_t* buf)
+{
+ uint8_t i;
+ for(i = 0; i < KEYLEN; ++i)
+ {
+ buf[i] ^= Iv[i];
+ }
+}
+
+void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
+{
+ intptr_t i;
+ uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */
+
+ BlockCopy(output, input);
+ state = (state_t*)output;
+
+ // Skip the key expansion if key is passed as 0
+ if(0 != key)
+ {
+ Key = key;
+ KeyExpansion();
+ }
+
+ if(iv != 0)
+ {
+ Iv = (uint8_t*)iv;
+ }
+
+ for(i = 0; i < length; i += KEYLEN)
+ {
+ XorWithIv(input);
+ BlockCopy(output, input);
+ state = (state_t*)output;
+ Cipher();
+ Iv = output;
+ input += KEYLEN;
+ output += KEYLEN;
+ }
+
+ if(remainders)
+ {
+ BlockCopy(output, input);
+ memset(output + remainders, 0, KEYLEN - remainders); /* add 0-padding */
+ state = (state_t*)output;
+ Cipher();
+ }
+}
+
+void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
+{
+ intptr_t i;
+ uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */
+
+ BlockCopy(output, input);
+ state = (state_t*)output;
+
+ // Skip the key expansion if key is passed as 0
+ if(0 != key)
+ {
+ Key = key;
+ KeyExpansion();
+ }
+
+ // If iv is passed as 0, we continue to encrypt without re-setting the Iv
+ if(iv != 0)
+ {
+ Iv = (uint8_t*)iv;
+ }
+
+ for(i = 0; i < length; i += KEYLEN)
+ {
+ BlockCopy(output, input);
+ state = (state_t*)output;
+ InvCipher();
+ XorWithIv(output);
+ Iv = input;
+ input += KEYLEN;
+ output += KEYLEN;
+ }
+
+ if(remainders)
+ {
+ BlockCopy(output, input);
+ memset(output+remainders, 0, KEYLEN - remainders); /* add 0-padding */
+ state = (state_t*)output;
+ InvCipher();
+ }
+}
+
+
+#endif // #if defined(CBC) && CBC
+
+
diff --git a/src/aes.h b/src/aes.h
new file mode 100644
index 0000000..708a09c
--- /dev/null
+++ b/src/aes.h
@@ -0,0 +1,40 @@
+#ifndef _AES_H_
+#define _AES_H_
+
+#include <stdint.h>
+
+
+// #define the macros below to 1/0 to enable/disable the mode of operation.
+//
+// CBC enables AES128 encryption in CBC-mode of operation and handles 0-padding.
+// ECB enables the basic ECB 16-byte block algorithm. Both can be enabled simultaneously.
+
+// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
+#ifndef CBC
+ #define CBC 1
+#endif
+
+#ifndef ECB
+ #define ECB 1
+#endif
+
+
+
+#if defined(ECB) && ECB
+
+void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t *output);
+void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output);
+
+#endif // #if defined(ECB) && ECB
+
+
+#if defined(CBC) && CBC
+
+void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv);
+void AES128_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv);
+
+#endif // #if defined(CBC) && CBC
+
+
+
+#endif //_AES_H_
--
2.1.0
2
19
This imports Tiny AES128 from https://github.com/kokke/tiny-AES128-C for
use in the decoding of OSTC3 firmwares.
This aes-code is released into the public domain.
Signed-off-by: Anton Lundin <glance(a)acc.umu.se>
---
msvc/libdivecomputer.vcproj | 6 +
src/Makefile.am | 1 +
src/aes.c | 486 ++++++++++++++++++++++++++++++++++++++++++++
src/aes.h | 16 ++
4 files changed, 509 insertions(+)
create mode 100644 src/aes.c
create mode 100644 src/aes.h
diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj
index 78a8190..10a4cf7 100644
--- a/msvc/libdivecomputer.vcproj
+++ b/msvc/libdivecomputer.vcproj
@@ -247,6 +247,9 @@
>
</File>
<File
+ RelativePath="..\src\aes.c"
+ >
+ <File
RelativePath="..\src\hw_ostc3.c"
>
</File>
@@ -537,6 +540,9 @@
>
</File>
<File
+ RelativePath="..\src\aes.h"
+ >
+ <File
RelativePath="..\include\libdivecomputer\hw_ostc3.h"
>
</File>
diff --git a/src/Makefile.am b/src/Makefile.am
index 595f0c7..eb39a4b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -43,6 +43,7 @@ libdivecomputer_la_SOURCES = \
ihex.h ihex.c \
hw_ostc.c hw_ostc_parser.c \
hw_frog.c \
+ aes.h aes.c \
hw_ostc3.c \
cressi_edy.c cressi_edy_parser.c \
cressi_leonardo.c cressi_leonardo_parser.c \
diff --git a/src/aes.c b/src/aes.c
new file mode 100644
index 0000000..000a067
--- /dev/null
+++ b/src/aes.c
@@ -0,0 +1,486 @@
+/*
+
+This is an implementation of the AES128 algorithm, specifically ECB mode.
+
+The implementation is verified against the test vectors in:
+ National Institute of Standards and Technology Special Publication 800-38A 2001 ED
+
+ECB-AES128
+----------
+
+ plain-text:
+ 6bc1bee22e409f96e93d7e117393172a
+ ae2d8a571e03ac9c9eb76fac45af8e51
+ 30c81c46a35ce411e5fbc1191a0a52ef
+ f69f2445df4f9b17ad2b417be66c3710
+
+ key:
+ 2b7e151628aed2a6abf7158809cf4f3c
+
+ resulting cipher
+ 50fe67cc996d32b6da0937e99bafec60
+ d9a4dada0892239f6b8b3d7680e15674
+ a78819583f0308e7a6bf36b1386abf23
+ c6d3416d29165c6fcb8e51a227ba994e
+
+
+NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
+ You should pad the end of the string with zeros if this is not the case.
+
+*/
+
+#ifndef _AES_C_
+#define _AES_C_
+
+
+/*****************************************************************************/
+/* Includes: */
+/*****************************************************************************/
+#include <stdint.h>
+#include "aes.h"
+
+
+/*****************************************************************************/
+/* Defines: */
+/*****************************************************************************/
+// The number of columns comprising a state in AES. This is a constant in AES. Value=4
+#define Nb 4
+// The number of 32 bit words in a key.
+#define Nk 4
+// Key length in bytes [128 bit]
+#define keyln 16
+// The number of rounds in AES Cipher.
+#define Nr 10
+
+
+/*****************************************************************************/
+/* Private variables: */
+/*****************************************************************************/
+// in - pointer to the CipherText to be decrypted.
+// out - pointer to buffer to hold output of the decryption.
+// state - array holding the intermediate results during decryption.
+static uint8_t* in, *out, state[4][4];
+
+// The array that stores the round keys.
+static uint8_t RoundKey[176];
+
+// The Key input to the AES Program
+static uint8_t* Key;
+
+// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
+// The numbers below can be computed dynamically trading ROM for RAM -
+// This can be useful in (embedded) bootloader applications, where ROM is often limited.
+static const uint8_t sbox[256] = {
+ //0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
+
+static const uint8_t rsbox[256] =
+{ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
+
+
+// The round constant word array, Rcon[i], contains the values given by
+// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+// Note that i starts at 1, not 0).
+static const uint8_t Rcon[255] = {
+ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
+ 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
+ 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
+ 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
+ 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
+ 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
+ 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
+ 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
+ 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
+ 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
+ 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
+ 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
+ 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
+ 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
+ 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb };
+
+
+/*****************************************************************************/
+/* Private functions: */
+/*****************************************************************************/
+static uint8_t getSBoxValue(uint8_t num)
+{
+ return sbox[num];
+}
+
+static uint8_t getSBoxInvert(uint8_t num)
+{
+ return rsbox[num];
+}
+
+
+// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
+static void KeyExpansion()
+{
+ uint32_t i, j, k;
+ uint8_t tempa[4]; // used for the column/row operations
+
+ // The first round key is the key itself.
+ for(i = 0; i < Nk; ++i)
+ {
+ RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
+ RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
+ RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
+ RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
+ }
+
+ // All other round keys are found from the previous round keys.
+ for(; (i < (Nb * (Nr + 1))); ++i)
+ {
+ for(j = 0; j < 4; ++j)
+ {
+ tempa[j]=RoundKey[(i-1) * 4 + j];
+ }
+ if (i % Nk == 0)
+ {
+ // This function rotates the 4 bytes in a word to the left once.
+ // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+
+ // Function RotWord()
+ {
+ k = tempa[0];
+ tempa[0] = tempa[1];
+ tempa[1] = tempa[2];
+ tempa[2] = tempa[3];
+ tempa[3] = k;
+ }
+
+ // SubWord() is a function that takes a four-byte input word and
+ // applies the S-box to each of the four bytes to produce an output word.
+
+ // Function Subword()
+ {
+ tempa[0] = getSBoxValue(tempa[0]);
+ tempa[1] = getSBoxValue(tempa[1]);
+ tempa[2] = getSBoxValue(tempa[2]);
+ tempa[3] = getSBoxValue(tempa[3]);
+ }
+
+ tempa[0] = tempa[0] ^ Rcon[i/Nk];
+ }
+ else if (Nk > 6 && i % Nk == 4)
+ {
+ // Function Subword()
+ {
+ tempa[0] = getSBoxValue(tempa[0]);
+ tempa[1] = getSBoxValue(tempa[1]);
+ tempa[2] = getSBoxValue(tempa[2]);
+ tempa[3] = getSBoxValue(tempa[3]);
+ }
+ }
+ RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0];
+ RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1];
+ RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2];
+ RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3];
+ }
+}
+
+// This function adds the round key to state.
+// The round key is added to the state by an XOR function.
+static void AddRoundKey(uint8_t round)
+{
+ uint8_t i,j;
+ for(i=0;i<4;i++)
+ {
+ for(j = 0; j < 4; ++j)
+ {
+ state[j][i] ^= RoundKey[round * Nb * 4 + i * Nb + j];
+ }
+ }
+}
+
+// The SubBytes Function Substitutes the values in the
+// state matrix with values in an S-box.
+static void SubBytes()
+{
+ uint8_t i, j;
+ for(i = 0; i < 4; ++i)
+ {
+ for(j = 0; j < 4; ++j)
+ {
+ state[i][j] = getSBoxValue(state[i][j]);
+ }
+ }
+}
+
+// The ShiftRows() function shifts the rows in the state to the left.
+// Each row is shifted with different offset.
+// Offset = Row number. So the first row is not shifted.
+static void ShiftRows()
+{
+ uint8_t temp;
+
+ // Rotate first row 1 columns to left
+ temp = state[1][0];
+ state[1][0] = state[1][1];
+ state[1][1] = state[1][2];
+ state[1][2] = state[1][3];
+ state[1][3] = temp;
+
+ // Rotate second row 2 columns to left
+ temp = state[2][0];
+ state[2][0] = state[2][2];
+ state[2][2] = temp;
+
+ temp = state[2][1];
+ state[2][1] = state[2][3];
+ state[2][3] = temp;
+
+ // Rotate third row 3 columns to left
+ temp = state[3][0];
+ state[3][0] = state[3][3];
+ state[3][3] = state[3][2];
+ state[3][2] = state[3][1];
+ state[3][1] = temp;
+}
+
+static uint8_t xtime(uint8_t x)
+{
+ return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
+}
+
+// MixColumns function mixes the columns of the state matrix
+static void MixColumns()
+{
+ uint8_t i;
+ uint8_t Tmp,Tm,t;
+ for(i = 0; i < 4; ++i)
+ {
+ t = state[0][i];
+ Tmp = state[0][i] ^ state[1][i] ^ state[2][i] ^ state[3][i] ;
+ Tm = state[0][i] ^ state[1][i] ; Tm = xtime(Tm); state[0][i] ^= Tm ^ Tmp ;
+ Tm = state[1][i] ^ state[2][i] ; Tm = xtime(Tm); state[1][i] ^= Tm ^ Tmp ;
+ Tm = state[2][i] ^ state[3][i] ; Tm = xtime(Tm); state[2][i] ^= Tm ^ Tmp ;
+ Tm = state[3][i] ^ t ; Tm = xtime(Tm); state[3][i] ^= Tm ^ Tmp ;
+ }
+}
+
+// Multiplty is a macro used to multiply numbers in the field GF(2^8)
+#define Multiply(x,y) (((y & 1) * x) ^ ((y>>1 & 1) * xtime(x)) ^ ((y>>2 & 1) * xtime(xtime(x))) ^ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))))
+
+
+// MixColumns function mixes the columns of the state matrix.
+// The method used to multiply may be difficult to understand for the inexperienced.
+// Please use the references to gain more information.
+static void InvMixColumns()
+{
+ int i;
+ uint8_t a,b,c,d;
+ for(i=0;i<4;i++)
+ {
+
+ a = state[0][i];
+ b = state[1][i];
+ c = state[2][i];
+ d = state[3][i];
+
+
+ state[0][i] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
+ state[1][i] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
+ state[2][i] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
+ state[3][i] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
+ }
+}
+
+
+// The SubBytes Function Substitutes the values in the
+// state matrix with values in an S-box.
+static void InvSubBytes()
+{
+ uint8_t i,j;
+ for(i=0;i<4;i++)
+ {
+ for(j=0;j<4;j++)
+ {
+ state[i][j] = getSBoxInvert(state[i][j]);
+ }
+ }
+}
+
+
+static void InvShiftRows()
+{
+ uint8_t temp;
+
+ // Rotate first row 1 columns to right
+ temp=state[1][3];
+ state[1][3]=state[1][2];
+ state[1][2]=state[1][1];
+ state[1][1]=state[1][0];
+ state[1][0]=temp;
+
+ // Rotate second row 2 columns to right
+ temp=state[2][0];
+ state[2][0]=state[2][2];
+ state[2][2]=temp;
+
+ temp=state[2][1];
+ state[2][1]=state[2][3];
+ state[2][3]=temp;
+
+ // Rotate third row 3 columns to right
+ temp=state[3][0];
+ state[3][0]=state[3][1];
+ state[3][1]=state[3][2];
+ state[3][2]=state[3][3];
+ state[3][3]=temp;
+}
+
+
+// Cipher is the main function that encrypts the PlainText.
+static void Cipher()
+{
+ uint8_t i, j, round = 0;
+
+ //Copy the input PlainText to state array.
+ for(i = 0; i < 4; ++i)
+ {
+ for(j = 0; j < 4 ; ++j)
+ {
+ state[j][i] = in[(i * 4) + j];
+ }
+ }
+
+ // Add the First round key to the state before starting the rounds.
+ AddRoundKey(0);
+
+ // There will be Nr rounds.
+ // The first Nr-1 rounds are identical.
+ // These Nr-1 rounds are executed in the loop below.
+ for(round = 1; round < Nr; ++round)
+ {
+ SubBytes();
+ ShiftRows();
+ MixColumns();
+ AddRoundKey(round);
+ }
+
+ // The last round is given below.
+ // The MixColumns function is not here in the last round.
+ SubBytes();
+ ShiftRows();
+ AddRoundKey(Nr);
+
+ // The encryption process is over.
+ // Copy the state array to output array.
+ for(i = 0; i < 4; ++i)
+ {
+ for(j = 0; j < 4; ++j)
+ {
+ out[(i * 4) + j] = state[j][i];
+ }
+ }
+}
+
+static void InvCipher()
+{
+ uint8_t i,j,round=0;
+
+ //Copy the input CipherText to state array.
+ for(i=0;i<4;i++)
+ {
+ for(j=0;j<4;j++)
+ {
+ state[j][i] = in[i*4 + j];
+ }
+ }
+
+ // Add the First round key to the state before starting the rounds.
+ AddRoundKey(Nr);
+
+ // There will be Nr rounds.
+ // The first Nr-1 rounds are identical.
+ // These Nr-1 rounds are executed in the loop below.
+ for(round=Nr-1;round>0;round--)
+ {
+ InvShiftRows();
+ InvSubBytes();
+ AddRoundKey(round);
+ InvMixColumns();
+ }
+
+ // The last round is given below.
+ // The MixColumns function is not here in the last round.
+ InvShiftRows();
+ InvSubBytes();
+ AddRoundKey(0);
+
+ // The decryption process is over.
+ // Copy the state array to output array.
+ for(i=0;i<4;i++)
+ {
+ for(j=0;j<4;j++)
+ {
+ out[i*4+j]=state[j][i];
+ }
+ }
+}
+
+
+/*****************************************************************************/
+/* Public functions: */
+/*****************************************************************************/
+
+void AES128_ECB_encrypt(uint8_t* input, uint8_t* key, uint8_t *output)
+{
+ // Copy the Key and CipherText
+ Key = key;
+ in = input;
+ out = output;
+
+ // The KeyExpansion routine must be called before encryption.
+ KeyExpansion();
+
+ // The next function call encrypts the PlainText with the Key using AES algorithm.
+ Cipher();
+}
+
+void AES128_ECB_decrypt(uint8_t* input, uint8_t* key, uint8_t *output)
+{
+ Key = key;
+ in = input;
+ out = output;
+
+ KeyExpansion();
+
+ InvCipher();
+}
+
+#endif //_AES_C_
+
+
diff --git a/src/aes.h b/src/aes.h
new file mode 100644
index 0000000..5fb2176
--- /dev/null
+++ b/src/aes.h
@@ -0,0 +1,16 @@
+#ifndef _AES_H_
+#define _AES_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void AES128_ECB_encrypt(uint8_t* input, uint8_t* key, uint8_t *output);
+void AES128_ECB_decrypt(uint8_t* input, uint8_t* key, uint8_t *output);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif //_AES_H_
--
1.9.1
2
41
3
3
Two friends brought over their Seiko-based DCs. One an Apeks Quantum and
the other a Zeagle N2ition. The Zeagle cable was used.
I tried both in Windows and Linux to import dives (subsurface) but as soon
as the conversation took place the DCs switched from PC to Time mode. It
occurred at the first LED flash on the cable.
This looks like a protocol issue but before I dig into this I thought I'd
ask and look for some advice.
--
John Van Ostrand
At large on sabbatical
2
1