Merge branch 'garmin-descent' into Subsurface-NG

Merge the initial Garmin Descent Mk1 support.

This actually works well enough to be useful, even though there are a
few ugly details yet to be sorted out.

The download itself is fairly complete, but all event handling is
currently missing (warnings, gas changes, things like that).

Also, because of how libdivecomputer works, the "downloading" of dives
is entirely separate from the "parsing" of dives in the libdivecomputer
world.  And that is actually problematic for the Garmin Descent
downloader, because you actually need to parse the data to even figure
out whether it's actually a dive at all!

The Garmin Descent is also a fitness and general excercise tracker, so
people can (and do) use it for other sports than just diving, and so the
activities we download may end up not being dives at all, but other
events.

But before we parse them, we don't know, and we aren't really supposed
to parse them until after we've passed the data to the application and
it passes it back for parsing.  Nasty chicken-and-egg problem there..

So right now non-diving activities will just show up as very short
and/or shallow dives.

This is fixable by just parsing things an extra time, but I really wish
libdivecomputer would just stop thinking that downloading and parsing
are separate events.

* garmin-descent:
  Add dc_usb_storage_open to the symbols list
  garmin: only record gasmixes for cylinders that aren't enabled
  garmin: don't emit fake device info and vendor event
  garmin: add support for downloading gas mixes
  garmin: add GPS coordinate data and improve parser_get_field() reports
  garmin: actually start using the parsed data
  garmin: turn all the remaining unrecognized fields into DEBUG messages
  garmin: add a lot of new field definitions
  garmin: teach the parser to show undefined values for unknown fields too
  garmin: fix file length header parsing
  garmin: teach the parser about invalid values and more dates
  garmin: some fields are defined in all message types
  Garmin: start parsing definition records
  Garmin Descent Mk1: flesh out the actual downloading part
  Add Garmin Descent Mk1 skeleton
  Add 'USB storage' transport enumeration
This commit is contained in:
Linus Torvalds 2018-08-31 13:24:03 -07:00
commit f6ea5f514a
14 changed files with 1544 additions and 1 deletions

View File

@ -89,6 +89,7 @@ static const backend_table_t g_backends[] = {
{"idive", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03}, {"idive", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03},
{"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0}, {"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0},
{"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0}, {"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0},
{"descentmk1", DC_FAMILY_GARMIN, 0},
}; };
static const transport_table_t g_transports[] = { static const transport_table_t g_transports[] = {
@ -98,6 +99,7 @@ static const transport_table_t g_transports[] = {
{"irda", DC_TRANSPORT_IRDA}, {"irda", DC_TRANSPORT_IRDA},
{"bluetooth", DC_TRANSPORT_BLUETOOTH}, {"bluetooth", DC_TRANSPORT_BLUETOOTH},
{"ble", DC_TRANSPORT_BLE}, {"ble", DC_TRANSPORT_BLE},
{"usbstorage",DC_TRANSPORT_USBSTORAGE},
}; };
const char * const char *
@ -536,6 +538,8 @@ dctool_iostream_open (dc_iostream_t **iostream, dc_context_t *context, dc_descri
return dctool_irda_open (iostream, context, descriptor, devname); return dctool_irda_open (iostream, context, descriptor, devname);
case DC_TRANSPORT_BLUETOOTH: case DC_TRANSPORT_BLUETOOTH:
return dctool_bluetooth_open (iostream, context, descriptor, devname); return dctool_bluetooth_open (iostream, context, descriptor, devname);
case DC_TRANSPORT_USBSTORAGE:
return dc_usb_storage_open (iostream, context, devname);
default: default:
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
} }

View File

