static dc_status_t hw_ostc_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { dc_buffer_t *buffer = dc_buffer_new (0); if (buffer == NULL) return DC_STATUS_NOMEMORY; dc_status_t rc = hw_ostc_device_dump (abstract, buffer); if (rc != DC_STATUS_SUCCESS) { dc_buffer_free (buffer); return rc; } // Emit a device info event. unsigned char *data = dc_buffer_get_data (buffer); dc_event_devinfo_t devinfo; devinfo.firmware = array_uint16_be (data + 264); devinfo.serial = array_uint16_le (data + 6); if (devinfo.serial > 7000) devinfo.model = 3; // OSTC 2C else if (devinfo.serial > 2048) devinfo.model = 2; // OSTC 2N else if (devinfo.serial > 300) devinfo.model = 1; // OSTC Mk2 else devinfo.model = 0; // OSTC device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); rc = hw_ostc_extract_dives (abstract, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); dc_buffer_free (buffer); return rc; }
static dc_status_t mares_darwin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) { mares_darwin_parser_t *parser = (mares_darwin_parser_t *) abstract; if (abstract->size < parser->headersize) return DC_STATUS_DATAFORMAT; const unsigned char *p = abstract->data; if (datetime) { datetime->year = array_uint16_be (p); datetime->month = p[2]; datetime->day = p[3]; datetime->hour = p[4]; datetime->minute = p[5]; datetime->second = 0; } return DC_STATUS_SUCCESS; }
static dc_status_t shearwater_predator_extract_predator (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) { shearwater_predator_device_t *device = (shearwater_predator_device_t*) abstract; dc_context_t *context = (abstract ? abstract->context : NULL); // Locate the most recent dive. // The device maintains an internal counter which is incremented for every // dive, and the current value at the time of the dive is stored in the // dive header. Thus the most recent dive will have the highest value. unsigned int maximum = 0; unsigned int eop = RB_PROFILE_END; // Search the ringbuffer backwards to locate matching header and // footer markers. Because the ringbuffer search algorithm starts at // some arbitrary position, which does not necessary corresponds // with a boundary between two dives, the begin position is adjusted // as soon as the first dive has been found. Without this step, // dives crossing the ringbuffer wrap point won't be detected when // searching backwards from the ringbuffer end offset. unsigned int footer = 0; unsigned int have_footer = 0; unsigned int begin = RB_PROFILE_BEGIN; unsigned int offset = RB_PROFILE_END; while (offset != begin) { // Handle the ringbuffer wrap point. if (offset == RB_PROFILE_BEGIN) offset = RB_PROFILE_END; // Move to the start of the block. offset -= SZ_BLOCK; if (array_isequal (data + offset, SZ_BLOCK, 0xFF)) { // Ignore empty blocks explicitly, because otherwise they are // incorrectly recognized as header markers. } else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFF && have_footer) { // If the first header marker is found, the begin offset is moved // after the corresponding footer marker. This is necessary to be // able to detect dives that cross the ringbuffer wrap point. if (begin == RB_PROFILE_BEGIN) begin = footer + SZ_BLOCK; // Get the internal dive number. unsigned int current = array_uint16_be (data + offset + 2); if (current > maximum) { maximum = current; eop = footer + SZ_BLOCK; } // The dive number in the header and footer should be identical. if (current != array_uint16_be (data + footer + 2)) { ERROR (context, "Unexpected dive number."); return DC_STATUS_DATAFORMAT; } // Reset the footer marker. have_footer = 0; } else if (data[offset + 0] == 0xFF && data[offset + 1] == 0xFE) { // Remember the footer marker. footer = offset; have_footer = 1; } } // Allocate memory for the profiles. unsigned char *buffer = (unsigned char *) malloc (RB_PROFILE_END - RB_PROFILE_BEGIN + SZ_BLOCK); if (buffer == NULL) { return DC_STATUS_NOMEMORY; } // Linearize the ringbuffer. memcpy (buffer + 0, data + eop, RB_PROFILE_END - eop); memcpy (buffer + RB_PROFILE_END - eop, data + RB_PROFILE_BEGIN, eop - RB_PROFILE_BEGIN); // Find the dives again in the linear buffer. footer = 0; have_footer = 0; offset = RB_PROFILE_END; while (offset != RB_PROFILE_BEGIN) { // Move to the start of the block. offset -= SZ_BLOCK; if (array_isequal (buffer + offset, SZ_BLOCK, 0xFF)) { break; } else if (buffer[offset + 0] == 0xFF && buffer[offset + 1] == 0xFF && have_footer) { // Append the final block. unsigned int length = footer + SZ_BLOCK - offset; memcpy (buffer + offset + length, data + SZ_MEMORY - SZ_BLOCK, SZ_BLOCK); // Check the fingerprint data. if (device && memcmp (buffer + offset + 12, device->fingerprint, sizeof (device->fingerprint)) == 0) break; if (callback && !callback (buffer + offset, length + SZ_BLOCK, buffer + offset + 12, sizeof (device->fingerprint), userdata)) break; have_footer = 0; } else if (buffer[offset + 0] == 0xFF && buffer[offset + 1] == 0xFE) { footer = offset; have_footer = 1; } } free (buffer); return DC_STATUS_SUCCESS; }
static dc_status_t hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + SZ_MEMORY; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Download the version data. unsigned char id[SZ_VERSION] = {0}; dc_status_t rc = hw_ostc3_device_version (abstract, id, sizeof (id)); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the version."); return rc; } // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = 0; devinfo.firmware = array_uint16_be (id + 2); devinfo.serial = array_uint16_le (id + 0); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate memory. unsigned char *header = (unsigned char *) malloc (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); if (header == NULL) { ERROR (abstract->context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; } // Download the logbook headers. rc = hw_ostc3_transfer (device, &progress, HEADER, NULL, 0, header, RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the header."); free (header); return rc; } // Locate the most recent dive. // The device maintains an internal counter which is incremented for every // dive, and the current value at the time of the dive is stored in the // dive header. Thus the most recent dive will have the highest value. unsigned int count = 0; unsigned int latest = 0; unsigned int maximum = 0; for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { unsigned int offset = i * RB_LOGBOOK_SIZE; // Ignore uninitialized header entries. if (array_isequal (header + offset, RB_LOGBOOK_SIZE, 0xFF)) continue; // Get the internal dive number. unsigned int current = array_uint16_le (header + offset + 80); if (current > maximum) { maximum = current; latest = i; } count++; } // Calculate the total and maximum size. unsigned int ndives = 0; unsigned int size = 0; unsigned int maxsize = 0; for (unsigned int i = 0; i < count; ++i) { unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; unsigned int offset = idx * RB_LOGBOOK_SIZE; // Uninitialized header entries should no longer be present at this // stage, unless the dives are interleaved with empty entries. But // that's something we don't support at all. if (array_isequal (header + offset, RB_LOGBOOK_SIZE, 0xFF)) { WARNING (abstract->context, "Unexpected empty header found."); break; } // Get the firmware version. unsigned int firmware = array_uint16_be (header + offset + 0x30); // Calculate the profile length. unsigned int length = RB_LOGBOOK_SIZE + array_uint24_le (header + offset + 9) - 6; if (firmware >= 93) length += 3; // Check the fingerprint data. if (memcmp (header + offset + 12, device->fingerprint, sizeof (device->fingerprint)) == 0) break; if (length > maxsize) maxsize = length; size += length; ndives++; } // Update and emit a progress event. progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + size; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Finish immediately if there are no dives available. if (ndives == 0) { free (header); return DC_STATUS_SUCCESS; } // Allocate enough memory for the largest dive. unsigned char *profile = (unsigned char *) malloc (maxsize); if (profile == NULL) { ERROR (abstract->context, "Failed to allocate memory."); free (header); return DC_STATUS_NOMEMORY; } // Download the dives. for (unsigned int i = 0; i < ndives; ++i) { unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; unsigned int offset = idx * RB_LOGBOOK_SIZE; // Get the firmware version. unsigned int firmware = array_uint16_be (header + offset + 0x30); // Calculate the profile length. unsigned int length = RB_LOGBOOK_SIZE + array_uint24_le (header + offset + 9) - 6; if (firmware >= 93) length += 3; // Download the dive. unsigned char number[1] = {idx}; rc = hw_ostc3_transfer (device, &progress, DIVE, number, sizeof (number), profile, length); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the dive."); free (profile); free (header); return rc; } // Verify the header in the logbook and profile are identical. if (memcmp (profile, header + offset, RB_LOGBOOK_SIZE) != 0) { ERROR (abstract->context, "Unexpected profile header."); free (profile); free (header); return rc; } if (callback && !callback (profile, length, profile + 12, sizeof (device->fingerprint), userdata)) break; } free (profile); free (header); return DC_STATUS_SUCCESS; }
static dc_status_t shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract; const unsigned char *data = abstract->data; unsigned int size = abstract->size; if (size < 2 * SZ_BLOCK) return DC_STATUS_DATAFORMAT; // Get the offset to the footer record. unsigned int footer = size - SZ_BLOCK; if (parser->petrel || array_uint16_be (data + footer) == 0xFFFD) { if (size < 3 * SZ_BLOCK) return DC_STATUS_DATAFORMAT; footer -= SZ_BLOCK; } // Get the sample size. unsigned int samplesize = SZ_SAMPLE_PREDATOR; if (parser->petrel) { samplesize = SZ_SAMPLE_PETREL; } // Get the unit system. unsigned int units = data[8]; // Previous gas mix. unsigned int o2_previous = 0, he_previous = 0; unsigned int time = 0; unsigned int offset = SZ_BLOCK; while (offset < footer) { dc_sample_value_t sample = {0}; // Ignore empty samples. if (array_isequal (data + offset, samplesize, 0x00)) { offset += samplesize; continue; } // Time (seconds). time += 10; sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); // Depth (1/10 m or ft). unsigned int depth = array_uint16_be (data + offset); if (units == IMPERIAL) sample.depth = depth * FEET / 10.0; else sample.depth = depth / 10.0; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); // Temperature (°C or °F). unsigned int temperature = data[offset + 13]; if (units == IMPERIAL) sample.temperature = (temperature - 32.0) * (5.0 / 9.0); else sample.temperature = temperature; if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); // PPO2 sample.ppo2 = data[offset + 6] / 100.0; if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); // Gaschange. unsigned int o2 = data[offset + 7]; unsigned int he = data[offset + 8]; if (o2 != o2_previous || he != he_previous) { sample.event.type = SAMPLE_EVENT_GASCHANGE2; sample.event.time = 0; sample.event.flags = 0; sample.event.value = o2 | (he << 16); if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); o2_previous = o2; he_previous = he; } // Deco stop / NDL. unsigned int decostop = array_uint16_be (data + offset + 2); if (decostop) { sample.deco.type = DC_DECO_DECOSTOP; if (units == IMPERIAL) sample.deco.depth = decostop * FEET; else sample.deco.depth = decostop; } else { sample.deco.type = DC_DECO_NDL; sample.deco.depth = 0.0; } sample.deco.time = data[offset + 9] * 60; if (callback) callback (DC_SAMPLE_DECO, sample, userdata); offset += samplesize; } return DC_STATUS_SUCCESS; }
static dc_status_t shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) { shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract; const unsigned char *data = abstract->data; unsigned int size = abstract->size; if (size < 2 * SZ_BLOCK) return DC_STATUS_DATAFORMAT; // Get the offset to the footer record. unsigned int footer = size - SZ_BLOCK; if (parser->petrel || array_uint16_be (data + footer) == 0xFFFD) { if (size < 3 * SZ_BLOCK) return DC_STATUS_DATAFORMAT; footer -= SZ_BLOCK; } // Get the unit system. unsigned int units = data[8]; // Get the gas mixes. unsigned int ngasmixes = 0; unsigned int oxygen[NGASMIXES] = {0}; unsigned int helium[NGASMIXES] = {0}; for (unsigned int i = 0; i < NGASMIXES; ++i) { unsigned int o2 = data[20 + i]; unsigned int he = data[30 + i]; if (o2 == 0 && he == 0) continue; // Skip disabled gas mixes. oxygen[ngasmixes] = o2; helium[ngasmixes] = he; ngasmixes++; } dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; unsigned int density = 0; if (value) { switch (type) { case DC_FIELD_DIVETIME: *((unsigned int *) value) = array_uint16_be (data + footer + 6) * 60; break; case DC_FIELD_MAXDEPTH: if (units == IMPERIAL) *((double *) value) = array_uint16_be (data + footer + 4) * FEET; else *((double *) value) = array_uint16_be (data + footer + 4); break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = ngasmixes; break; case DC_FIELD_GASMIX: gasmix->oxygen = oxygen[flags] / 100.0; gasmix->helium = helium[flags] / 100.0; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; case DC_FIELD_SALINITY: density = array_uint16_be (data + 83); if (density == 1000) water->type = DC_WATER_FRESH; else water->type = DC_WATER_SALT; water->density = density; break; case DC_FIELD_ATMOSPHERIC: *((double *) value) = array_uint16_be (data + 47) / 1000.0; break; default: return DC_STATUS_UNSUPPORTED; } } return DC_STATUS_SUCCESS; }
static device_status_t suunto_d9_device_packet (device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size) { suunto_d9_device_t *device = (suunto_d9_device_t *) abstract; if (device_is_cancelled (abstract)) return DEVICE_STATUS_CANCELLED; // Clear RTS to send the command. serial_set_rts (device->port, 0); // Send the command to the dive computer. int n = serial_write (device->port, command, csize); if (n != csize) { WARNING ("Failed to send the command."); return EXITCODE (n); } // Wait until all data has been transmitted. serial_drain (device->port); // Receive the echo. unsigned char echo[128] = {0}; assert (sizeof (echo) >= csize); n = serial_read (device->port, echo, csize); if (n != csize) { WARNING ("Failed to receive the echo."); return EXITCODE (n); } // Verify the echo. if (memcmp (command, echo, csize) != 0) { WARNING ("Unexpected echo."); return DEVICE_STATUS_PROTOCOL; } // Set RTS to receive the reply. serial_set_rts (device->port, 1); // Receive the answer of the dive computer. n = serial_read (device->port, answer, asize); if (n != asize) { WARNING ("Failed to receive the answer."); return EXITCODE (n); } // Verify the header of the package. if (answer[0] != command[0]) { WARNING ("Unexpected answer header."); return DEVICE_STATUS_PROTOCOL; } // Verify the size of the package. if (array_uint16_be (answer + 1) + 4 != asize) { WARNING ("Unexpected answer size."); return DEVICE_STATUS_PROTOCOL; } // Verify the parameters of the package. if (memcmp (command + 3, answer + 3, asize - size - 4) != 0) { WARNING ("Unexpected answer parameters."); return DEVICE_STATUS_PROTOCOL; } // Verify the checksum of the package. unsigned char crc = answer[asize - 1]; unsigned char ccrc = checksum_xor_uint8 (answer, asize - 1, 0x00); if (crc != ccrc) { WARNING ("Unexpected answer CRC."); return DEVICE_STATUS_PROTOCOL; } return DEVICE_STATUS_SUCCESS; }
dc_status_t suunto_common_extract_dives (suunto_common_device_t *device, const suunto_common_layout_t *layout, const unsigned char data[], dc_dive_callback_t callback, void *userdata) { assert (layout != NULL); unsigned int eop; if (layout->eop) { // Get the end-of-profile pointer directly from the header. eop = array_uint16_be (data + layout->eop); } else { // Get the end-of-profile pointer by searching for the // end-of-profile marker in the profile ringbuffer. eop = layout->rb_profile_begin; while (eop < layout->rb_profile_end) { if (data[eop] == 0x82) break; eop++; } } // Validate the end-of-profile pointer. if (eop < layout->rb_profile_begin || eop >= layout->rb_profile_end || data[eop] != 0x82) { return DC_STATUS_DATAFORMAT; } // Memory buffer for the profile ringbuffer. unsigned int length = layout->rb_profile_end - layout->rb_profile_begin; unsigned char *buffer = (unsigned char *) malloc (length); if (buffer == NULL) return DC_STATUS_NOMEMORY; unsigned int current = eop; unsigned int previous = eop; for (unsigned int i = 0; i < length; ++i) { // Move backwards through the ringbuffer. if (current == layout->rb_profile_begin) current = layout->rb_profile_end; current--; // Check for an end of profile marker. if (data[current] == 0x82) break; // Check for an end of dive marker (of the next dive), // to find the start of the current dive. unsigned int idx = RB_PROFILE_PEEK (current, layout); if (data[idx] == 0x80) { unsigned int len = RB_PROFILE_DISTANCE (current, previous, layout); if (current + len > layout->rb_profile_end) { unsigned int a = layout->rb_profile_end - current; unsigned int b = (current + len) - layout->rb_profile_end; memcpy (buffer + 0, data + current, a); memcpy (buffer + a, data + layout->rb_profile_begin, b); } else { memcpy (buffer, data + current, len); } if (device && memcmp (buffer + layout->fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) { free (buffer); return DC_STATUS_SUCCESS; } if (callback && !callback (buffer, len, buffer + layout->fp_offset, sizeof (device->fingerprint), userdata)) { free (buffer); return DC_STATUS_SUCCESS; } previous = current; } } free (buffer); if (data[current] != 0x82) return DC_STATUS_DATAFORMAT; return DC_STATUS_SUCCESS; }
static dc_status_t mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { mares_darwin_parser_t *parser = (mares_darwin_parser_t *) abstract; if (abstract->size < parser->headersize) return DC_STATUS_DATAFORMAT; unsigned int time = 0; unsigned int pressure = array_uint16_be (abstract->data + 0x17); unsigned int offset = parser->headersize; while (offset + parser->samplesize <= abstract->size) { dc_sample_value_t sample = {0}; unsigned int value = array_uint16_le (abstract->data + offset); unsigned int depth = value & 0x07FF; unsigned int ascent = (value & 0xE000) >> 13; unsigned int violation = (value & 0x1000) >> 12; unsigned int deco = (value & 0x0800) >> 11; // Surface Time (seconds). time += 20; sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); // Depth (1/10 m). sample.depth = depth / 10.0; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); // Ascent rate if (ascent) { sample.event.type = SAMPLE_EVENT_ASCENT; sample.event.time = 0; sample.event.flags = 0; sample.event.value = ascent; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } // Deco violation if (violation) { sample.event.type = SAMPLE_EVENT_CEILING; sample.event.time = 0; sample.event.flags = 0; sample.event.value = 0; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } // Deco stop if (deco) { sample.event.type = SAMPLE_EVENT_DECOSTOP; sample.event.time = 0; sample.event.flags = 0; sample.event.value = 0; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); } if (parser->samplesize == 3) { unsigned int type = (time / 20 + 2) % 3; if (type == 0) { // Tank Pressure (bar) pressure -= abstract->data[offset + 2]; sample.pressure.tank = 0; sample.pressure.value = pressure; if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); } } offset += parser->samplesize; } return DC_STATUS_SUCCESS; }
static dc_status_t shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { shearwater_petrel_device_t *device = (shearwater_petrel_device_t *) abstract; dc_status_t rc = DC_STATUS_SUCCESS; // Allocate memory buffers for the manifests. dc_buffer_t *buffer = dc_buffer_new (MANIFEST_SIZE); dc_buffer_t *manifests = dc_buffer_new (MANIFEST_SIZE); if (buffer == NULL || manifests == NULL) { ERROR (abstract->context, "Insufficient buffer space available."); dc_buffer_free (buffer); dc_buffer_free (manifests); return DC_STATUS_NOMEMORY; } // Enable progress notifications. unsigned int current = 0, maximum = 0; dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Read the serial number. rc = shearwater_common_identifier (&device->base, buffer, ID_SERIAL); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the serial number."); dc_buffer_free (buffer); dc_buffer_free (manifests); return rc; } // Convert to a number. unsigned char serial[4] = {0}; if (array_convert_hex2bin (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), serial, sizeof (serial)) != 0 ) { ERROR (abstract->context, "Failed to convert the serial number."); dc_buffer_free (buffer); dc_buffer_free (manifests); return DC_STATUS_DATAFORMAT; } // Read the firmware version. rc = shearwater_common_identifier (&device->base, buffer, ID_FIRMWARE); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the firmware version."); dc_buffer_free (buffer); dc_buffer_free (manifests); return rc; } // Convert to a number. unsigned int firmware = str2num (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), 1); // Read the hardware type. rc = shearwater_common_identifier (&device->base, buffer, ID_HARDWARE); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the hardware type."); dc_buffer_free (buffer); dc_buffer_free (manifests); return rc; } // Convert and map to the model number. unsigned int hardware = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer)); unsigned int model = 0; switch (hardware) { case 0x0808: // Petrel 2 case 0x0909: // Petrel 1 case 0x0B0B: // Petrel 1 (newer hardware) model = PETREL; break; case 0x0A0A: // Nerd 1 case 0x0E0D: // Nerd 2 model = NERD; break; case 0x0707: model = PERDIX; break; case 0x0C0D: model = PERDIXAI; break; default: WARNING (abstract->context, "Unknown hardware type %04x.", hardware); } // Emit a device info event. dc_event_devinfo_t devinfo; devinfo.model = model; devinfo.firmware = firmware; devinfo.serial = array_uint32_be (serial); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); while (1) { // Update the progress state. // Assume the worst case scenario of a full manifest, and adjust the // value with the actual number of dives after the manifest has been // processed. maximum += 1 + RECORD_COUNT; // Download a manifest. progress.current = NSTEPS * current; progress.maximum = NSTEPS * maximum; rc = shearwater_common_download (&device->base, buffer, MANIFEST_ADDR, MANIFEST_SIZE, 0, &progress); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to download the manifest."); dc_buffer_free (buffer); dc_buffer_free (manifests); return rc; } // Cache the buffer pointer and size. unsigned char *data = dc_buffer_get_data (buffer); unsigned int size = dc_buffer_get_size (buffer); // Process the records in the manifest. unsigned int count = 0; unsigned int offset = 0; while (offset < size) { // Check for a valid dive header. unsigned int header = array_uint16_be (data + offset); if (header != 0xA5C4) break; // Check the fingerprint data. if (memcmp (data + offset + 4, device->fingerprint, sizeof (device->fingerprint)) == 0) break; offset += RECORD_SIZE; count++; } // Update the progress state. current += 1; maximum -= RECORD_COUNT - count; // Append the manifest records to the main buffer. if (!dc_buffer_append (manifests, data, count * RECORD_SIZE)) { ERROR (abstract->context, "Insufficient buffer space available."); dc_buffer_free (buffer); dc_buffer_free (manifests); return DC_STATUS_NOMEMORY; } // Stop downloading manifest if there are no more records. if (count != RECORD_COUNT) break; } // Update and emit a progress event. progress.current = NSTEPS * current; progress.maximum = NSTEPS * maximum; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Cache the buffer pointer and size. unsigned char *data = dc_buffer_get_data (manifests); unsigned int size = dc_buffer_get_size (manifests); unsigned int offset = 0; while (offset < size) { // Get the address of the dive. unsigned int address = array_uint32_be (data + offset + 20); // Download the dive. progress.current = NSTEPS * current; progress.maximum = NSTEPS * maximum; rc = shearwater_common_download (&device->base, buffer, DIVE_ADDR + address, DIVE_SIZE, 1, &progress); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to download the dive."); dc_buffer_free (buffer); dc_buffer_free (manifests); return rc; } // Update the progress state. current += 1; unsigned char *buf = dc_buffer_get_data (buffer); unsigned int len = dc_buffer_get_size (buffer); if (callback && !callback (buf, len, buf + 12, sizeof (device->fingerprint), userdata)) break; offset += RECORD_SIZE; } // Update and emit a progress event. progress.current = NSTEPS * current; progress.maximum = NSTEPS * maximum; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); dc_buffer_free (manifests); dc_buffer_free (buffer); return rc; }
int irda_socket_open (irda **out) { if (out == NULL) return -1; // EINVAL (Invalid argument) // Allocate memory. struct irda *device = (struct irda *) malloc (sizeof (struct irda)); if (device == NULL) { TRACE ("malloc"); return -1; // ENOMEM (Not enough space) } // Default to blocking reads. device->timeout = -1; // Open the socket. device->fd = socket (AF_IRDA, SOCK_STREAM, 0); #ifdef _WIN32 if (device->fd == INVALID_SOCKET) { #else if (device->fd == -1) { #endif TRACE ("socket"); free (device); return -1; } *out = device; return 0; } int irda_socket_close (irda *device) { if (device == NULL) return -1; // Terminate all send and receive operations. shutdown (device->fd, 0); // Close the socket. #ifdef _WIN32 if (closesocket (device->fd) != 0) { TRACE ("closesocket"); #else if (close (device->fd) != 0) { TRACE ("close"); #endif free (device); return -1; } // Free memory. free (device); return 0; } int irda_socket_set_timeout (irda *device, long timeout) { if (device == NULL) return -1; // EINVAL (Invalid argument) device->timeout = timeout; return 0; } #define DISCOVER_MAX_DEVICES 16 // Maximum number of devices. #define DISCOVER_MAX_RETRIES 4 // Maximum number of retries. #ifdef _WIN32 #define DISCOVER_BUFSIZE sizeof (DEVICELIST) + \ sizeof (IRDA_DEVICE_INFO) * (DISCOVER_MAX_DEVICES - 1) #else #define DISCOVER_BUFSIZE sizeof (struct irda_device_list) + \ sizeof (struct irda_device_info) * (DISCOVER_MAX_DEVICES - 1) #endif int irda_socket_discover (irda *device, irda_callback_t callback, void *userdata) { if (device == NULL) return -1; unsigned char data[DISCOVER_BUFSIZE] = {0}; #ifdef _WIN32 DEVICELIST *list = (DEVICELIST *) data; int size = sizeof (data); #else struct irda_device_list *list = (struct irda_device_list *) data; socklen_t size = sizeof (data); #endif int rc = 0; unsigned int nretries = 0; while ((rc = getsockopt (device->fd, SOL_IRLMP, IRLMP_ENUMDEVICES, (char*) data, &size)) != 0 || #ifdef _WIN32 list->numDevice == 0) #else list->len == 0) #endif { // Automatically retry the discovery when no devices were found. // On Linux, getsockopt fails with EAGAIN when no devices are // discovered, while on Windows it succeeds and sets the number // of devices to zero. Both situations are handled the same here. if (rc != 0) { #ifdef _WIN32 if (WSAGetLastError() != WSAEWOULDBLOCK) { #else if (errno != EAGAIN) { #endif TRACE ("getsockopt"); return -1; // Error during getsockopt call. } } // Abort if the maximum number of retries is reached. if (nretries++ >= DISCOVER_MAX_RETRIES) return 0; // Restore the size parameter in case it was // modified by the previous getsockopt call. size = sizeof (data); #ifdef _WIN32 Sleep (1000); #else sleep (1); #endif } if (callback) { #ifdef _WIN32 for (unsigned int i = 0; i < list->numDevice; ++i) { unsigned int address = array_uint32_be (list->Device[i].irdaDeviceID); unsigned int hints = (list->Device[i].irdaDeviceHints1 << 8) + list->Device[i].irdaDeviceHints2; callback (address, list->Device[i].irdaDeviceName, list->Device[i].irdaCharSet, hints, userdata); } #else for (unsigned int i = 0; i < list->len; ++i) { unsigned int hints = array_uint16_be (list->dev[i].hints); callback (list->dev[i].daddr, list->dev[i].info, list->dev[i].charset, hints, userdata); } #endif } return 0; } int irda_socket_connect_name (irda *device, unsigned int address, const char *name) { if (device == NULL) return -1; #ifdef _WIN32 SOCKADDR_IRDA peer; peer.irdaAddressFamily = AF_IRDA; peer.irdaDeviceID[0] = (address >> 24) & 0xFF; peer.irdaDeviceID[1] = (address >> 16) & 0xFF; peer.irdaDeviceID[2] = (address >> 8) & 0xFF; peer.irdaDeviceID[3] = (address ) & 0xFF; if (name) strncpy (peer.irdaServiceName, name, 25); else memset (peer.irdaServiceName, 0x00, 25); #else struct sockaddr_irda peer; peer.sir_family = AF_IRDA; peer.sir_addr = address; if (name) strncpy (peer.sir_name, name, 25); else memset (peer.sir_name, 0x00, 25); #endif if (connect (device->fd, (struct sockaddr *) &peer, sizeof (peer)) != 0) { TRACE ("connect"); return -1; } return 0; } int irda_socket_connect_lsap (irda *device, unsigned int address, unsigned int lsap) { if (device == NULL) return -1; #ifdef _WIN32 SOCKADDR_IRDA peer; peer.irdaAddressFamily = AF_IRDA; peer.irdaDeviceID[0] = (address >> 24) & 0xFF; peer.irdaDeviceID[1] = (address >> 16) & 0xFF; peer.irdaDeviceID[2] = (address >> 8) & 0xFF; peer.irdaDeviceID[3] = (address ) & 0xFF; snprintf (peer.irdaServiceName, 25, "LSAP-SEL%u", lsap); #else struct sockaddr_irda peer; peer.sir_family = AF_IRDA; peer.sir_addr = address; peer.sir_lsap_sel = lsap; memset (peer.sir_name, 0x00, 25); #endif if (connect (device->fd, (struct sockaddr *) &peer, sizeof (peer)) != 0) { TRACE ("connect"); return -1; } return 0; } int irda_socket_available (irda* device) { if (device == NULL) return -1; // EINVAL (Invalid argument) #ifdef _WIN32 unsigned long bytes = 0; if (ioctlsocket (device->fd, FIONREAD, &bytes) != 0) { TRACE ("ioctlsocket"); #else int bytes = 0; if (ioctl (device->fd, FIONREAD, &bytes) != 0) { TRACE ("ioctl"); #endif return -1; } return bytes; } int irda_socket_read (irda* device, void* data, unsigned int size) { if (device == NULL) return -1; // EINVAL (Invalid argument) struct timeval tv; if (device->timeout >= 0) { tv.tv_sec = (device->timeout / 1000); tv.tv_usec = (device->timeout % 1000) * 1000; } fd_set fds; FD_ZERO (&fds); FD_SET (device->fd, &fds); unsigned int nbytes = 0; while (nbytes < size) { int rc = select (device->fd + 1, &fds, NULL, NULL, (device->timeout >= 0 ? &tv : NULL)); if (rc < 0) { TRACE ("select"); return -1; // Error during select call. } else if (rc == 0) { break; // Timeout. } int n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0); if (n < 0) { TRACE ("recv"); return -1; // Error during recv call. } else if (n == 0) { break; // EOF reached. } nbytes += n; } return nbytes; } int irda_socket_write (irda* device, const void *data, unsigned int size) { if (device == NULL) return -1; // EINVAL (Invalid argument) unsigned int nbytes = 0; while (nbytes < size) { int n = send (device->fd, (char*) data + nbytes, size - nbytes, 0); if (n < 0) { TRACE ("send"); return -1; // Error during send call. } nbytes += n; } return nbytes; }
dc_status_t diverite_nitekq_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) { diverite_nitekq_device_t *device = (diverite_nitekq_device_t *) abstract; dc_context_t *context = (abstract ? abstract->context : NULL); if (abstract && !ISINSTANCE (abstract)) return DC_STATUS_INVALIDARGS; if (size < SZ_PACKET + SZ_MEMORY) return DC_STATUS_DATAFORMAT; // Skip the first packet. We don't need it for anything. It also // makes the logic easier because all offsets in the data are // relative to the real start of the memory (e.g. excluding this // artificial first block). data += SZ_PACKET; // Allocate memory. unsigned char *buffer = (unsigned char *) malloc (RB_PROFILE_END - RB_PROFILE_BEGIN); if (buffer == NULL) { ERROR (context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; } // Get the end of profile pointer. unsigned int eop = array_uint16_be(data + EOP); if (eop < RB_PROFILE_BEGIN || eop >= RB_PROFILE_END) { ERROR (context, "Invalid ringbuffer pointer detected."); free (buffer); return DC_STATUS_DATAFORMAT; } // When a new dive is added, the device moves all existing logbook // and address entries towards the end, such that the most recent // one is always the first one. This is not the case for the profile // data, which is added at the end. unsigned int previous = eop; for (unsigned int i = 0; i < 10; ++i) { // Get the pointer to the logbook entry. const unsigned char *p = data + LOGBOOK + i * SZ_LOGBOOK; // Abort if an empty logbook is found. if (array_isequal (p, SZ_LOGBOOK, 0x00)) break; // Get the address of the profile data. unsigned int address = array_uint16_be(data + ADDRESS + i * 2); if (address < RB_PROFILE_BEGIN || address >= RB_PROFILE_END) { ERROR (context, "Invalid ringbuffer pointer detected."); free (buffer); return DC_STATUS_DATAFORMAT; } // Check the fingerprint data. if (device && memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0) break; // Copy the logbook entry. memcpy (buffer, p, SZ_LOGBOOK); // Copy the profile data. unsigned int length = 0; if (previous > address) { length = previous - address; memcpy (buffer + SZ_LOGBOOK, data + address, length); } else { unsigned int len_a = RB_PROFILE_END - address; unsigned int len_b = previous - RB_PROFILE_BEGIN; length = len_a + len_b; memcpy (buffer + SZ_LOGBOOK, data + address, len_a); memcpy (buffer + SZ_LOGBOOK + len_a, data + RB_PROFILE_BEGIN, len_b); } if (callback && !callback (buffer, length + SZ_LOGBOOK, buffer, SZ_LOGBOOK, userdata)) { break; } previous = address; } free (buffer); return DC_STATUS_SUCCESS; }
static dc_status_t cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) abstract; // Erase the current contents of the buffer and // pre-allocate the required amount of memory. if (!dc_buffer_clear (buffer) || !dc_buffer_resize (buffer, SZ_MEMORY)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = SZ_MEMORY; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Send the command header to the dive computer. const unsigned char command[] = {0x7B, 0x31, 0x32, 0x33, 0x44, 0x42, 0x41, 0x7d}; int n = serial_write (device->port, command, sizeof (command)); if (n != sizeof (command)) { ERROR (abstract->context, "Failed to send the command."); return EXITCODE (n); } // Receive the header packet. unsigned char header[7] = {0}; n = serial_read (device->port, header, sizeof (header)); if (n != sizeof (header)) { ERROR (abstract->context, "Failed to receive the answer."); return EXITCODE (n); } // Verify the header packet. const unsigned char expected[] = {0x7B, 0x21, 0x44, 0x35, 0x42, 0x33, 0x7d}; if (memcmp (header, expected, sizeof (expected)) != 0) { ERROR (abstract->context, "Unexpected answer byte."); return DC_STATUS_PROTOCOL; } unsigned char *data = dc_buffer_get_data (buffer); unsigned int nbytes = 0; while (nbytes < SZ_MEMORY) { // Set the minimum packet size. unsigned int len = 1024; // Increase the packet size if more data is immediately available. int available = serial_get_received (device->port); if (available > len) len = available; // Limit the packet size to the total size. if (nbytes + len > SZ_MEMORY) len = SZ_MEMORY - nbytes; // Read the packet. n = serial_read (device->port, data + nbytes, len); if (n != len) { ERROR (abstract->context, "Failed to receive the answer."); return EXITCODE (n); } // Update and emit a progress event. progress.current += len; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); nbytes += len; } // Receive the trailer packet. unsigned char trailer[4] = {0}; n = serial_read (device->port, trailer, sizeof (trailer)); if (n != sizeof (trailer)) { ERROR (abstract->context, "Failed to receive the answer."); return EXITCODE (n); } // Convert to a binary checksum. unsigned char checksum[2] = {0}; array_convert_hex2bin (trailer, sizeof (trailer), checksum, sizeof (checksum)); // Verify the checksum. unsigned int csum1 = array_uint16_be (checksum); unsigned int csum2 = checksum_crc_ccitt_uint16 (data, SZ_MEMORY); if (csum1 != csum2) { ERROR (abstract->context, "Unexpected answer bytes."); return DC_STATUS_PROTOCOL; } return DC_STATUS_SUCCESS; }
static dc_status_t shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { shearwater_petrel_device_t *device = (shearwater_petrel_device_t *) abstract; dc_status_t rc = DC_STATUS_SUCCESS; // Allocate memory buffers for the manifests. dc_buffer_t *buffer = dc_buffer_new (MANIFEST_SIZE); dc_buffer_t *manifests = dc_buffer_new (MANIFEST_SIZE); if (buffer == NULL || manifests == NULL) { ERROR (abstract->context, "Insufficient buffer space available."); dc_buffer_free (buffer); dc_buffer_free (manifests); return DC_STATUS_NOMEMORY; } while (1) { // Download a manifest. rc = shearwater_common_download (&device->base, buffer, MANIFEST_ADDR, MANIFEST_SIZE, 0); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to download the manifest."); dc_buffer_free (buffer); dc_buffer_free (manifests); return rc; } // Cache the buffer pointer and size. unsigned char *data = dc_buffer_get_data (buffer); unsigned int size = dc_buffer_get_size (buffer); // Process the records in the manifest. unsigned int count = 0; unsigned int offset = 0; while (offset < size) { // Check for a valid dive header. unsigned int header = array_uint16_be (data + offset); if (header != 0xA5C4) break; // Check the fingerprint data. if (memcmp (data + offset + 4, device->fingerprint, sizeof (device->fingerprint)) == 0) break; offset += RECORD_SIZE; count++; } // Append the manifest records to the main buffer. if (!dc_buffer_append (manifests, data, count * RECORD_SIZE)) { ERROR (abstract->context, "Insufficient buffer space available."); dc_buffer_free (buffer); dc_buffer_free (manifests); return DC_STATUS_NOMEMORY; } // Stop downloading manifest if there are no more records. if (count != RECORD_COUNT) break; } // Cache the buffer pointer and size. unsigned char *data = dc_buffer_get_data (manifests); unsigned int size = dc_buffer_get_size (manifests); unsigned int offset = 0; while (offset < size) { // Get the address of the dive. unsigned int address = array_uint32_be (data + offset + 20); // Download the dive. rc = shearwater_common_download (&device->base, buffer, DIVE_ADDR + address, DIVE_SIZE, 1); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to download the dive."); dc_buffer_free (buffer); dc_buffer_free (manifests); return rc; } unsigned char *buf = dc_buffer_get_data (buffer); unsigned int len = dc_buffer_get_size (buffer); if (callback && !callback (buf, len, buf + 12, sizeof (device->fingerprint), userdata)) break; offset += RECORD_SIZE; } dc_buffer_free (manifests); dc_buffer_free (buffer); return rc; }
static dc_status_t hw_ostc_firmware_readfile (hw_ostc_firmware_t *firmware, dc_context_t *context, const char *filename) { dc_status_t rc = DC_STATUS_SUCCESS; if (firmware == NULL) { ERROR (context, "Invalid arguments."); return DC_STATUS_INVALIDARGS; } // Initialize the buffers. memset (firmware->data, 0xFF, sizeof (firmware->data)); memset (firmware->bitmap, 0x00, sizeof (firmware->bitmap)); // Open the hex file. dc_ihex_file_t *file = NULL; rc = dc_ihex_file_open (&file, context, filename); if (rc != DC_STATUS_SUCCESS) { ERROR (context, "Failed to open the hex file."); return rc; } // Read the hex file. unsigned int lba = 0; dc_ihex_entry_t entry; while ((rc = dc_ihex_file_read (file, &entry)) == DC_STATUS_SUCCESS) { if (entry.type == 0) { // Data record. unsigned int address = (lba << 16) + entry.address; if (address + entry.length > SZ_FIRMWARE) { WARNING (context, "Ignoring out of range record (0x%08x,%u).", address, entry.length); continue; } // Copy the record to the buffer. memcpy (firmware->data + address, entry.data, entry.length); // Mark the corresponding blocks in the bitmap. unsigned int begin = address / SZ_BLOCK; unsigned int end = (address + entry.length + SZ_BLOCK - 1) / SZ_BLOCK; for (unsigned int i = begin; i < end; ++i) { firmware->bitmap[i] = 1; } } else if (entry.type == 1) { // End of file record. break; } else if (entry.type == 4) { // Extended linear address record. lba = array_uint16_be (entry.data); } else { ERROR (context, "Unexpected record type."); dc_ihex_file_close (file); return DC_STATUS_DATAFORMAT; } } if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_DONE) { ERROR (context, "Failed to read the record."); dc_ihex_file_close (file); return rc; } // Close the file. dc_ihex_file_close (file); // Verify the presence of the first block. if (firmware->bitmap[0] == 0) { ERROR (context, "No first data block."); return DC_STATUS_DATAFORMAT; } // Setup the last block. // Copy the "goto main" instruction, stored in the first 8 bytes of the hex // file, to the end of the last block at address 0x17F38. This last block // needs to be present, regardless of whether it's included in the hex file // or not! memset (firmware->data + SZ_FIRMWARE - SZ_BLOCK, 0xFF, SZ_BLOCK - 8); memcpy (firmware->data + SZ_FIRMWARE - 8, firmware->data, 8); firmware->bitmap[C_ARRAY_SIZE(firmware->bitmap) - 1] = 1; // Setup the first block. // Copy the hardcoded "goto 0x17F40" instruction to the start of the first // block at address 0x00000. const unsigned char header[] = {0xA0, 0xEF, 0xBF, 0xF0}; memcpy (firmware->data, header, sizeof (header)); return rc; }
static dc_status_t hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { hw_ostc_device_t *device = (hw_ostc_device_t*) abstract; // Erase the current contents of the buffer. if (!dc_buffer_clear (buffer)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = SZ_HEADER + SZ_FW_NEW; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Send the command. unsigned char command[1] = {'a'}; int rc = serial_write (device->port, command, sizeof (command)); if (rc != sizeof (command)) { ERROR (abstract->context, "Failed to send the command."); return EXITCODE (rc); } // Read the header. unsigned char header[SZ_HEADER] = {0}; int n = serial_read (device->port, header, sizeof (header)); if (n != sizeof (header)) { ERROR (abstract->context, "Failed to receive the header."); return EXITCODE (n); } // Verify the header. unsigned char preamble[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55}; if (memcmp (header, preamble, sizeof (preamble)) != 0) { ERROR (abstract->context, "Unexpected answer header."); return DC_STATUS_DATAFORMAT; } // Get the firmware version. unsigned int firmware = array_uint16_be (header + 264); // Get the amount of profile data. unsigned int size = sizeof (header); if (firmware > FW_190) size += SZ_FW_NEW; else size += SZ_FW_190; // Update and emit a progress event. progress.current = sizeof (header); progress.maximum = size; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Allocate the required amount of memory. if (!dc_buffer_resize (buffer, size)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } unsigned char *data = dc_buffer_get_data (buffer); // Copy the header to the output buffer. memcpy (data, header, sizeof (header)); unsigned int nbytes = sizeof (header); while (nbytes < size) { // Set the minimum packet size. unsigned int len = 1024; // Increase the packet size if more data is immediately available. int available = serial_get_received (device->port); if (available > len) len = available; // Limit the packet size to the total size. if (nbytes + len > size) len = size - nbytes; // Read the packet. int n = serial_read (device->port, data + nbytes, len); if (n != len) { ERROR (abstract->context, "Failed to receive the answer."); return EXITCODE (n); } // Update and emit a progress event. progress.current += len; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); nbytes += len; } return DC_STATUS_SUCCESS; }