diff --git a/include/libdivecomputer/descriptor.h b/include/libdivecomputer/descriptor.h index 6eaba8ed..62b36738 100644 --- a/include/libdivecomputer/descriptor.h +++ b/include/libdivecomputer/descriptor.h @@ -124,6 +124,26 @@ dc_descriptor_get_transports (const dc_descriptor_t *descriptor); int dc_descriptor_filter (const dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata); +/** + * Find a descriptor by hardware identifier. + * + * Some device families share a coarse model number across multiple marketed + * products. When a device reports a hardware identifier (via + * DC_EVENT_DEVINFO.hw_id), this function can find a more specific + * descriptor than the model number alone provides. This is best-effort: + * returns NULL if the hardware id is unknown or not in the table. + * + * The returned descriptor is a direct reference to an internal table entry + * and must be released with dc_descriptor_free(), which is safe to call on + * such a reference. + * + * @param[in] family The device family type. + * @param[in] hw_id The hardware identifier from DC_EVENT_DEVINFO. + * @returns A descriptor on success, or NULL if no match. + */ +dc_descriptor_t * +dc_descriptor_find_by_hw_id (dc_family_t family, unsigned int hw_id); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/include/libdivecomputer/device.h b/include/libdivecomputer/device.h index 8810a652..9ffce79e 100644 --- a/include/libdivecomputer/device.h +++ b/include/libdivecomputer/device.h @@ -52,6 +52,13 @@ typedef struct dc_event_devinfo_t { unsigned int model; unsigned int firmware; unsigned int serial; + /* Generic hardware identifier, 0 = unknown/not available. + * On live download from devices that support it, this is populated + * with the device's hardware type identifier (e.g. Shearwater FWID + * from RDBI 0x8050). It supplements the coarse model field for + * consumer-side sub-model differentiation. Consumers that do not use + * this field are unaffected; the model field remains authoritative. */ + unsigned int hw_id; } dc_event_devinfo_t; typedef struct dc_event_clock_t { diff --git a/src/atomics_cobalt.c b/src/atomics_cobalt.c index 10de3e7c..7b86920e 100644 --- a/src/atomics_cobalt.c +++ b/src/atomics_cobalt.c @@ -333,6 +333,7 @@ atomics_cobalt_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac devinfo.serial *= 10; devinfo.serial += device->version[i] - '0'; } + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate a memory buffer. diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 83f88009..eec420ea 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -937,7 +937,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call devinfo.serial = array_uint32_word_be(data.config + layout->cf_serial_number); else devinfo.serial = array_uint32_le(data.config + layout->cf_serial_number); - + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); unsigned int head_dive = 0, tail_dive = 0, dive_count = 0; diff --git a/src/cressi_edy.c b/src/cressi_edy.c index 6845a0ff..ecd9b244 100644 --- a/src/cressi_edy.c +++ b/src/cressi_edy.c @@ -414,6 +414,7 @@ cressi_edy_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = device->model; devinfo.firmware = 0; devinfo.serial = 0; + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return device_dump_read (abstract, 0, dc_buffer_get_data (buffer), @@ -438,6 +439,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v devinfo.model = device->model; devinfo.firmware = 0; devinfo.serial = 0; + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Read the logbook data. diff --git a/src/cressi_goa.c b/src/cressi_goa.c index 0fe88519..2fb6ac40 100644 --- a/src/cressi_goa.c +++ b/src/cressi_goa.c @@ -572,6 +572,7 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v devinfo.model = model; devinfo.firmware = firmware; devinfo.serial = serial; + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate memory for the logbook data. diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c index b1ec8b4d..eccf2de1 100644 --- a/src/cressi_leonardo.c +++ b/src/cressi_leonardo.c @@ -383,6 +383,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = data[0]; devinfo.firmware = 0; devinfo.serial = array_uint24_le (data + 1); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return DC_STATUS_SUCCESS; diff --git a/src/deepblu_cosmiq.c b/src/deepblu_cosmiq.c index 78c91ce6..1abc2f0b 100644 --- a/src/deepblu_cosmiq.c +++ b/src/deepblu_cosmiq.c @@ -408,6 +408,7 @@ deepblu_cosmiq_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac devinfo.model = 0; devinfo.firmware = fw[0] & 0x3F; devinfo.serial = array_uint32_le (mac); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); unsigned char ndives = 0; diff --git a/src/deepsix_excursion.c b/src/deepsix_excursion.c index 1ed78677..ef0c8873 100644 --- a/src/deepsix_excursion.c +++ b/src/deepsix_excursion.c @@ -310,6 +310,7 @@ deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t call devinfo.model = 0; devinfo.firmware = array_uint16_be (rsp_software + 4); devinfo.serial = array_convert_str2num (rsp_serial + 3, sizeof(rsp_serial) - 3); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Firmware version 6+ uses the new commands. diff --git a/src/descriptor.c b/src/descriptor.c index fd6a1509..9cf40e27 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -97,6 +97,38 @@ static const dc_iterator_vtable_t dc_descriptor_iterator_vtable = { * actually used to identify individual models, identical values are assigned. */ +/* Hardware-identifier to descriptor mapping. + * + * Some device families share a coarse model number (DC_EVENT_DEVINFO.model) + * across multiple marketed products; the hardware identifier (hw_id) + * from DC_EVENT_DEVINFO can refine the selection to a specific descriptor. + * This table is best-effort: the FWID is only available on live download and + * may change across firmware updates. Consumers should treat a NULL result + * from dc_descriptor_find_by_hw_id() as "use the coarse model descriptor". */ +typedef struct { + dc_family_t family; + unsigned int model; + unsigned int hw_id; + const char *product; +} dc_hw_id_entry_t; + +static const dc_hw_id_entry_t g_hw_id_map[] = { + /* Shearwater Petrel family. + * Petrel 1 (model=3): hardware ids 0x0404, 0x0909 */ + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x0404, "Petrel"}, + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x0909, "Petrel"}, + /* Petrel 2 (model=3): hardware ids 0x0505, 0x0808, 0x0838, 0x08A5, + * 0x0B0B, 0x7828, 0x7B2C, 0x8838 */ + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x0505, "Petrel 2"}, + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x0808, "Petrel 2"}, + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x0838, "Petrel 2"}, + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x08A5, "Petrel 2"}, + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x0B0B, "Petrel 2"}, + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x7828, "Petrel 2"}, + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x7B2C, "Petrel 2"}, + {DC_FAMILY_SHEARWATER_PETREL, 3, 0x8838, "Petrel 2"}, +}; + static const dc_descriptor_t g_descriptors[] = { /* Suunto Solution */ {"Suunto", "Solution", DC_FAMILY_SUUNTO_SOLUTION, 0, DC_TRANSPORT_SERIAL, NULL}, @@ -1053,6 +1085,36 @@ dc_descriptor_free (dc_descriptor_t *descriptor) return; } +dc_descriptor_t * +dc_descriptor_find_by_hw_id (dc_family_t family, unsigned int hw_id) +{ + if (hw_id == 0) + return NULL; + + for (size_t i = 0; i < C_ARRAY_SIZE(g_hw_id_map); i++) { + if (g_hw_id_map[i].family != family || g_hw_id_map[i].hw_id != hw_id) + continue; + + /* Matched — find the corresponding entry in g_descriptors[] by + * family, model, and product name. Returning a direct pointer + * into the static table is safe: dc_descriptor_free() is a + * no-op and g_descriptors[] has static storage duration. */ + for (size_t j = 0; j < C_ARRAY_SIZE(g_descriptors); j++) { + if (g_descriptors[j].type == family && + g_descriptors[j].model == g_hw_id_map[i].model && + strcmp(g_descriptors[j].product, g_hw_id_map[i].product) == 0) { + return (dc_descriptor_t *) &g_descriptors[j]; + } + } + /* hw_id matched the map but the descriptor was not found — + * product name mismatch or g_descriptors[] was edited without + * updating g_hw_id_map[]. */ + return NULL; + } + + return NULL; +} + const char * dc_descriptor_get_vendor (const dc_descriptor_t *descriptor) { diff --git a/src/diverite_nitekq.c b/src/diverite_nitekq.c index aee0784f..47dd4a6e 100644 --- a/src/diverite_nitekq.c +++ b/src/diverite_nitekq.c @@ -266,6 +266,7 @@ diverite_nitekq_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = 0; devinfo.firmware = 0; devinfo.serial = array_uint32_be (device->version + 0x0A); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Send the upload request. It's not clear whether this request is diff --git a/src/divesoft_freedom.c b/src/divesoft_freedom.c index 80117769..f60c5594 100644 --- a/src/divesoft_freedom.c +++ b/src/divesoft_freedom.c @@ -418,6 +418,7 @@ divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb devinfo.model = rsp_version[0]; devinfo.firmware = array_uint24_be (rsp_version + 3); devinfo.serial = array_convert_str2num (rsp_version + 10 + 5, 11); + devinfo.hw_id = 0; device_event_emit(abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate memory for the dive list. diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index 51904b52..2799e704 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -460,6 +460,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb devinfo.model = array_uint16_le (packet); devinfo.firmware = array_uint32_le (packet + 2); devinfo.serial = array_uint32_le (packet + 6); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. diff --git a/src/garmin.c b/src/garmin.c index 22482685..bc78871f 100644 --- a/src/garmin.c +++ b/src/garmin.c @@ -719,6 +719,7 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void if (devinfo_p) { // first time we came through here, let's emit the // devinfo and vendor events + devinfo_p->hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, devinfo_p); devinfo_p = NULL; } diff --git a/src/halcyon_symbios.c b/src/halcyon_symbios.c index 07d6c45e..3af612be 100644 --- a/src/halcyon_symbios.c +++ b/src/halcyon_symbios.c @@ -471,6 +471,7 @@ halcyon_symbios_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba devinfo.model = info[5]; devinfo.firmware = array_uint24_be (info + 16); devinfo.serial = array_uint32_le (info); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); DEBUG (abstract->context, "Device: model=%u, serial=%u, firmware=%u.%u.%u, hw=%u, bt=%u.%u, battery=%u, pressure=%u, errorbits=%u", diff --git a/src/hw_ostc.c b/src/hw_ostc.c index 6fdc6ebf..9c525c3f 100644 --- a/src/hw_ostc.c +++ b/src/hw_ostc.c @@ -284,6 +284,7 @@ hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = 1; // OSTC Mk2 else devinfo.model = 0; // OSTC + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return DC_STATUS_SUCCESS; diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 0e48b799..0b791a22 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -854,6 +854,7 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi else devinfo.model = OSTC3; } + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate memory. @@ -1894,6 +1895,7 @@ hw_ostc3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) else devinfo.model = OSTC3; } + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate the required amount of memory. diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 48bdabef..d1b78917 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -36,6 +36,7 @@ dc_descriptor_get_type dc_descriptor_get_model dc_descriptor_get_transports dc_descriptor_filter +dc_descriptor_find_by_hw_id dc_iostream_get_transport dc_iostream_set_timeout diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c index 74916583..ef2ced1d 100644 --- a/src/liquivision_lynx.c +++ b/src/liquivision_lynx.c @@ -377,6 +377,7 @@ liquivision_lynx_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = array_uint16_le (device->info + 0); devinfo.firmware = 0; devinfo.serial = array_uint32_le (device->more + 0); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate the required amount of memory. @@ -410,6 +411,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb devinfo.model = model; devinfo.firmware = 0; devinfo.serial = array_uint32_le (device->more + 0); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Read the config segment. diff --git a/src/mares_darwin.c b/src/mares_darwin.c index cac1717e..cec312e1 100644 --- a/src/mares_darwin.c +++ b/src/mares_darwin.c @@ -213,6 +213,7 @@ mares_darwin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = device->model; devinfo.firmware = 0; devinfo.serial = array_uint16_be (data + 8); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return status; diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 9a55769f..a851818a 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -822,6 +822,7 @@ mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = device->model; devinfo.firmware = 0; devinfo.serial = array_uint32_le (data + 0x0C); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return status; @@ -848,6 +849,7 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb devinfo.model = device->model; devinfo.firmware = 0; devinfo.serial = array_uint32_le (serial); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Enable progress notifications. @@ -1101,6 +1103,7 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca devinfo.model = device->model; devinfo.firmware = 0; devinfo.serial = serial; + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Erase the buffer. diff --git a/src/mares_nemo.c b/src/mares_nemo.c index 4b6929bb..3185b4bb 100644 --- a/src/mares_nemo.c +++ b/src/mares_nemo.c @@ -251,6 +251,7 @@ mares_nemo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = data[1]; devinfo.firmware = 0; devinfo.serial = array_uint16_be (data + 8); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return DC_STATUS_SUCCESS; diff --git a/src/mares_puck.c b/src/mares_puck.c index 14a25a20..4a3561df 100644 --- a/src/mares_puck.c +++ b/src/mares_puck.c @@ -216,6 +216,7 @@ mares_puck_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = data[1]; devinfo.firmware = 0; devinfo.serial = array_uint16_be (data + 8); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return status; diff --git a/src/mclean_extreme.c b/src/mclean_extreme.c index 8dbe7c23..f1719374 100644 --- a/src/mclean_extreme.c +++ b/src/mclean_extreme.c @@ -559,6 +559,7 @@ mclean_extreme_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback devinfo.model = 0; devinfo.firmware = array_uint32_le (firmware); devinfo.serial = hashcode (serial, serial_len); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Read the computer configuration. diff --git a/src/oceanic_common.c b/src/oceanic_common.c index fc80bc11..a0f02573 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -258,6 +258,7 @@ oceanic_common_device_devinfo (dc_device_t *abstract, dc_event_progress_t *progr (id[11] & 0x0F) * 100000 + ((id[11] & 0xF0) >> 4) * 10000 + (id[12] & 0x0F) * 1000 + ((id[12] & 0xF0) >> 4) * 100 + (id[13] & 0x0F) * 10 + ((id[13] & 0xF0) >> 4) * 1; + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return status; diff --git a/src/oceans_s1.c b/src/oceans_s1.c index 125d0908..2337c9ad 100644 --- a/src/oceans_s1.c +++ b/src/oceans_s1.c @@ -575,6 +575,7 @@ oceans_s1_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, vo devinfo.model = 0; devinfo.firmware = major << 16 | minor; devinfo.serial = 0; + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); dc_buffer_t *buffer = dc_buffer_new (4096); diff --git a/src/pelagic_i330r.c b/src/pelagic_i330r.c index 8cbbed95..a22b3b23 100644 --- a/src/pelagic_i330r.c +++ b/src/pelagic_i330r.c @@ -593,6 +593,7 @@ pelagic_i330r_device_devinfo (dc_device_t *abstract, dc_event_progress_t *progre bcd2dec (device->hwcal[12]) + bcd2dec (device->hwcal[13]) * 100 + bcd2dec (device->hwcal[14]) * 10000; + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return DC_STATUS_SUCCESS; diff --git a/src/reefnet_sensus.c b/src/reefnet_sensus.c index 0e0345c5..feb58ced 100644 --- a/src/reefnet_sensus.c +++ b/src/reefnet_sensus.c @@ -242,6 +242,7 @@ reefnet_sensus_handshake (reefnet_sensus_device_t *device) devinfo.model = handshake[2] - '0'; devinfo.firmware = handshake[3] - '0'; devinfo.serial = array_uint16_le (handshake + 6); + devinfo.hw_id = 0; device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. diff --git a/src/reefnet_sensuspro.c b/src/reefnet_sensuspro.c index 4ba4b20c..636ad45f 100644 --- a/src/reefnet_sensuspro.c +++ b/src/reefnet_sensuspro.c @@ -203,6 +203,7 @@ reefnet_sensuspro_handshake (reefnet_sensuspro_device_t *device) devinfo.model = handshake[0]; devinfo.firmware = handshake[1]; devinfo.serial = array_uint16_le (handshake + 4); + devinfo.hw_id = 0; device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. diff --git a/src/reefnet_sensusultra.c b/src/reefnet_sensusultra.c index a29d853c..896bfd45 100644 --- a/src/reefnet_sensusultra.c +++ b/src/reefnet_sensusultra.c @@ -263,6 +263,7 @@ reefnet_sensusultra_handshake (reefnet_sensusultra_device_t *device, unsigned sh devinfo.model = handshake[1]; devinfo.firmware = handshake[0]; devinfo.serial = array_uint16_le (handshake + 2); + devinfo.hw_id = 0; device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. diff --git a/src/seac_screen.c b/src/seac_screen.c index be33a794..424c0249 100644 --- a/src/seac_screen.c +++ b/src/seac_screen.c @@ -492,6 +492,7 @@ seac_screen_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.firmware = array_uint32_le (device->info + 0x11C); } devinfo.serial = array_uint32_le (device->info + 0x10); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. @@ -531,6 +532,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, devinfo.firmware = array_uint32_le (device->info + 0x11C); } devinfo.serial = array_uint32_le (device->info + 0x010); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. diff --git a/src/shearwater_common.c b/src/shearwater_common.c index 22ae3ca6..0e838f73 100644 --- a/src/shearwater_common.c +++ b/src/shearwater_common.c @@ -762,110 +762,3 @@ shearwater_common_timesync_utc (shearwater_common_device_t *device, const dc_dat return status; } - -dc_status_t shearwater_common_get_model(shearwater_common_device_t *device, unsigned int *model) -{ - // Read the hardware type. - unsigned char rsp_hardware[2] = {0}; - dc_status_t status = shearwater_common_rdbi (device, ID_HARDWARE, rsp_hardware, sizeof(rsp_hardware), NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (device->base.context, "Failed to read the hardware type."); - return status; - } - - // Convert and map to the model number. - unsigned int hardware = array_uint16_be (rsp_hardware); - - DEBUG(device->base.context, "Hardware type: 0x%04x", hardware); - - switch (hardware) { - case 0x0101: - case 0x0202: - *model = PREDATOR; - break; - case 0x0404: - case 0x0909: - *model = PETREL; - break; - case 0x0505: - case 0x0808: - case 0x0838: - case 0x08A5: - case 0x0B0B: - case 0x7828: - case 0x7B2C: - case 0x8838: - *model = PETREL2; - break; - case 0xB407: - case 0xB429: - case 0xB469: - case 0x3C3D: - *model = PETREL3; - break; - case 0x0606: - case 0x0A0A: - *model = NERD; - break; - case 0x0E0D: - case 0x7E2D: - *model = NERD2; - break; - case 0x0707: - *model = PERDIX; - break; - case 0x0C0D: - case 0x425B: - case 0x7C2D: - case 0x8D6C: - *model = PERDIXAI; - break; - case 0x704C: - case 0x924C: - case 0x9C64: - case 0xC407: - case 0xC429: - case 0xC964: - *model = PERDIX2; - break; - case 0x39C2: - case 0x4AB1: - *model = PERDIX3; - break; - case 0x1F0A: - case 0x1F0F: - case 0x0F0F: - case 0x1F10: - case 0x1F1A: - *model = TERIC; - break; - case 0x1512: - case 0x1613: - case 0x2623: - case 0x63A5: - *model = PEREGRINE; - break; - case 0x1712: - case 0x813A: - *model = PEREGRINE_TX; - break; - case 0xC0E0: - *model = TERN; - break; - default: - // Unknown hardware type: fall back to reading the model number directly from the device. - WARNING (device->base.context, "Unknown hardware type 0x%04x, falling back to ID_MODEL.", hardware); - { - unsigned char rsp_model = 0; - dc_status_t rc = shearwater_common_rdbi (device, ID_MODEL, &rsp_model, sizeof(rsp_model), NULL); - if (rc != DC_STATUS_SUCCESS) { - ERROR (device->base.context, "Failed to read the model number."); - return rc; - } - *model = rsp_model; - } - break; - } - - return status; -} diff --git a/src/shearwater_common.h b/src/shearwater_common.h index 9808875b..cb870e09 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -46,6 +46,9 @@ extern "C" { #define PREDATOR 2 #define PETREL 3 +/* PETREL2: Petrel 2 hardware reports ID_MODEL=PETREL (3); there is no + * distinct Product-Version value for it. FWID-based sub-model distinction + * is exposed via devinfo.hw_id on live download. */ #define PETREL2 PETREL #define NERD 4 #define PERDIX 5 @@ -93,8 +96,6 @@ dc_status_t shearwater_common_can_wdbi (shearwater_common_device_t *device, dc_b dc_status_t shearwater_common_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime); -dc_status_t shearwater_common_get_model(shearwater_common_device_t *device, unsigned int *model); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 3a9af132..da53e9f1 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -190,16 +190,33 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Convert to a number. unsigned int firmware = str2num (rsp_firmware, rsp_firmware_length, 1); - unsigned int model = 0; - rc = shearwater_common_get_model (&device->base, &model); - if (rc != DC_STATUS_SUCCESS) + // Read the model number (ID_MODEL, RDBI 0x8060) — Product-Version byte, + // authoritative for the coarse product family (aligns with upstream libdc). + unsigned char rsp_model = 0; + rc = shearwater_common_rdbi (&device->base, ID_MODEL, &rsp_model, sizeof(rsp_model), NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the model number."); return rc; + } + + // Attempt to read the hardware type (ID_HARDWARE, RDBI 0x8050) for + // consumer-side sub-model differentiation (e.g. Petrel 1 vs Petrel 2). + // Best-effort: FWID can change across firmware updates and stored logs + // do not carry it. Failure is non-fatal; hw_id remains 0. + unsigned char rsp_hardware[2] = {0}; + unsigned int fwid = 0; + dc_status_t hw_rc = shearwater_common_rdbi (&device->base, ID_HARDWARE, rsp_hardware, sizeof(rsp_hardware), NULL); + if (hw_rc == DC_STATUS_SUCCESS) { + fwid = array_uint16_be (rsp_hardware); + DEBUG (abstract->context, "Hardware type (FWID): 0x%04x", fwid); + } // Emit a device info event. - dc_event_devinfo_t devinfo; - devinfo.model = model; + dc_event_devinfo_t devinfo = {0}; + devinfo.model = rsp_model; devinfo.firmware = firmware; devinfo.serial = array_uint32_be (serial); + devinfo.hw_id = fwid; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Read the logbook type @@ -356,15 +373,17 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call static dc_status_t shearwater_petrel_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime) { - dc_status_t status = DC_STATUS_SUCCESS; shearwater_common_device_t *device = (shearwater_common_device_t *) abstract; - unsigned int model = 0; - status = shearwater_common_get_model (device, &model); - if (status != DC_STATUS_SUCCESS) + // Read ID_MODEL (RDBI 0x8060) directly to determine time-sync variant. + unsigned char rsp_model = 0; + dc_status_t status = shearwater_common_rdbi (device, ID_MODEL, &rsp_model, sizeof(rsp_model), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the model number."); return status; + } - if (model == TERIC) { + if (rsp_model == TERIC) { return shearwater_common_timesync_utc (device, datetime); } else { return shearwater_common_timesync_local (device, datetime); diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c index 2234a5d4..159bbc78 100644 --- a/src/shearwater_predator.c +++ b/src/shearwater_predator.c @@ -136,11 +136,14 @@ shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) } // Emit a device info event. + // hw_id is not available from the Predator memory dump + // (no live RDBI 0x8050 read in this path); set to 0 (unknown). unsigned char *data = dc_buffer_get_data (buffer); - dc_event_devinfo_t devinfo; + dc_event_devinfo_t devinfo = {0}; devinfo.model = data[0x2000D]; devinfo.firmware = bcd2dec (data[0x2000A]); devinfo.serial = array_uint32_be (data + 0x20002); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return status; diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 10b2f13a..32437eb0 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -858,6 +858,11 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } // Get the correct model number from the final block. + // Product-Version byte from the device's final record — authoritative for + // the coarse product family. This value is not the FWID; stored logs do not + // carry the FWID, so sub-model distinctions (e.g. Petrel 1 vs Petrel 2) are + // not recoverable on re-parse. Consumer-side refinement uses the FWID + // exposed via devinfo.hw_id on live download. if (parser->final != UNDEFINED) { parser->model = data[parser->final + 13]; DEBUG (abstract->context, "Device: model=%u, serial=%u, firmware=%u", diff --git a/src/sporasub_sp2.c b/src/sporasub_sp2.c index e34966f6..4b4462d8 100644 --- a/src/sporasub_sp2.c +++ b/src/sporasub_sp2.c @@ -357,6 +357,7 @@ sporasub_sp2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = 0; devinfo.firmware = 0; devinfo.serial = array_uint16_be (device->version + 1); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. diff --git a/src/suunto_common2.c b/src/suunto_common2.c index f80bda84..54258f24 100644 --- a/src/suunto_common2.c +++ b/src/suunto_common2.c @@ -265,6 +265,7 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac devinfo.model = device->version[0]; devinfo.firmware = array_uint24_be (device->version + 1); devinfo.serial = array_convert_bin2dec (serial, 4); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Read the header bytes. diff --git a/src/suunto_eon.c b/src/suunto_eon.c index a7f09dd9..0be4d4ad 100644 --- a/src/suunto_eon.c +++ b/src/suunto_eon.c @@ -186,6 +186,7 @@ suunto_eon_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = 0; devinfo.firmware = 0; devinfo.serial = array_convert_bcd2dec (answer + 244, 3); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return DC_STATUS_SUCCESS; diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index a18d27c3..c765c6f9 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -717,6 +717,7 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac devinfo.model = eon->model; devinfo.firmware = array_uint32_be (eon->version + 0x20); devinfo.serial = array_convert_str2num(eon->version + 0x10, 16); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); rc = get_file_list(eon, &de); diff --git a/src/suunto_solution.c b/src/suunto_solution.c index e3e6b1cf..c2b4959c 100644 --- a/src/suunto_solution.c +++ b/src/suunto_solution.c @@ -226,6 +226,7 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = 0; devinfo.firmware = 0; devinfo.serial = array_convert_bcd2dec (data + 0x1D, 3); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return DC_STATUS_SUCCESS; diff --git a/src/suunto_vyper.c b/src/suunto_vyper.c index 90ef7028..185339dc 100644 --- a/src/suunto_vyper.c +++ b/src/suunto_vyper.c @@ -456,6 +456,7 @@ suunto_vyper_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.firmware = data[hoffset + 1]; devinfo.serial = 0; devinfo.serial = array_convert_bin2dec (data + hoffset + 2, 4); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); return status; @@ -502,6 +503,7 @@ suunto_vyper_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, devinfo.model = header[hoffset + 0]; devinfo.firmware = header[hoffset + 1]; devinfo.serial = array_convert_bin2dec (header + hoffset + 2, 4); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Allocate a memory buffer. diff --git a/src/tecdiving_divecomputereu.c b/src/tecdiving_divecomputereu.c index a430e0f2..a3b2077b 100644 --- a/src/tecdiving_divecomputereu.c +++ b/src/tecdiving_divecomputereu.c @@ -451,6 +451,7 @@ tecdiving_divecomputereu_device_foreach (dc_device_t *abstract, dc_dive_callback devinfo.model = 0; devinfo.firmware = 0; devinfo.serial = array_uint16_be (device->version + 0x22) << 16 | array_uint16_be (device->version + 0x26); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Emit a vendor event. diff --git a/src/uwatec_aladin.c b/src/uwatec_aladin.c index ee154331..21c0ff87 100644 --- a/src/uwatec_aladin.c +++ b/src/uwatec_aladin.c @@ -249,6 +249,7 @@ uwatec_aladin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback devinfo.model = data[HEADER + 0x7bc]; devinfo.firmware = 0; devinfo.serial = array_uint24_be (data + HEADER + 0x7ed); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); rc = uwatec_aladin_extract_dives (abstract, diff --git a/src/uwatec_memomouse.c b/src/uwatec_memomouse.c index e605578e..7c44aadb 100644 --- a/src/uwatec_memomouse.c +++ b/src/uwatec_memomouse.c @@ -503,6 +503,7 @@ uwatec_memomouse_extract_dives (dc_device_t *abstract, const unsigned char data[ devinfo.model = data[current + 3]; devinfo.firmware = 0; devinfo.serial = array_uint24_be (data + current); + devinfo.hw_id = 0; device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); } diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index 1a4734a7..2c76f7c6 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -624,6 +624,7 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) devinfo.model = model[0]; devinfo.firmware = bcd2dec (software[0]); devinfo.serial = array_uint32_le (serial); + devinfo.hw_id = 0; device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); // Command parameters.