@ -48,9 +48,13 @@ typedef enum dc_transport_t {
DC_TRANSPORT_USBHID = (1 << 2), DC_TRANSPORT_USBHID = (1 << 2),
DC_TRANSPORT_IRDA = (1 << 3), DC_TRANSPORT_IRDA = (1 << 3),
DC_TRANSPORT_BLUETOOTH = (1 << 4), DC_TRANSPORT_BLUETOOTH = (1 << 4),
DC_TRANSPORT_BLE = (1 << 5) DC_TRANSPORT_BLE = (1 << 5),
DC_TRANSPORT_USBSTORAGE= (1 << 6),
} dc_transport_t; } dc_transport_t;
// Idiotic enums can't be queried
#define DC_TRANSPORT_USBSTORAGE DC_TRANSPORT_USBSTORAGE
typedef enum dc_family_t { typedef enum dc_family_t {
DC_FAMILY_NULL = 0, DC_FAMILY_NULL = 0,
/* Suunto */ /* Suunto */
@ -103,6 +107,8 @@ typedef enum dc_family_t {
DC_FAMILY_COCHRAN_COMMANDER = (14 << 16), DC_FAMILY_COCHRAN_COMMANDER = (14 << 16),
/* Tecdiving */ /* Tecdiving */
DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16), DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16),
/* Garmin */
DC_FAMILY_GARMIN = (16 << 16),
} dc_family_t; } dc_family_t;
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -283,6 +283,9 @@ dc_iostream_sleep (dc_iostream_t *iostream, unsigned int milliseconds);
dc_status_t dc_status_t
dc_iostream_close (dc_iostream_t *iostream); dc_iostream_close (dc_iostream_t *iostream);
dc_status_t
dc_usb_storage_open (dc_iostream_t **out, dc_context_t *context, const char *name);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -490,6 +490,14 @@
RelativePath="..\src\tecdiving_divecomputereu_parser.c" RelativePath="..\src\tecdiving_divecomputereu_parser.c"
> >
</File> </File>
<File
RelativePath="..\src\garmin.c"
>
</File>
<File
RelativePath="..\src\garmin_parser.c"
>
</File>
<File <File
RelativePath="..\src\timer.c" RelativePath="..\src\timer.c"
> >
@ -828,6 +836,10 @@
RelativePath="..\src\tecdiving_divecomputereu.h" RelativePath="..\src\tecdiving_divecomputereu.h"
> >
</File> </File>
<File
RelativePath="..\src\garmin.h"
>
</File>
<File <File
RelativePath="..\src\timer.h" RelativePath="..\src\timer.h"
> >

View File

@ -71,10 +71,12 @@ libdivecomputer_la_SOURCES = \
buffer.c \ buffer.c \
cochran_commander.h cochran_commander.c cochran_commander_parser.c \ cochran_commander.h cochran_commander.c cochran_commander_parser.c \
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \ tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
garmin.h garmin.c garmin_parser.c \
socket.h socket.c \ socket.h socket.c \
irda.c \ irda.c \
usbhid.c \ usbhid.c \
bluetooth.c \ bluetooth.c \
usb_storage.c \
custom.c custom.c
if OS_WIN32 if OS_WIN32

View File

@ -334,6 +334,7 @@ dc_context_get_transports (dc_context_t *context)
#elif defined(HAVE_LIBUSB) && !defined(__APPLE__) #elif defined(HAVE_LIBUSB) && !defined(__APPLE__)
| DC_TRANSPORT_USBHID | DC_TRANSPORT_USBHID
#endif #endif
| DC_TRANSPORT_USBSTORAGE
#ifdef _WIN32 #ifdef _WIN32
#ifdef HAVE_AF_IRDA_H #ifdef HAVE_AF_IRDA_H
| DC_TRANSPORT_IRDA | DC_TRANSPORT_IRDA

View File

