Garmin: add MTP support
Create parallel helper functions that use libmtp to walk the file tree on the device and to then read a specific file from the device into our dc_buffer. MTP is not a file system, it's an object storage, that just happens to allow object names and parent/child relationships between objects. As a result we need to remember those file ids for MTP downloads. The mtp_get_file_list function is rather complex as it includes both the initial communication with the device and the code to walk the object tree and then create the list of file. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
05e066b2ff
commit
ea3e542450
13
configure.ac
13
configure.ac
@ -100,6 +100,19 @@ AS_IF([test "x$with_libusb" != "xno"], [
|
|||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# Checks for MTP support.
|
||||||
|
AC_ARG_WITH([libmtp],
|
||||||
|
[AS_HELP_STRING([--without-libmtp],
|
||||||
|
[Build without the libmtp library])],
|
||||||
|
[], [with_libmtp=auto])
|
||||||
|
AS_IF([test "x$with_libmtp" != "xno"], [
|
||||||
|
PKG_CHECK_MODULES([LIBMTP], [libmtp], [have_libmtp=yes], [have_libmtp=no])
|
||||||
|
AS_IF([test "x$have_libmtp" = "xyes"], [
|
||||||
|
AC_DEFINE([HAVE_LIBMTP], [1], [libmtp library])
|
||||||
|
DEPENDENCIES="$DEPENDENCIES libmtp"
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
# Checks for HIDAPI support.
|
# Checks for HIDAPI support.
|
||||||
AC_ARG_WITH([hidapi],
|
AC_ARG_WITH([hidapi],
|
||||||
[AS_HELP_STRING([--without-hidapi],
|
[AS_HELP_STRING([--without-hidapi],
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
||||||
AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS) $(BLUEZ_CFLAGS)
|
AM_CFLAGS = $(LIBUSB_CFLAGS) $(LIBMTP_CFLAGS) $(HIDAPI_CFLAGS) $(BLUEZ_CFLAGS)
|
||||||
|
|
||||||
lib_LTLIBRARIES = libdivecomputer.la
|
lib_LTLIBRARIES = libdivecomputer.la
|
||||||
|
|
||||||
libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm
|
libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(LIBMTP_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm
|
||||||
libdivecomputer_la_LDFLAGS = \
|
libdivecomputer_la_LDFLAGS = \
|
||||||
-version-info $(DC_VERSION_LIBTOOL) \
|
-version-info $(DC_VERSION_LIBTOOL) \
|
||||||
-no-undefined \
|
-no-undefined \
|
||||||
|
|||||||
223
src/garmin.c
223
src/garmin.c
@ -35,10 +35,27 @@
|
|||||||
#include "device-private.h"
|
#include "device-private.h"
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMTP
|
||||||
|
#include "libmtp.h"
|
||||||
|
|
||||||
|
#define GARMIN_VENDOR 0x091E
|
||||||
|
#define DESCENT_MK2 0x4CBA
|
||||||
|
#define DESCENT_MK2_APAC 0x4E76
|
||||||
|
|
||||||
|
// deal with ancient libmpt found on older Linux distros
|
||||||
|
#ifndef LIBMTP_FILES_AND_FOLDERS_ROOT
|
||||||
|
#define LIBMTP_FILES_AND_FOLDERS_ROOT 0xffffffff
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct garmin_device_t {
|
typedef struct garmin_device_t {
|
||||||
dc_device_t base;
|
dc_device_t base;
|
||||||
dc_iostream_t *iostream;
|
dc_iostream_t *iostream;
|
||||||
unsigned char fingerprint[FIT_NAME_SIZE];
|
unsigned char fingerprint[FIT_NAME_SIZE];
|
||||||
|
#ifdef HAVE_LIBMTP
|
||||||
|
unsigned char use_mtp;
|
||||||
|
LIBMTP_mtpdevice_t *mtp_device;
|
||||||
|
#endif
|
||||||
} garmin_device_t;
|
} garmin_device_t;
|
||||||
|
|
||||||
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_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||||
@ -77,6 +94,14 @@ garmin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *ios
|
|||||||
device->iostream = iostream;
|
device->iostream = iostream;
|
||||||
memset(device->fingerprint, 0, sizeof(device->fingerprint));
|
memset(device->fingerprint, 0, sizeof(device->fingerprint));
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMTP
|
||||||
|
// 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);
|
||||||
|
DEBUG(context, "Found Garmin with model 0x%x which is a %s\n", model, (device->use_mtp ? "Mk2/Mk2i" : "Mk1"));
|
||||||
|
#endif
|
||||||
|
|
||||||
*out = (dc_device_t *) device;
|
*out = (dc_device_t *) device;
|
||||||
|
|
||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
@ -104,19 +129,25 @@ garmin_device_close (dc_device_t *abstract)
|
|||||||
{
|
{
|
||||||
dc_status_t status = DC_STATUS_SUCCESS;
|
dc_status_t status = DC_STATUS_SUCCESS;
|
||||||
garmin_device_t *device = (garmin_device_t *) abstract;
|
garmin_device_t *device = (garmin_device_t *) abstract;
|
||||||
|
#ifdef HAVE_LIBMTP
|
||||||
|
if (device->use_mtp && device->mtp_device)
|
||||||
|
LIBMTP_Release_Device(device->mtp_device);
|
||||||
|
#endif
|
||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct file_list {
|
struct file_list {
|
||||||
int nr, allocated;
|
int nr, allocated;
|
||||||
struct fit_name *array;
|
struct fit_file *array;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int name_cmp(const void *a, const void *b)
|
static int name_cmp(const void *_a, const void *_b)
|
||||||
{
|
{
|
||||||
|
const struct fit_file *a = _a;
|
||||||
|
const struct fit_file *b = _b;
|
||||||
|
|
||||||
// Sort reverse string ordering (newest first), so use 'b,a'
|
// Sort reverse string ordering (newest first), so use 'b,a'
|
||||||
return strcmp(b,a);
|
return strcmp(b->name, a->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -148,7 +179,7 @@ static dc_status_t
|
|||||||
make_space(struct file_list *files)
|
make_space(struct file_list *files)
|
||||||
{
|
{
|
||||||
if (files->nr == files->allocated) {
|
if (files->nr == files->allocated) {
|
||||||
struct fit_name *array;
|
struct fit_file *array;
|
||||||
int n = 3*(files->allocated + 8)/2;
|
int n = 3*(files->allocated + 8)/2;
|
||||||
size_t new_size;
|
size_t new_size;
|
||||||
|
|
||||||
@ -156,7 +187,6 @@ make_space(struct file_list *files)
|
|||||||
array = realloc(files->array, new_size);
|
array = realloc(files->array, new_size);
|
||||||
if (!array)
|
if (!array)
|
||||||
return DC_STATUS_NOMEMORY;
|
return DC_STATUS_NOMEMORY;
|
||||||
|
|
||||||
files->array = array;
|
files->array = array;
|
||||||
files->allocated = n;
|
files->allocated = n;
|
||||||
}
|
}
|
||||||
@ -164,7 +194,7 @@ make_space(struct file_list *files)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_name(struct file_list *files, const char *name)
|
add_name(struct file_list *files, const char *name, unsigned int mtp_id)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* NOTE! This depends on the zero-padding that strncpy does.
|
* NOTE! This depends on the zero-padding that strncpy does.
|
||||||
@ -172,9 +202,10 @@ add_name(struct file_list *files, const char *name)
|
|||||||
* strncpy() doesn't just limit the size of the copy, it
|
* strncpy() doesn't just limit the size of the copy, it
|
||||||
* will zero-pad the end of the result buffer.
|
* will zero-pad the end of the result buffer.
|
||||||
*/
|
*/
|
||||||
struct fit_name *entry = files->array + files->nr++;
|
struct fit_file *entry = files->array + files->nr++;
|
||||||
strncpy(entry->name, name, FIT_NAME_SIZE);
|
strncpy(entry->name, name, FIT_NAME_SIZE);
|
||||||
entry->name[FIT_NAME_SIZE] = 0; // ensure it's null-terminated
|
entry->name[FIT_NAME_SIZE] = 0; // ensure it's null-terminated
|
||||||
|
entry->mtp_id = mtp_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
@ -190,14 +221,140 @@ get_file_list(dc_device_t *abstract, DIR *dir, struct file_list *files)
|
|||||||
dc_status_t rc = make_space(files);
|
dc_status_t rc = make_space(files);
|
||||||
if (rc != DC_STATUS_SUCCESS)
|
if (rc != DC_STATUS_SUCCESS)
|
||||||
return rc;
|
return rc;
|
||||||
add_name(files, de->d_name);
|
add_name(files, de->d_name, 0);
|
||||||
}
|
}
|
||||||
DEBUG(abstract->context, "Found %d files", files->nr);
|
DEBUG(abstract->context, "Found %d files", files->nr);
|
||||||
|
|
||||||
qsort(files->array, files->nr, sizeof(struct fit_name), name_cmp);
|
qsort(files->array, files->nr, sizeof(struct fit_file), name_cmp);
|
||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMTP
|
||||||
|
static unsigned int
|
||||||
|
mtp_get_folder_id(dc_device_t *abstract, LIBMTP_mtpdevice_t *device, LIBMTP_devicestorage_t *storage, const char *folder, unsigned int parent_id)
|
||||||
|
{
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: looking for folder %s under parent id %d", folder, parent_id);
|
||||||
|
// memory management is interesting here - we have to always walk the list returned and destroy them one by one
|
||||||
|
unsigned int folder_id = LIBMTP_FILES_AND_FOLDERS_ROOT;
|
||||||
|
LIBMTP_file_t* files = LIBMTP_Get_Files_And_Folders (device, storage->id, parent_id);
|
||||||
|
while (files != NULL) {
|
||||||
|
LIBMTP_file_t* mtp_file = files;
|
||||||
|
if (mtp_file->filetype == LIBMTP_FILETYPE_FOLDER && mtp_file->filename && !strncasecmp(mtp_file->filename, folder, strlen(folder))) {
|
||||||
|
folder_id = mtp_file->item_id;
|
||||||
|
}
|
||||||
|
files = files->next;
|
||||||
|
LIBMTP_destroy_file_t(mtp_file);
|
||||||
|
}
|
||||||
|
return folder_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
mtp_get_file_list(dc_device_t *abstract, struct file_list *files)
|
||||||
|
{
|
||||||
|
garmin_device_t *device = (garmin_device_t *)abstract;
|
||||||
|
LIBMTP_raw_device_t *rawdevices;
|
||||||
|
int numrawdevices;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
LIBMTP_Init();
|
||||||
|
DEBUG(abstract->context, "Attempting to connect to mtp device");
|
||||||
|
|
||||||
|
switch (LIBMTP_Detect_Raw_Devices(&rawdevices, &numrawdevices)) {
|
||||||
|
case LIBMTP_ERROR_NO_DEVICE_ATTACHED:
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: no device found");
|
||||||
|
return DC_STATUS_NODEVICE;
|
||||||
|
case LIBMTP_ERROR_CONNECTING:
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: error connecting");
|
||||||
|
return DC_STATUS_NOACCESS;
|
||||||
|
case LIBMTP_ERROR_MEMORY_ALLOCATION:
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: memory allocation error");
|
||||||
|
return DC_STATUS_NOMEMORY;
|
||||||
|
case LIBMTP_ERROR_GENERAL: // Unknown general errors - that's bad
|
||||||
|
default:
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: unknown error");
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
case LIBMTP_ERROR_NONE:
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: successfully connected with %d raw devices", numrawdevices);
|
||||||
|
}
|
||||||
|
/* iterate through connected MTP devices */
|
||||||
|
for (i = 0; i < numrawdevices; i++) {
|
||||||
|
LIBMTP_devicestorage_t *storage;
|
||||||
|
// we only want to read from a Garmin Descent Mk2 device at this point
|
||||||
|
if (rawdevices[i].device_entry.vendor_id != GARMIN_VENDOR ||
|
||||||
|
(rawdevices[i].device_entry.product_id != DESCENT_MK2 && rawdevices[i].device_entry.product_id != DESCENT_MK2_APAC)) {
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: skipping raw device %04x/%04x",
|
||||||
|
rawdevices[i].device_entry.vendor_id, rawdevices[i].device_entry.product_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
device->mtp_device = LIBMTP_Open_Raw_Device_Uncached(&rawdevices[i]);
|
||||||
|
if (device->mtp_device == NULL) {
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: unable to open raw device %d", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: succcessfully opened device");
|
||||||
|
for (storage = device->mtp_device->storage; storage != 0; storage = storage->next) {
|
||||||
|
unsigned int garmin_id = mtp_get_folder_id(abstract, device->mtp_device, storage, "Garmin", LIBMTP_FILES_AND_FOLDERS_ROOT);
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: Garmin folder at file_id %d", garmin_id);
|
||||||
|
if (garmin_id == LIBMTP_FILES_AND_FOLDERS_ROOT)
|
||||||
|
continue; // this storage partition didn't have a Garmin folder
|
||||||
|
unsigned int activity_id = mtp_get_folder_id(abstract, device->mtp_device, storage, "Activity", garmin_id);
|
||||||
|
DEBUG(abstract->context, "Garmin/mtp: Activity folder at file_id %d", activity_id);
|
||||||
|
if (activity_id == LIBMTP_FILES_AND_FOLDERS_ROOT)
|
||||||
|
continue; // no Activity folder
|
||||||
|
|
||||||
|
// now walk that folder to create our file_list
|
||||||
|
LIBMTP_file_t* activity_files = LIBMTP_Get_Files_And_Folders (device->mtp_device, storage->id, activity_id);
|
||||||
|
while (activity_files != NULL) {
|
||||||
|
LIBMTP_file_t* mtp_file = activity_files;
|
||||||
|
if (mtp_file->filetype != LIBMTP_FILETYPE_FOLDER && mtp_file->filename) {
|
||||||
|
if (check_filename(abstract, mtp_file->filename)) {
|
||||||
|
dc_status_t rc = make_space(files);
|
||||||
|
if (rc != DC_STATUS_SUCCESS)
|
||||||
|
return rc;
|
||||||
|
add_name(files, mtp_file->filename, mtp_file->item_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activity_files = activity_files->next;
|
||||||
|
LIBMTP_destroy_file_t(mtp_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(rawdevices);
|
||||||
|
DEBUG(abstract->context, "Found %d files", files->nr);
|
||||||
|
|
||||||
|
qsort(files->array, files->nr, sizeof(struct fit_file), name_cmp);
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MTP hands us the file data in chunks which we then just add to our data buffer
|
||||||
|
static uint16_t
|
||||||
|
mtp_put_func(void* params, void* priv, uint32_t sendlen, unsigned char *data, uint32_t *putlen)
|
||||||
|
{
|
||||||
|
dc_buffer_t *file = (dc_buffer_t *)priv;
|
||||||
|
dc_buffer_append(file, data, sendlen);
|
||||||
|
if (putlen)
|
||||||
|
*putlen = sendlen;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the file from the MTP device and store the content in the data buffer
|
||||||
|
static dc_status_t
|
||||||
|
mtp_read_file(garmin_device_t *device, unsigned int file_id, dc_buffer_t *file)
|
||||||
|
{
|
||||||
|
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||||
|
if (!device->mtp_device) {
|
||||||
|
DEBUG(device->base.context, "Garmin/mtp: cannot read file without MTP device");
|
||||||
|
return DC_STATUS_NODEVICE;
|
||||||
|
}
|
||||||
|
DEBUG(device->base.context, "Garmin/mtp: call Get_File_To_Handler");
|
||||||
|
if (LIBMTP_Get_File_To_Handler(device->mtp_device, file_id, &mtp_put_func, (void *) file, NULL, NULL) != 0) {
|
||||||
|
LIBMTP_Dump_Errorstack(device->mtp_device);
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_LIBMTP */
|
||||||
|
|
||||||
#ifndef O_BINARY
|
#ifndef O_BINARY
|
||||||
#define O_BINARY 0
|
#define O_BINARY 0
|
||||||
#endif
|
#endif
|
||||||
@ -245,7 +402,7 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
|
|||||||
struct file_list files = {
|
struct file_list files = {
|
||||||
0, // nr
|
0, // nr
|
||||||
0, // allocated
|
0, // allocated
|
||||||
NULL, // array of names
|
NULL // array of file names / ids
|
||||||
};
|
};
|
||||||
dc_buffer_t *file;
|
dc_buffer_t *file;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
@ -269,20 +426,30 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
|
|||||||
strcpy(pathname + pathlen, "Garmin/Activity");
|
strcpy(pathname + pathlen, "Garmin/Activity");
|
||||||
pathlen += strlen("Garmin/Activity");
|
pathlen += strlen("Garmin/Activity");
|
||||||
|
|
||||||
dir = opendir(pathname);
|
#ifdef HAVE_LIBMTP
|
||||||
if (!dir) {
|
if (device->use_mtp) {
|
||||||
ERROR (abstract->context, "Failed to open directory '%s'.", pathname);
|
rc = mtp_get_file_list(abstract, &files);
|
||||||
return DC_STATUS_IO;
|
if (rc != DC_STATUS_SUCCESS || !files.nr) {
|
||||||
|
free(files.array);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{ // slight coding style violation to deal with the non-MTP case
|
||||||
|
dir = opendir(pathname);
|
||||||
|
if (!dir) {
|
||||||
|
ERROR (abstract->context, "Failed to open directory '%s'.", pathname);
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
}
|
||||||
|
// Get the list of FIT files
|
||||||
|
rc = get_file_list(abstract, dir, &files);
|
||||||
|
closedir(dir);
|
||||||
|
if (rc != DC_STATUS_SUCCESS || !files.nr) {
|
||||||
|
free(files.array);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// We found at least one file
|
||||||
// Get the list of FIT files
|
|
||||||
rc = get_file_list(abstract, dir, &files);
|
|
||||||
closedir(dir);
|
|
||||||
if (rc != DC_STATUS_SUCCESS || !files.nr) {
|
|
||||||
free(files.array);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can we find the fingerprint entry?
|
// Can we find the fingerprint entry?
|
||||||
for (int i = 0; i < files.nr; i++) {
|
for (int i = 0; i < files.nr; i++) {
|
||||||
const char *name = files.array[i].name;
|
const char *name = files.array[i].name;
|
||||||
@ -295,7 +462,6 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
|
|||||||
DEBUG(abstract->context, "Ignoring '%s' and older", name);
|
DEBUG(abstract->context, "Ignoring '%s' and older", name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable progress notifications.
|
// Enable progress notifications.
|
||||||
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||||
progress.maximum = files.nr;
|
progress.maximum = files.nr;
|
||||||
@ -330,8 +496,13 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
|
|||||||
// Reset the membuffer, read the data
|
// Reset the membuffer, read the data
|
||||||
dc_buffer_clear(file);
|
dc_buffer_clear(file);
|
||||||
dc_buffer_append(file, name, FIT_NAME_SIZE);
|
dc_buffer_append(file, name, FIT_NAME_SIZE);
|
||||||
|
#ifdef HAVE_LIBMTP
|
||||||
|
if (device->use_mtp)
|
||||||
|
status = mtp_read_file(device, files.array[i].mtp_id, file);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
status = read_file(pathname, pathlen, name, file);
|
||||||
|
|
||||||
status = read_file(pathname, pathlen, name, file);
|
|
||||||
if (status != DC_STATUS_SUCCESS)
|
if (status != DC_STATUS_SUCCESS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@ -49,8 +49,9 @@ garmin_parser_is_dive (dc_parser_t *abstract, const unsigned char *data, unsigne
|
|||||||
// special fixed header in the parser data too.
|
// special fixed header in the parser data too.
|
||||||
#define FIT_NAME_SIZE 24
|
#define FIT_NAME_SIZE 24
|
||||||
|
|
||||||
struct fit_name {
|
struct fit_file {
|
||||||
char name[FIT_NAME_SIZE + 1];
|
char name[FIT_NAME_SIZE + 1];
|
||||||
|
unsigned int mtp_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -76,10 +76,14 @@ dc_usb_storage_open (dc_iostream_t **out, dc_context_t *context, const char *nam
|
|||||||
if (out == NULL || name == NULL)
|
if (out == NULL || name == NULL)
|
||||||
return DC_STATUS_INVALIDARGS;
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
INFO (context, "Open: name=%s", name);
|
if (*name == '\0') {
|
||||||
if (stat(name, &st) < 0 || !S_ISDIR(st.st_mode))
|
// that indicates an MTP device
|
||||||
return DC_STATUS_NODEVICE;
|
INFO (context, "Open MTP device");
|
||||||
|
} else {
|
||||||
|
INFO (context, "Open: name=%s", name);
|
||||||
|
if (stat(name, &st) < 0 || !S_ISDIR(st.st_mode))
|
||||||
|
return DC_STATUS_NODEVICE;
|
||||||
|
}
|
||||||
// Allocate memory.
|
// Allocate memory.
|
||||||
device = (dc_usbstorage_t *) dc_iostream_allocate (context, &dc_usbstorage_vtable, DC_TRANSPORT_USBSTORAGE);
|
device = (dc_usbstorage_t *) dc_iostream_allocate (context, &dc_usbstorage_vtable, DC_TRANSPORT_USBSTORAGE);
|
||||||
if (device == NULL) {
|
if (device == NULL) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user