Skip to content

Garmin: Add MTP Support for More Models. #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: Subsurface-DS9
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions src/garmin.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
#include "libmtp.h"

#define GARMIN_VENDOR 0x091E
#define DESCENT_MK2 0x4CBA
#define DESCENT_MK2_APAC 0x4E76

#define DESCENT_MK2 3258

// deal with ancient libmpt found on older Linux distros
#ifndef LIBMTP_FILES_AND_FOLDERS_ROOT
Expand All @@ -59,6 +59,24 @@ typedef struct garmin_device_t {
#endif
} garmin_device_t;

// Ids can be found at https://developer.garmin.com/connect-iq/reference-guides/devices-reference/
// (look for 'Part Number')

const garmin_model_t garmin_models[] = {
{ "Descent™ G1 / G1 Solar", 4005, true },
{ "Descent™ G2", 4588, true },
{ "Descent™ Mk1", 2859, false },
{ "Descent™ Mk1 APAC", 2991, false },
{ "Descent™ Mk2(i)", 3258, true },
{ "Descent™ Mk2(i) APAC", 3702, true },
{ "Descent™ Mk2 S", 3542, true },
{ "Descent™ Mk2 S APAC", 3930, true },
{ "Descent™ Mk3(i) 43mm", 4222, true },
{ "Descent™ Mk3(i) 51mm", 4223, true },
{ "Descent™ X50i", 4518, true },
{ NULL, 0, false }
};

static dc_status_t garmin_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t garmin_device_close (dc_device_t *abstract);
Expand Down Expand Up @@ -99,7 +117,7 @@ garmin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *ios
// for a Descent Mk2/Mk2i, we have to use MTP to access its storage;
// for Garmin devices, the model number corresponds to the lower three nibbles of the USB product ID
// in order to have only one entry for the Mk2, we don't use the Mk2/APAC model number in our code
device->use_mtp = (model == (0x0FFF & DESCENT_MK2));
device->use_mtp = model == DESCENT_MK2;
device->mtp_device = NULL;
#endif

Expand Down Expand Up @@ -335,7 +353,17 @@ mtp_get_file_list(dc_device_t *abstract, struct file_list *files)
rawdevices[i].device_entry.vendor_id, rawdevices[i].device_entry.product_id);
continue;
}
if (rawdevices[i].device_entry.product_id != DESCENT_MK2 && rawdevices[i].device_entry.product_id != DESCENT_MK2_APAC) {

bool mtp_capable = false;
for (unsigned j = 0; garmin_models[j].name; j++) {
if ((garmin_models[j].id | 0x4000) == rawdevices[i].device_entry.product_id) {
Copy link
Preview

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number 0x4000 is used to derive the USB product ID flag. Consider defining a named constant (e.g., USB_MTP_FLAG) to improve readability and maintainability.

Suggested change
if ((garmin_models[j].id | 0x4000) == rawdevices[i].device_entry.product_id) {
if ((garmin_models[j].id | USB_MTP_FLAG) == rawdevices[i].device_entry.product_id) {

Copilot uses AI. Check for mistakes.

mtp_capable = garmin_models[j].mtp_capable;

break;
}
}

if (!mtp_capable) {
DEBUG(abstract->context, "Garmin/mtp: skipping Garmin raw device %04x/%04x, as it is not a dive computer / does not support MTP",
rawdevices[i].device_entry.vendor_id, rawdevices[i].device_entry.product_id);
continue;
Expand Down
10 changes: 10 additions & 0 deletions src/garmin.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#ifndef GARMIN_H
#define GARMIN_H

#include <stdbool.h>

#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
Expand All @@ -31,6 +33,14 @@
extern "C" {
#endif /* __cplusplus */

typedef struct {
const char *name;
int id;
bool mtp_capable;
} garmin_model_t;

extern const garmin_model_t garmin_models[];

dc_status_t
garmin_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);

Expand Down
29 changes: 5 additions & 24 deletions src/garmin_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1648,25 +1648,6 @@ static void add_sensor_string(garmin_parser_t *garmin, const char *desc, const s
static dc_status_t
garmin_parser_set_data (garmin_parser_t *garmin, const unsigned char *data, unsigned int size)
{
// Ids can be found at https://developer.garmin.com/connect-iq/reference-guides/devices-reference/
// (look for 'Part Number')
static const struct {
const char *name;
int id;
} models[] = {
{ "Descent™ G1 / G1 Solar", 4005 },
{ "Descent™ G2", 4588 },
{ "Descent™ Mk1", 2859 },
{ "Descent™ Mk1 APAC", 2991 },
{ "Descent™ Mk2(i)", 3258 },
{ "Descent™ Mk2(i) APAC", 3702 },
{ "Descent™ Mk2 S", 3542 },
{ "Descent™ Mk2 S APAC", 3930 },
{ "Descent™ Mk3(i) 43mm", 4222 },
{ "Descent™ Mk3(i) 51mm", 4223 },
{ "Descent™ X50i", 4518 },
};

/* Walk the data once without a callback to set up the core fields */
garmin->callback = NULL;
garmin->userdata = NULL;
Expand All @@ -1683,13 +1664,13 @@ garmin_parser_set_data (garmin_parser_t *garmin, const unsigned char *data, unsi
dc_field_add_string_fmt(&garmin->cache, "Firmware", "%u.%02u",
garmin->dive.firmware / 100, garmin->dive.firmware % 100);
if (garmin->dive.product) {
int i = 0;
for (i = 0; i < C_ARRAY_SIZE(models); i++)
if (models[i].id == garmin->dive.product)
unsigned i;
for (i = 0; garmin_models[i].name; i++)
if (garmin_models[i].id == garmin->dive.product)
break;

if (i < C_ARRAY_SIZE(models))
dc_field_add_string_fmt(&garmin->cache, "Model", "%s", models[i].name);
if (garmin_models[i].name)
dc_field_add_string_fmt(&garmin->cache, "Model", "%s", garmin_models[i].name);
else
dc_field_add_string_fmt(&garmin->cache, "Model", "Unknown model ID: %u", garmin->dive.product);
}
Expand Down
Loading