@ -34,6 +34,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata);
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata); static int dc_filter_shearwater (dc_transport_t transport, const void *userdata);
static int dc_filter_hw (dc_transport_t transport, const void *userdata); static int dc_filter_hw (dc_transport_t transport, const void *userdata);
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata); static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata);
static int dc_filter_garmin (dc_transport_t transport, const void *userdata);
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
@ -328,6 +329,8 @@ static const dc_descriptor_t g_descriptors[] = {
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, DC_TRANSPORT_SERIAL, NULL}, {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, DC_TRANSPORT_SERIAL, NULL},
/* Tecdiving DiveComputer.eu */ /* Tecdiving DiveComputer.eu */
{"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving}, {"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving},
/* Garmin */
{"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 0, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
}; };
static int static int
@ -467,6 +470,19 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata)
return 1; return 1;
} }
static int dc_filter_garmin (dc_transport_t transport, const void *userdata)
{
static const dc_usb_desc_t usbhid[] = {
{0x091e, 0x2b2b}, // Garmin Descent Mk1
};
if (transport == DC_TRANSPORT_USBSTORAGE) {
return dc_filter_internal_usb ((const dc_usb_desc_t *) userdata, usbhid, C_ARRAY_SIZE(usbhid));
}
return 1;
}
dc_status_t dc_status_t
dc_descriptor_iterator (dc_iterator_t **out) dc_descriptor_iterator (dc_iterator_t **out)
{ {

View File

@ -56,6 +56,7 @@
#include "divesystem_idive.h" #include "divesystem_idive.h"
#include "cochran_commander.h" #include "cochran_commander.h"
#include "tecdiving_divecomputereu.h" #include "tecdiving_divecomputereu.h"
#include "garmin.h"
#include "device-private.h" #include "device-private.h"
#include "context-private.h" #include "context-private.h"
@ -207,6 +208,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU: case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
rc = tecdiving_divecomputereu_device_open (&device, context, iostream); rc = tecdiving_divecomputereu_device_open (&device, context, iostream);
break; break;
case DC_FAMILY_GARMIN:
rc = garmin_device_open (&device, context, iostream);
break;
default: default:
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
} }

302
src/garmin.c Normal file
View File

@ -0,0 +1,302 @@
/*
* Garmin Descent Mk1 USB storage downloading
*
* Copyright (C) 2018 Linus Torvalds
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "garmin.h"
#include "context-private.h"
#include "device-private.h"
#include "array.h"
typedef struct garmin_device_t {
dc_device_t base;
dc_iostream_t *iostream;
unsigned char fingerprint[FIT_NAME_SIZE];
} 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_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t garmin_device_close (dc_device_t *abstract);
static const dc_device_vtable_t garmin_device_vtable = {
sizeof(garmin_device_t),
DC_FAMILY_GARMIN,
garmin_device_set_fingerprint, /* set_fingerprint */
NULL, /* read */
NULL, /* write */
NULL, /* dump */
garmin_device_foreach, /* foreach */
NULL, /* timesync */
garmin_device_close, /* close */
};
dc_status_t
garmin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
{
dc_status_t status = DC_STATUS_SUCCESS;
garmin_device_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (garmin_device_t *) dc_device_allocate (context, &garmin_device_vtable);
if (device == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Set the default values.
device->iostream = iostream;
memset(device->fingerprint, 0, sizeof(device->fingerprint));
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
}
static dc_status_t
garmin_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{
garmin_device_t *device = (garmin_device_t *)abstract;
if (size && size != sizeof (device->fingerprint))
return DC_STATUS_INVALIDARGS;
if (size)
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
else
memset (device->fingerprint, 0, sizeof (device->fingerprint));
return DC_STATUS_SUCCESS;
}
static dc_status_t
garmin_device_close (dc_device_t *abstract)
{
dc_status_t status = DC_STATUS_SUCCESS;
garmin_device_t *device = (garmin_device_t *) abstract;
return DC_STATUS_SUCCESS;
}
struct file_list {
int nr, allocated;
struct fit_name *array;
};
static int name_cmp(const void *a, const void *b)
{
// Sort reverse string ordering (newest first), so use 'b,a'
return strcmp(b,a);
}
/*
* Get the FIT file list and sort it.
*
* Return number of files found.
*/
static int get_file_list(DIR *dir, struct file_list *files)
{
struct dirent *de;
while ((de = readdir(dir)) != NULL) {
int len = strlen(de->d_name);
if (len != FIT_NAME_SIZE-1)
continue;
if (strncasecmp(de->d_name + len - 4, ".FIT", 4))
continue;
if (files->nr == files->allocated) {
struct fit_name *array;
int n = 3*(files->allocated + 8)/2;
size_t new_size;
new_size = n * sizeof(array[0]);
array = realloc(files->array, new_size);
if (!array)
return DC_STATUS_NOMEMORY;
files->array = array;
files->allocated = n;
}
memcpy(files->array + files->nr++, de->d_name, FIT_NAME_SIZE);
}
qsort(files->array, files->nr, sizeof(struct fit_name), name_cmp);
return DC_STATUS_SUCCESS;
}
static dc_status_t
read_file(char *pathname, int pathlen, const char *name, dc_buffer_t *file)
{
int fd, rc;
pathname[pathlen] = '/';
memcpy(pathname+pathlen+1, name, FIT_NAME_SIZE);
fd = open(pathname, O_RDONLY);
if (fd < 0)
return DC_STATUS_IO;
rc = DC_STATUS_SUCCESS;
for (;;) {
char buffer[4096];
int n;
n = read(fd, buffer, sizeof(buffer));
if (!n)
break;
if (n > 0) {
dc_buffer_append(file, buffer, n);
continue;
}
rc = DC_STATUS_IO;
break;
}
close(fd);
return rc;
}
static dc_status_t
garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t status = DC_STATUS_SUCCESS;
garmin_device_t *device = (garmin_device_t *) abstract;
char pathname[PATH_MAX];
size_t pathlen;
struct file_list files = { 0, 0, NULL };
dc_buffer_t *file;
DIR *dir;
int rc;
// Read the directory name from the iostream
rc = dc_iostream_read(device->iostream, &pathname, sizeof(pathname), &pathlen);
if (rc != DC_STATUS_SUCCESS)
return rc;
// The actual dives are under the "Garmin/Activity/" directory
// as FIT files, with names like "2018-08-20-10-23-30.fit".
// Make sure our buffer is big enough.
if (pathlen + strlen("/Garmin/Activity/") + FIT_NAME_SIZE + 2 > PATH_MAX)
return DC_STATUS_IO;
if (pathlen && pathname[pathlen-1] != '/')
pathname[pathlen++] = '/';
strcpy(pathname + pathlen, "Garmin/Activity");
pathlen += strlen("Garmin/Activity");
dir = opendir(pathname);
if (!dir)
return DC_STATUS_IO;
// Get the list of FIT files
rc = get_file_list(dir, &files);
closedir(dir);
if (rc != DC_STATUS_SUCCESS || !files.nr) {
free(files.array);
return rc;
}
// Can we find the fingerprint entry?
for (int i = 0; i < files.nr; i++) {
const char *name = files.array[i].name;
if (memcmp(name, device->fingerprint, sizeof (device->fingerprint)))
continue;
// Found fingerprint, just cut the array short here
files.nr = i;
break;
}
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
progress.maximum = files.nr;
progress.current = 0;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
#if 0
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = 0;
devinfo.firmware = 0;
devinfo.serial = 0;
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Emit a vendor event.
dc_event_vendor_t vendor;
vendor.data = "Garmin";
vendor.size = 6;
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
#endif
file = dc_buffer_new (16384);
if (file == NULL) {
ERROR (abstract->context, "Insufficient buffer space available.");
free(files.array);
return DC_STATUS_NOMEMORY;
}
for (int i = 0; i < files.nr; i++) {
const char *name = files.array[i].name;
const unsigned char *data;
unsigned int size;
if (device_is_cancelled(abstract)) {
status = DC_STATUS_CANCELLED;
break;
}
// Reset the membuffer, read the data
dc_buffer_clear(file);
dc_buffer_append(file, name, FIT_NAME_SIZE);
status = read_file(pathname, pathlen, name, file);
if (status != DC_STATUS_SUCCESS)
break;
data = dc_buffer_get_data(file);
size = dc_buffer_get_size(file);
if (callback && !callback(data, size, name, FIT_NAME_SIZE, userdata))
break;
progress.current++;
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
}
free(files.array);
return status;
}

54
src/garmin.h Normal file
View File

@ -0,0 +1,54 @@
/*
* Garmin Descent Mk1
*
* Copyright (C) 2018 Linus Torvalds
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifndef GARMIN_H
#define GARMIN_H
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
dc_status_t
garmin_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t
garmin_parser_create (dc_parser_t **parser, dc_context_t *context);
// The dive names are of the form "2018-08-20-10-23-30.fit"
// With the terminating zero, that's 24 bytes.
//
// We use this as the fingerprint, but it ends up being a
// special fixed header in the parser data too.
#define FIT_NAME_SIZE 24
struct fit_name {
char name[FIT_NAME_SIZE];
};
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* GARMIN_H */

1024
src/garmin_parser.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,8 @@ dc_usbhid_device_free
dc_usbhid_iterator_new dc_usbhid_iterator_new
dc_usbhid_open dc_usbhid_open
dc_usb_storage_open
dc_custom_open dc_custom_open
dc_parser_new dc_parser_new

View File

@ -56,6 +56,7 @@
#include "divesystem_idive.h" #include "divesystem_idive.h"
#include "cochran_commander.h" #include "cochran_commander.h"
#include "tecdiving_divecomputereu.h" #include "tecdiving_divecomputereu.h"
#include "garmin.h"
#include "context-private.h" #include "context-private.h"
#include "parser-private.h" #include "parser-private.h"
@ -168,6 +169,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU: case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
rc = tecdiving_divecomputereu_parser_create (&parser, context); rc = tecdiving_divecomputereu_parser_create (&parser, context);
break; break;
case DC_FAMILY_GARMIN:
rc = garmin_parser_create (&parser, context);
break;
default: default:
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
} }

109
src/usb_storage.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Dummy "stream" operations for USB storage
*
* Copyright (C) 2018 Linus Torvalds
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "common-private.h"
#include "context-private.h"
#include "iostream-private.h"
#include "iterator-private.h"
#include "descriptor-private.h"
#include "timer.h"
// Fake "device" that just contains the directory name that
// you can read out of the iostream. All the actual IO is
// up to you.
typedef struct dc_usbstorage_t {
dc_iostream_t base;
char pathname[PATH_MAX];
} dc_usbstorage_t;
static dc_status_t
dc_usb_storage_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
static const dc_iostream_vtable_t dc_usbstorage_vtable = {
sizeof(dc_usbstorage_t),
NULL, /* set_timeout */
NULL, /* set_latency */
NULL, /* set_break */
NULL, /* set_dtr */
NULL, /* set_rts */
NULL, /* get_lines */
NULL, /* get_available */
NULL, /* configure */
dc_usb_storage_read, /* read */
NULL, /* write */
NULL, /* flush */
NULL, /* purge */
NULL, /* sleep */
NULL, /* close */
};
dc_status_t
dc_usb_storage_open (dc_iostream_t **out, dc_context_t *context, const char *name)
{
dc_usbstorage_t *device = NULL;
struct stat st;
if (out == NULL || name == NULL)
return DC_STATUS_INVALIDARGS;
INFO (context, "Open: name=%s", name);
if (stat(name, &st) < 0 || !S_ISDIR(st.st_mode))
return DC_STATUS_NODEVICE;
// Allocate memory.
device = (dc_usbstorage_t *) dc_iostream_allocate (context, &dc_usbstorage_vtable, DC_TRANSPORT_USBSTORAGE);
if (device == NULL) {
SYSERROR (context, ENOMEM);
return DC_STATUS_NOMEMORY;
}
strncpy(device->pathname, name, PATH_MAX);
device->pathname[PATH_MAX-1] = 0;
*out = (dc_iostream_t *) device;
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_usb_storage_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual)
{
dc_usbstorage_t *device = (dc_usbstorage_t *) iostream;
size_t len = strlen(device->pathname);
if (size <= len)
return DC_STATUS_IO;
memcpy(data, device->pathname, len+1);
if (actual)
*actual = len;
return DC_STATUS_SUCCESS;
}