From b5503e53fd7555cfbc5da6a582d8e60b5e10eef8 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 07:26:07 +0100 Subject: [PATCH 01/16] Add a more modular example application. The universal application works well, but is quite difficult to extend with more functionality. Therefore a new and more modular application is needed. The new dctool application will support multiple sub-commands, to carry out specific actions. Extending the application will be as easy as adding new commands. --- configure.ac | 2 + examples/Makefile.am | 14 +- examples/common.c | 66 --- examples/dctool.c | 125 ++++ examples/{common.h => dctool.h} | 23 +- examples/hw_ostc_fwupdate.c | 145 ----- examples/universal.c | 979 -------------------------------- 7 files changed, 144 insertions(+), 1210 deletions(-) delete mode 100644 examples/common.c create mode 100644 examples/dctool.c rename examples/{common.h => dctool.h} (69%) delete mode 100644 examples/hw_ostc_fwupdate.c delete mode 100644 examples/universal.c diff --git a/configure.ac b/configure.ac index 047b3f0..ca071fa 100644 --- a/configure.ac +++ b/configure.ac @@ -105,10 +105,12 @@ AM_CONDITIONAL([IRDA], [test "$irda_win32" = "yes" || test "$irda_linux" = "yes" # Checks for header files. AC_CHECK_HEADERS([linux/serial.h]) AC_CHECK_HEADERS([IOKit/serial/ioss.h]) +AC_CHECK_HEADERS([getopt.h]) # Checks for library functions. AC_FUNC_STRERROR_R AC_CHECK_FUNCS([localtime_r gmtime_r]) +AC_CHECK_FUNCS([getopt_long]) # Versioning. AC_SUBST([DC_VERSION],[dc_version]) diff --git a/examples/Makefile.am b/examples/Makefile.am index d77db45..ddbade4 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -2,12 +2,10 @@ AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include LDADD = $(top_builddir)/src/libdivecomputer.la bin_PROGRAMS = \ - universal \ - ostc-fwupdate + dctool -COMMON = common.c common.h \ - utils.c utils.h - -universal_SOURCES = universal.c $(COMMON) - -ostc_fwupdate_SOURCES = hw_ostc_fwupdate.c $(COMMON) +dctool_SOURCES = \ + dctool.h \ + dctool.c \ + utils.h \ + utils.c diff --git a/examples/common.c b/examples/common.c deleted file mode 100644 index f69e7e4..0000000 --- a/examples/common.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2011 Jef Driesen - * - * 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 "common.h" -#include "utils.h" - -const char * -errmsg (dc_status_t rc) -{ - switch (rc) { - case DC_STATUS_SUCCESS: - return "Success"; - case DC_STATUS_UNSUPPORTED: - return "Unsupported operation"; - case DC_STATUS_INVALIDARGS: - return "Invalid arguments"; - case DC_STATUS_NOMEMORY: - return "Out of memory"; - case DC_STATUS_NODEVICE: - return "No device found"; - case DC_STATUS_NOACCESS: - return "Access denied"; - case DC_STATUS_IO: - return "Input/output error"; - case DC_STATUS_TIMEOUT: - return "Timeout"; - case DC_STATUS_PROTOCOL: - return "Protocol error"; - case DC_STATUS_DATAFORMAT: - return "Data format error"; - case DC_STATUS_CANCELLED: - return "Cancelled"; - default: - return "Unknown error"; - } -} - -void -logfunc (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata) -{ - const char *loglevels[] = {"NONE", "ERROR", "WARNING", "INFO", "DEBUG", "ALL"}; - - if (loglevel == DC_LOGLEVEL_ERROR || loglevel == DC_LOGLEVEL_WARNING) { - message ("%s: %s [in %s:%d (%s)]\n", loglevels[loglevel], msg, file, line, function); - } else { - message ("%s: %s\n", loglevels[loglevel], msg); - } -} diff --git a/examples/dctool.c b/examples/dctool.c new file mode 100644 index 0000000..b0b0c9c --- /dev/null +++ b/examples/dctool.c @@ -0,0 +1,125 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include "dctool.h" +#include "utils.h" + +#if defined(__GLIBC__) || defined(__MINGW32__) +#define NOPERMUTATION "+" +#define RESET 0 +#else +#define NOPERMUTATION "" +#define RESET 1 +#endif + +static const dctool_command_t *g_commands[] = { + NULL +}; + +const dctool_command_t * +dctool_command_find (const char *name) +{ + if (name == NULL) + return NULL; + + size_t i = 0; + while (g_commands[i] != NULL) { + if (strcmp(g_commands[i]->name, name) == 0) { + break; + } + i++; + } + + return g_commands[i]; +} + +int +main (int argc, char *argv[]) +{ + // Default option values. + unsigned int help = 0; + + // Parse the command-line options. + int opt = 0; + const char *optstring = NOPERMUTATION "h"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + default: + return EXIT_FAILURE; + } + } + + // Skip the processed arguments. + argc -= optind; + argv += optind; + optind = RESET; + + // Show help message. + if (help || argv[0] == NULL) { + printf ( + "A simple command line interface for the libdivecomputer library\n" + "\n" + "Usage:\n" + " dctool [options] []\n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" +#else + " -h Show help message\n" +#endif + "\n"); + return EXIT_SUCCESS; + } + + // Try to find the command. + const dctool_command_t *command = dctool_command_find (argv[0]); + if (command == NULL) { + message ("Unknown command %s.\n", argv[0]); + return EXIT_FAILURE; + } + + // Execute the command. + return command->run (argc, argv); +} diff --git a/examples/common.h b/examples/dctool.h similarity index 69% rename from examples/common.h rename to examples/dctool.h index 4c23f1b..6443caf 100644 --- a/examples/common.h +++ b/examples/dctool.h @@ -1,7 +1,7 @@ /* * libdivecomputer * - * Copyright (C) 2011 Jef Driesen + * Copyright (C) 2015 Jef Driesen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,23 +19,22 @@ * MA 02110-1301 USA */ -#ifndef EXAMPLES_COMMON_H -#define EXAMPLES_COMMON_H - -#include -#include +#ifndef DCTOOL_H +#define DCTOOL_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -const char * -errmsg (dc_status_t rc); - -void -logfunc (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata); +typedef struct dctool_command_t { + int (*run) (int argc, char *argv[]); + const char *name; + const char *description; + const char *usage; +} dctool_command_t; #ifdef __cplusplus } #endif /* __cplusplus */ -#endif /* EXAMPLES_COMMON_H */ + +#endif /* DCTOOL_H */ diff --git a/examples/hw_ostc_fwupdate.c b/examples/hw_ostc_fwupdate.c deleted file mode 100644 index 8fa5c31..0000000 --- a/examples/hw_ostc_fwupdate.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2013 Jef Driesen - * - * 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 - -#include -#include - -#include "utils.h" -#include "common.h" - -static void -event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *userdata) -{ - const dc_event_progress_t *progress = (dc_event_progress_t *) data; - - switch (event) { - case DC_EVENT_PROGRESS: - message ("Event: progress %3.2f%% (%u/%u)\n", - 100.0 * (double) progress->current / (double) progress->maximum, - progress->current, progress->maximum); - break; - default: - break; - } -} - -static dc_status_t -fwupdate (const char *name, const char *hexfile, int ostc3) -{ - dc_context_t *context = NULL; - dc_device_t *device = NULL; - dc_status_t rc = DC_STATUS_SUCCESS; - - dc_context_new (&context); - dc_context_set_loglevel (context, DC_LOGLEVEL_ALL); - dc_context_set_logfunc (context, logfunc, NULL); - - if (ostc3) { - message ("hw_ostc3_device_open\n"); - rc = hw_ostc3_device_open (&device, context, name); - } else { - message ("hw_ostc_device_open\n"); - rc = hw_ostc_device_open (&device, context, name); - } - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error opening serial port."); - dc_context_free (context); - return rc; - } - - message ("dc_device_set_events.\n"); - rc = dc_device_set_events (device, DC_EVENT_PROGRESS, event_cb, NULL); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error registering the event handler."); - dc_device_close (device); - dc_context_free (context); - return rc; - } - - if (ostc3) { - message ("hw_ostc3_device_fwupdate\n"); - rc = hw_ostc3_device_fwupdate (device, hexfile); - } else { - message ("hw_ostc_device_fwupdate\n"); - rc = hw_ostc_device_fwupdate (device, hexfile); - } - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error flashing firmware."); - dc_device_close (device); - dc_context_free (context); - return rc; - } - - message ("dc_device_close\n"); - rc = dc_device_close (device); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Cannot close device."); - dc_context_free (context); - return rc; - } - - dc_context_free (context); - - return DC_STATUS_SUCCESS; -} - - -int main(int argc, char *argv[]) -{ - message_set_logfile ("OSTC-FWUPDATE.LOG"); - -#ifdef _WIN32 - const char* name = "COM1"; -#else - const char* name = "/dev/ttyUSB0"; -#endif - const char *hexfile = NULL; - int ostc3 = 0; - - if (argc > 1) { - name = argv[1]; - } - if (argc > 2) { - hexfile = argv[2]; - } - if (argc > 3) { - if (strcmp(argv[3], "-3") == 0) { - ostc3 = 1; - } else { - ostc3 = 0; - } - } - - message ("DEVICE=%s\n", name); - message ("HEXFILE=%s\n", hexfile); - - dc_status_t a = fwupdate (name, hexfile, ostc3); - - message ("SUMMARY\n"); - message ("-------\n"); - message ("fwupdate: %s\n", errmsg (a)); - - message_set_logfile (NULL); - - return 0; -} diff --git a/examples/universal.c b/examples/universal.c deleted file mode 100644 index b4d9b47..0000000 --- a/examples/universal.c +++ /dev/null @@ -1,979 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2009 Jef Driesen - * - * 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 // fopen, fwrite, fclose -#include -#include -#include - -#ifndef _MSC_VER -#include -#endif - -#ifdef _MSC_VER -#define snprintf _snprintf -#define strcasecmp _stricmp -#define strncasecmp _strnicmp -#endif - -#ifdef _WIN32 -#define DC_TICKS_FORMAT "%I64d" -#else -#define DC_TICKS_FORMAT "%lld" -#endif - -#include -#include -#include - -#include "utils.h" -#include "common.h" - -static const char *g_cachedir = NULL; -static int g_cachedir_read = 1; - -typedef struct device_data_t { - dc_event_devinfo_t devinfo; - dc_event_clock_t clock; -} device_data_t; - -typedef struct dive_data_t { - dc_device_t *device; - FILE* fp; - unsigned int number; - dc_buffer_t *fingerprint; -} dive_data_t; - -typedef struct sample_data_t { - FILE* fp; - unsigned int nsamples; -} sample_data_t; - -typedef struct backend_table_t { - const char *name; - dc_family_t type; -} backend_table_t; - -static const backend_table_t g_backends[] = { - {"solution", DC_FAMILY_SUUNTO_SOLUTION}, - {"eon", DC_FAMILY_SUUNTO_EON}, - {"vyper", DC_FAMILY_SUUNTO_VYPER}, - {"vyper2", DC_FAMILY_SUUNTO_VYPER2}, - {"d9", DC_FAMILY_SUUNTO_D9}, - {"eonsteel", DC_FAMILY_SUUNTO_EONSTEEL}, - {"aladin", DC_FAMILY_UWATEC_ALADIN}, - {"memomouse", DC_FAMILY_UWATEC_MEMOMOUSE}, - {"smart", DC_FAMILY_UWATEC_SMART}, - {"meridian", DC_FAMILY_UWATEC_MERIDIAN}, - {"sensus", DC_FAMILY_REEFNET_SENSUS}, - {"sensuspro", DC_FAMILY_REEFNET_SENSUSPRO}, - {"sensusultra", DC_FAMILY_REEFNET_SENSUSULTRA}, - {"vtpro", DC_FAMILY_OCEANIC_VTPRO}, - {"veo250", DC_FAMILY_OCEANIC_VEO250}, - {"atom2", DC_FAMILY_OCEANIC_ATOM2}, - {"nemo", DC_FAMILY_MARES_NEMO}, - {"puck", DC_FAMILY_MARES_PUCK}, - {"darwin", DC_FAMILY_MARES_DARWIN}, - {"iconhd", DC_FAMILY_MARES_ICONHD}, - {"ostc", DC_FAMILY_HW_OSTC}, - {"frog", DC_FAMILY_HW_FROG}, - {"ostc3", DC_FAMILY_HW_OSTC3}, - {"edy", DC_FAMILY_CRESSI_EDY}, - {"leonardo", DC_FAMILY_CRESSI_LEONARDO}, - {"n2ition3", DC_FAMILY_ZEAGLE_N2ITION3}, - {"cobalt", DC_FAMILY_ATOMICS_COBALT}, - {"predator", DC_FAMILY_SHEARWATER_PREDATOR}, - {"petrel", DC_FAMILY_SHEARWATER_PETREL}, - {"nitekq", DC_FAMILY_DIVERITE_NITEKQ}, - {"aqualand", DC_FAMILY_CITIZEN_AQUALAND}, - {"idive", DC_FAMILY_DIVESYSTEM_IDIVE}, -}; - -static dc_family_t -lookup_type (const char *name) -{ - unsigned int nbackends = sizeof (g_backends) / sizeof (g_backends[0]); - for (unsigned int i = 0; i < nbackends; ++i) { - if (strcmp (name, g_backends[i].name) == 0) - return g_backends[i].type; - } - - return DC_FAMILY_NULL; -} - -static const char * -lookup_name (dc_family_t type) -{ - unsigned int nbackends = sizeof (g_backends) / sizeof (g_backends[0]); - for (unsigned int i = 0; i < nbackends; ++i) { - if (g_backends[i].type == type) - return g_backends[i].name; - } - - return NULL; -} - -static unsigned char -hex2dec (unsigned char value) -{ - if (value >= '0' && value <= '9') - return value - '0'; - else if (value >= 'A' && value <= 'F') - return value - 'A' + 10; - else if (value >= 'a' && value <= 'f') - return value - 'a' + 10; - else - return 0; -} - -static dc_buffer_t * -fpconvert (const char *fingerprint) -{ - // Get the length of the fingerprint data. - size_t nbytes = (fingerprint ? strlen (fingerprint) / 2 : 0); - if (nbytes == 0) - return NULL; - - // Allocate a memory buffer. - dc_buffer_t *buffer = dc_buffer_new (nbytes); - - // Convert the hexadecimal string. - for (unsigned int i = 0; i < nbytes; ++i) { - unsigned char msn = hex2dec (fingerprint[i * 2 + 0]); - unsigned char lsn = hex2dec (fingerprint[i * 2 + 1]); - unsigned char byte = (msn << 4) + lsn; - - dc_buffer_append (buffer, &byte, 1); - } - - return buffer; -} - -static dc_buffer_t * -fpread (const char *dirname, dc_family_t backend, unsigned int serial) -{ - // Build the filename. - char filename[1024] = {0}; - snprintf (filename, sizeof (filename), "%s/%s-%08X.bin", - dirname, lookup_name (backend), serial); - - // Open the fingerprint file. - FILE *fp = fopen (filename, "rb"); - if (fp == NULL) - return NULL; - - // Allocate a memory buffer. - dc_buffer_t *buffer = dc_buffer_new (0); - - // Read the entire file into the buffer. - size_t n = 0; - unsigned char block[1024] = {0}; - while ((n = fread (block, 1, sizeof (block), fp)) > 0) { - dc_buffer_append (buffer, block, n); - } - - // Close the file. - fclose (fp); - - return buffer; -} - -static void -fpwrite (dc_buffer_t *buffer, const char *dirname, dc_family_t backend, unsigned int serial) -{ - // Check the buffer size. - if (dc_buffer_get_size (buffer) == 0) - return; - - // Build the filename. - char filename[1024] = {0}; - snprintf (filename, sizeof (filename), "%s/%s-%08X.bin", - dirname, lookup_name (backend), serial); - - // Open the fingerprint file. - FILE *fp = fopen (filename, "wb"); - if (fp == NULL) - return; - - // Write the fingerprint data. - fwrite (dc_buffer_get_data (buffer), 1, dc_buffer_get_size (buffer), fp); - - // Close the file. - fclose (fp); -} - -volatile sig_atomic_t g_cancel = 0; - -void -sighandler (int signum) -{ -#ifndef _WIN32 - // Restore the default signal handler. - signal (signum, SIG_DFL); -#endif - - g_cancel = 1; -} - -static int -cancel_cb (void *userdata) -{ - return g_cancel; -} - -void -sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata) -{ - static const char *events[] = { - "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter", - "violation", "bookmark", "surface", "safety stop", "gaschange", - "safety stop (voluntary)", "safety stop (mandatory)", "deepstop", - "ceiling (safety stop)", "floor", "divetime", "maxdepth", - "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning", - "gaschange2"}; - static const char *decostop[] = { - "ndl", "safety", "deco", "deep"}; - - sample_data_t *sampledata = (sample_data_t *) userdata; - - switch (type) { - case DC_SAMPLE_TIME: - if (sampledata->nsamples++) - fprintf (sampledata->fp, "\n"); - fprintf (sampledata->fp, "\n"); - fprintf (sampledata->fp, " \n", value.time / 60, value.time % 60); - break; - case DC_SAMPLE_DEPTH: - fprintf (sampledata->fp, " %.2f\n", value.depth); - break; - case DC_SAMPLE_PRESSURE: - fprintf (sampledata->fp, " %.2f\n", value.pressure.tank, value.pressure.value); - break; - case DC_SAMPLE_TEMPERATURE: - fprintf (sampledata->fp, " %.2f\n", value.temperature); - break; - case DC_SAMPLE_EVENT: - if (value.event.type == SAMPLE_EVENT_GASCHANGE || value.event.type == SAMPLE_EVENT_GASCHANGE2) { - // Ignore deprecated events. - } else { - fprintf (sampledata->fp, " %s\n", - value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]); - } - break; - case DC_SAMPLE_RBT: - fprintf (sampledata->fp, " %u\n", value.rbt); - break; - case DC_SAMPLE_HEARTBEAT: - fprintf (sampledata->fp, " %u\n", value.heartbeat); - break; - case DC_SAMPLE_BEARING: - fprintf (sampledata->fp, " %u\n", value.bearing); - break; - case DC_SAMPLE_VENDOR: - fprintf (sampledata->fp, " ", value.vendor.type, value.vendor.size); - for (unsigned int i = 0; i < value.vendor.size; ++i) - fprintf (sampledata->fp, "%02X", ((unsigned char *) value.vendor.data)[i]); - fprintf (sampledata->fp, "\n"); - break; - case DC_SAMPLE_SETPOINT: - fprintf (sampledata->fp, " %.2f\n", value.setpoint); - break; - case DC_SAMPLE_PPO2: - fprintf (sampledata->fp, " %.2f\n", value.ppo2); - break; - case DC_SAMPLE_CNS: - fprintf (sampledata->fp, " %.1f\n", value.cns * 100.0); - break; - case DC_SAMPLE_DECO: - fprintf (sampledata->fp, " %s\n", - value.deco.time, value.deco.depth, decostop[value.deco.type]); - break; - case DC_SAMPLE_GASMIX: - fprintf (sampledata->fp, " %u\n", value.gasmix); - break; - default: - break; - } -} - - -static dc_status_t -doparse (FILE *fp, dc_device_t *device, const unsigned char data[], unsigned int size) -{ - // Create the parser. - message ("Creating the parser.\n"); - dc_parser_t *parser = NULL; - dc_status_t rc = dc_parser_new (&parser, device); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error creating the parser."); - return rc; - } - - // Register the data. - message ("Registering the data.\n"); - rc = dc_parser_set_data (parser, data, size); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error registering the data."); - dc_parser_destroy (parser); - return rc; - } - - // Parse the datetime. - message ("Parsing the datetime.\n"); - dc_datetime_t dt = {0}; - rc = dc_parser_get_datetime (parser, &dt); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the datetime."); - dc_parser_destroy (parser); - return rc; - } - - fprintf (fp, "%04i-%02i-%02i %02i:%02i:%02i\n", - dt.year, dt.month, dt.day, - dt.hour, dt.minute, dt.second); - - // Parse the divetime. - message ("Parsing the divetime.\n"); - unsigned int divetime = 0; - rc = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the divetime."); - dc_parser_destroy (parser); - return rc; - } - - fprintf (fp, "%02u:%02u\n", - divetime / 60, divetime % 60); - - // Parse the temperature. - message ("Parsing the temperature.\n"); - for (unsigned int i = 0; i < 3; ++i) { - dc_field_type_t fields[] = {DC_FIELD_TEMPERATURE_SURFACE, - DC_FIELD_TEMPERATURE_MINIMUM, - DC_FIELD_TEMPERATURE_MAXIMUM}; - const char *names[] = {"surface", "minimum", "maximum"}; - - double temperature = 0.0; - rc = dc_parser_get_field (parser, fields[i], 0, &temperature); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the temperature."); - dc_parser_destroy (parser); - return rc; - } - - if (rc != DC_STATUS_UNSUPPORTED) { - fprintf (fp, "%.1f\n", - names[i], temperature); - } - } - - // Parse the maxdepth. - message ("Parsing the maxdepth.\n"); - double maxdepth = 0.0; - rc = dc_parser_get_field (parser, DC_FIELD_MAXDEPTH, 0, &maxdepth); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the maxdepth."); - dc_parser_destroy (parser); - return rc; - } - - fprintf (fp, "%.2f\n", - maxdepth); - - // Parse the gas mixes. - message ("Parsing the gas mixes.\n"); - unsigned int ngases = 0; - rc = dc_parser_get_field (parser, DC_FIELD_GASMIX_COUNT, 0, &ngases); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the gas mix count."); - dc_parser_destroy (parser); - return rc; - } - - for (unsigned int i = 0; i < ngases; ++i) { - dc_gasmix_t gasmix = {0}; - rc = dc_parser_get_field (parser, DC_FIELD_GASMIX, i, &gasmix); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the gas mix."); - dc_parser_destroy (parser); - return rc; - } - - fprintf (fp, - "\n" - " %.1f\n" - " %.1f\n" - " %.1f\n" - "\n", - gasmix.helium * 100.0, - gasmix.oxygen * 100.0, - gasmix.nitrogen * 100.0); - } - - // Parse the tanks. - message ("Parsing the tanks.\n"); - unsigned int ntanks = 0; - rc = dc_parser_get_field (parser, DC_FIELD_TANK_COUNT, 0, &ntanks); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the tank count."); - dc_parser_destroy (parser); - return rc; - } - - for (unsigned int i = 0; i < ntanks; ++i) { - const char *names[] = {"none", "metric", "imperial"}; - - dc_tank_t tank = {0}; - rc = dc_parser_get_field (parser, DC_FIELD_TANK, i, &tank); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the tank."); - dc_parser_destroy (parser); - return rc; - } - - fprintf (fp, "\n"); - if (tank.gasmix != DC_GASMIX_UNKNOWN) { - fprintf (fp, - " %u\n", - tank.gasmix); - } - if (tank.type != DC_TANKVOLUME_NONE) { - fprintf (fp, - " %s\n" - " %.1f\n" - " %.2f\n", - names[tank.type], tank.volume, tank.workpressure); - } - fprintf (fp, - " %.2f\n" - " %.2f\n" - "\n", - tank.beginpressure, tank.endpressure); - } - - // Parse the dive mode. - message ("Parsing the dive mode.\n"); - dc_divemode_t divemode = DC_DIVEMODE_OC; - rc = dc_parser_get_field (parser, DC_FIELD_DIVEMODE, 0, &divemode); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the dive mode."); - dc_parser_destroy (parser); - return rc; - } - - if (rc != DC_STATUS_UNSUPPORTED) { - const char *names[] = {"freedive", "gauge", "oc", "cc"}; - fprintf (fp, "%s\n", - names[divemode]); - } - - // Parse the salinity. - message ("Parsing the salinity.\n"); - dc_salinity_t salinity = {DC_WATER_FRESH, 0.0}; - rc = dc_parser_get_field (parser, DC_FIELD_SALINITY, 0, &salinity); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the salinity."); - dc_parser_destroy (parser); - return rc; - } - - if (rc != DC_STATUS_UNSUPPORTED) { - fprintf (fp, "%.1f\n", - salinity.type, salinity.density); - } - - // Parse the atmospheric pressure. - message ("Parsing the atmospheric pressure.\n"); - double atmospheric = 0.0; - rc = dc_parser_get_field (parser, DC_FIELD_ATMOSPHERIC, 0, &atmospheric); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - WARNING ("Error parsing the atmospheric pressure."); - dc_parser_destroy (parser); - return rc; - } - - if (rc != DC_STATUS_UNSUPPORTED) { - fprintf (fp, "%.5f\n", - atmospheric); - } - - // Initialize the sample data. - sample_data_t sampledata = {0}; - sampledata.nsamples = 0; - sampledata.fp = fp; - - // Parse the sample data. - message ("Parsing the sample data.\n"); - rc = dc_parser_samples_foreach (parser, sample_cb, &sampledata); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error parsing the sample data."); - dc_parser_destroy (parser); - return rc; - } - - if (sampledata.nsamples) - fprintf (fp, "\n"); - - // Destroy the parser. - message ("Destroying the parser.\n"); - rc = dc_parser_destroy (parser); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error destroying the parser."); - return rc; - } - - return DC_STATUS_SUCCESS; -} - -static void -event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *userdata) -{ - const dc_event_progress_t *progress = (dc_event_progress_t *) data; - const dc_event_devinfo_t *devinfo = (dc_event_devinfo_t *) data; - const dc_event_clock_t *clock = (dc_event_clock_t *) data; - const dc_event_vendor_t *vendor = (dc_event_vendor_t *) data; - - device_data_t *devdata = (device_data_t *) userdata; - - switch (event) { - case DC_EVENT_WAITING: - message ("Event: waiting for user action\n"); - break; - case DC_EVENT_PROGRESS: - message ("Event: progress %3.2f%% (%u/%u)\n", - 100.0 * (double) progress->current / (double) progress->maximum, - progress->current, progress->maximum); - break; - case DC_EVENT_DEVINFO: - devdata->devinfo = *devinfo; - message ("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n", - devinfo->model, devinfo->model, - devinfo->firmware, devinfo->firmware, - devinfo->serial, devinfo->serial); - if (g_cachedir && g_cachedir_read) { - dc_buffer_t *fingerprint = fpread (g_cachedir, dc_device_get_type (device), devinfo->serial); - dc_device_set_fingerprint (device, - dc_buffer_get_data (fingerprint), - dc_buffer_get_size (fingerprint)); - dc_buffer_free (fingerprint); - } - break; - case DC_EVENT_CLOCK: - devdata->clock = *clock; - message ("Event: systime=" DC_TICKS_FORMAT ", devtime=%u\n", - clock->systime, clock->devtime); - break; - case DC_EVENT_VENDOR: - message ("Event: vendor="); - for (unsigned int i = 0; i < vendor->size; ++i) - message ("%02X", vendor->data[i]); - message ("\n"); - break; - default: - break; - } -} - -static int -dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata) -{ - dive_data_t *divedata = (dive_data_t *) userdata; - - divedata->number++; - - message ("Dive: number=%u, size=%u, fingerprint=", divedata->number, size); - for (unsigned int i = 0; i < fsize; ++i) - message ("%02X", fingerprint[i]); - message ("\n"); - - if (divedata->number == 1) { - divedata->fingerprint = dc_buffer_new (fsize); - dc_buffer_append (divedata->fingerprint, fingerprint, fsize); - } - - if (divedata->fp) { - fprintf (divedata->fp, "\n%u\n%u\n", divedata->number, size); - for (unsigned int i = 0; i < fsize; ++i) - fprintf (divedata->fp, "%02X", fingerprint[i]); - fprintf (divedata->fp, "\n"); - - doparse (divedata->fp, divedata->device, data, size); - - fprintf (divedata->fp, "\n"); - } - - return 1; -} - - -static void -usage (const char *filename) -{ -#ifndef _MSC_VER - fprintf (stderr, "Usage:\n\n"); - fprintf (stderr, " %s [options] devname\n\n", filename); - fprintf (stderr, "Options:\n\n"); - fprintf (stderr, " -n name Set device name (required).\n"); - fprintf (stderr, " -b name Set backend name (required).\n"); - fprintf (stderr, " -t model Set model code.\n"); - fprintf (stderr, " -f hexdata Set fingerprint data.\n"); - fprintf (stderr, " -l logfile Set logfile.\n"); - fprintf (stderr, " -d filename Download dives.\n"); - fprintf (stderr, " -m filename Download memory dump.\n"); - fprintf (stderr, " -c cachedir Set cache directory.\n"); - fprintf (stderr, " -h Show this help message.\n\n"); -#else - fprintf (stderr, "Usage:\n\n"); - fprintf (stderr, " %s backend devname\n\n", filename); -#endif - - fprintf (stderr, "Supported backends:\n\n"); - unsigned int nbackends = sizeof (g_backends) / sizeof (g_backends[0]); - for (unsigned int i = 0; i < nbackends; ++i) { - fprintf (stderr, "%s", g_backends[i].name); - if (i != nbackends - 1) - fprintf (stderr, ", "); - else - fprintf (stderr, "\n\n"); - } - - fprintf (stderr, "Supported devices:\n\n"); - dc_iterator_t *iterator = NULL; - dc_descriptor_t *descriptor = NULL; - dc_descriptor_iterator (&iterator); - while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) { - fprintf (stderr, " %s %s\n", - dc_descriptor_get_vendor (descriptor), - dc_descriptor_get_product (descriptor)); - dc_descriptor_free (descriptor); - } - dc_iterator_free (iterator); -} - - -static dc_status_t -search (dc_descriptor_t **out, const char *name, dc_family_t backend, unsigned int model) -{ - dc_status_t rc = DC_STATUS_SUCCESS; - - dc_iterator_t *iterator = NULL; - rc = dc_descriptor_iterator (&iterator); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error creating the device descriptor iterator."); - return rc; - } - - dc_descriptor_t *descriptor = NULL, *current = NULL; - while ((rc = dc_iterator_next (iterator, &descriptor)) == DC_STATUS_SUCCESS) { - if (name) { - const char *vendor = dc_descriptor_get_vendor (descriptor); - const char *product = dc_descriptor_get_product (descriptor); - - size_t n = strlen (vendor); - if (strncasecmp (name, vendor, n) == 0 && name[n] == ' ' && - strcasecmp (name + n + 1, product) == 0) - { - current = descriptor; - break; - } else if (strcasecmp (name, product) == 0) { - current = descriptor; - break; - } - } else { - if (backend == dc_descriptor_get_type (descriptor)) { - if (model == dc_descriptor_get_model (descriptor)) { - // Exact match found. Return immediately. - dc_descriptor_free (current); - current = descriptor; - break; - } else { - // Possible match found. Keep searching for an exact match. - // If no exact match is found, the first match is returned. - if (current == NULL) { - current = descriptor; - descriptor = NULL; - } - } - } - } - - dc_descriptor_free (descriptor); - } - - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_DONE) { - dc_descriptor_free (current); - dc_iterator_free (iterator); - WARNING ("Error iterating the device descriptors."); - return rc; - } - - dc_iterator_free (iterator); - - *out = current; - - return DC_STATUS_SUCCESS; -} - - -static dc_status_t -dowork (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *rawfile, const char *xmlfile, int memory, int dives, dc_buffer_t *fingerprint) -{ - dc_status_t rc = DC_STATUS_SUCCESS; - - // Initialize the device data. - device_data_t devdata = {{0}}; - - // Open the device. - message ("Opening the device (%s %s, %s).\n", - dc_descriptor_get_vendor (descriptor), - dc_descriptor_get_product (descriptor), - devname ? devname : "null"); - dc_device_t *device = NULL; - rc = dc_device_open (&device, context, descriptor, devname); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error opening device."); - return rc; - } - - // Register the event handler. - message ("Registering the event handler.\n"); - int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; - rc = dc_device_set_events (device, events, event_cb, &devdata); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error registering the event handler."); - dc_device_close (device); - return rc; - } - - // Register the cancellation handler. - message ("Registering the cancellation handler.\n"); - rc = dc_device_set_cancel (device, cancel_cb, NULL); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error registering the cancellation handler."); - dc_device_close (device); - return rc; - } - - // Register the fingerprint data. - if (fingerprint) { - message ("Registering the fingerprint data.\n"); - rc = dc_device_set_fingerprint (device, dc_buffer_get_data (fingerprint), dc_buffer_get_size (fingerprint)); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error registering the fingerprint data."); - dc_device_close (device); - return rc; - } - } - - if (memory) { - // Allocate a memory buffer. - dc_buffer_t *buffer = dc_buffer_new (0); - - // Download the memory dump. - message ("Downloading the memory dump.\n"); - rc = dc_device_dump (device, buffer); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error downloading the memory dump."); - dc_buffer_free (buffer); - dc_device_close (device); - return rc; - } - - // Write the memory dump to disk. - FILE* fp = fopen (rawfile, "wb"); - if (fp != NULL) { - fwrite (dc_buffer_get_data (buffer), 1, dc_buffer_get_size (buffer), fp); - fclose (fp); - } - - // Free the memory buffer. - dc_buffer_free (buffer); - } - - if (dives) { - // Initialize the dive data. - dive_data_t divedata = {0}; - divedata.device = device; - divedata.fingerprint = NULL; - divedata.number = 0; - - // Open the output file. - divedata.fp = fopen (xmlfile, "w"); - - if (divedata.fp) { - fprintf (divedata.fp, "\n"); - } - - // Download the dives. - message ("Downloading the dives.\n"); - rc = dc_device_foreach (device, dive_cb, &divedata); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error downloading the dives."); - dc_buffer_free (divedata.fingerprint); - if (divedata.fp) fclose (divedata.fp); - dc_device_close (device); - return rc; - } - - if (divedata.fp) { - fprintf (divedata.fp, "\n"); - } - - // Store the fingerprint data. - if (g_cachedir) { - fpwrite (divedata.fingerprint, g_cachedir, dc_device_get_type (device), devdata.devinfo.serial); - } - - // Free the fingerprint buffer. - dc_buffer_free (divedata.fingerprint); - - // Close the output file. - if (divedata.fp) fclose (divedata.fp); - } - - // Close the device. - message ("Closing the device.\n"); - rc = dc_device_close (device); - if (rc != DC_STATUS_SUCCESS) { - WARNING ("Error closing the device."); - return rc; - } - - return DC_STATUS_SUCCESS; -} - - -int -main (int argc, char *argv[]) -{ - // Default values. - dc_family_t backend = DC_FAMILY_NULL; - dc_loglevel_t loglevel = DC_LOGLEVEL_WARNING; - const char *name = NULL; - const char *logfile = "output.log"; - const char *rawfile = "output.bin"; - const char *xmlfile = "output.xml"; - const char *devname = NULL; - const char *fingerprint = NULL; - unsigned int model = 0; - int memory = 0, dives = 0; - -#ifndef _MSC_VER - // Parse command-line options. - int opt = 0; - while ((opt = getopt (argc, argv, "n:b:t:f:l:m:d:c:vh")) != -1) { - switch (opt) { - case 'n': - name = optarg; - break; - case 'b': - backend = lookup_type (optarg); - break; - case 't': - model = strtoul (optarg, NULL, 0); - break; - case 'f': - fingerprint = optarg; - g_cachedir_read = 0; - break; - case 'l': - logfile = optarg; - break; - case 'v': - loglevel++; - break; - case 'm': - memory = 1; - rawfile = optarg; - break; - case 'd': - dives = 1; - xmlfile = optarg; - break; - case 'c': - g_cachedir = optarg; - break; - case '?': - case 'h': - default: - usage (argv[0]); - return EXIT_FAILURE; - } - } - - if (optind < argc) - devname = argv[optind]; -#else - if (argc > 1) - backend = lookup_type (argv[1]); - - if (argc > 2) - devname = argv[2]; -#endif - - // Set the default action. - if (!memory && !dives) { - memory = 1; - dives = 1; - } - - signal (SIGINT, sighandler); - - message_set_logfile (logfile); - - dc_context_t *context = NULL; - dc_status_t rc = dc_context_new (&context); - if (rc != DC_STATUS_SUCCESS) { - message_set_logfile (NULL); - return EXIT_FAILURE; - } - - dc_context_set_loglevel (context, loglevel); - dc_context_set_logfunc (context, logfunc, NULL); - - /* Search for a matching device descriptor. */ - dc_descriptor_t *descriptor = NULL; - rc = search (&descriptor, name, backend, model); - if (rc != DC_STATUS_SUCCESS) { - message_set_logfile (NULL); - return EXIT_FAILURE; - } - - /* Fail if no device descriptor found. */ - if (descriptor == NULL) { - WARNING ("No matching device found."); - usage (argv[0]); - message_set_logfile (NULL); - return EXIT_FAILURE; - } - - dc_buffer_t *fp = fpconvert (fingerprint); - rc = dowork (context, descriptor, devname, rawfile, xmlfile, memory, dives, fp); - dc_buffer_free (fp); - message ("Result: %s\n", errmsg (rc)); - - dc_descriptor_free (descriptor); - dc_context_free (context); - - message_set_logfile (NULL); - - return rc != DC_STATUS_SUCCESS ? EXIT_FAILURE : EXIT_SUCCESS; -} From 689e1f1f5584467465b5c16610bec6bbf59df9a7 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 07:30:48 +0100 Subject: [PATCH 02/16] Change the format for error and warning messages. Use the same format as used for printing the error and warning messages from libdivecomputer itself. --- examples/utils.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/utils.h b/examples/utils.h index 7562beb..da8a65b 100644 --- a/examples/utils.h +++ b/examples/utils.h @@ -26,13 +26,20 @@ extern "C" { #endif /* __cplusplus */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define FUNCTION __func__ +#else +#define FUNCTION __FUNCTION__ +#endif + #if defined(__GNUC__) #define ATTR_FORMAT_PRINTF(a,b) __attribute__((format(printf, a, b))) #else #define ATTR_FORMAT_PRINTF(a,b) #endif -#define WARNING(expr) message ("%s:%d: %s\n", __FILE__, __LINE__, expr) +#define WARNING(expr) message("WARNING: %s [in %s:%d (%s)]\n", expr, __FILE__, __LINE__, FUNCTION) +#define ERROR(expr) message("ERROR: %s [in %s:%d (%s)]\n", expr, __FILE__, __LINE__, FUNCTION) int message (const char* fmt, ...) ATTR_FORMAT_PRINTF(1, 2); From 3c57060d95523347cfe5a6f70b01a50058b892d2 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 07:46:52 +0100 Subject: [PATCH 03/16] Setup the library context. --- examples/dctool.c | 63 ++++++++++++++++++++++++++++++++++++++++++++--- examples/dctool.h | 4 ++- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/examples/dctool.c b/examples/dctool.c index b0b0c9c..501e33a 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -63,18 +63,39 @@ dctool_command_find (const char *name) return g_commands[i]; } +static void +logfunc (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata) +{ + const char *loglevels[] = {"NONE", "ERROR", "WARNING", "INFO", "DEBUG", "ALL"}; + + if (loglevel == DC_LOGLEVEL_ERROR || loglevel == DC_LOGLEVEL_WARNING) { + message ("%s: %s [in %s:%d (%s)]\n", loglevels[loglevel], msg, file, line, function); + } else { + message ("%s: %s\n", loglevels[loglevel], msg); + } +} + int main (int argc, char *argv[]) { + int exitcode = EXIT_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; + dc_context_t *context = NULL; + // Default option values. unsigned int help = 0; + dc_loglevel_t loglevel = DC_LOGLEVEL_WARNING; + const char *logfile = NULL; // Parse the command-line options. int opt = 0; - const char *optstring = NOPERMUTATION "h"; + const char *optstring = NOPERMUTATION "hl:qv"; #ifdef HAVE_GETOPT_LONG struct option options[] = { {"help", no_argument, 0, 'h'}, + {"logfile", required_argument, 0, 'l'}, + {"quiet", no_argument, 0, 'q'}, + {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0 } }; while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { @@ -85,6 +106,15 @@ main (int argc, char *argv[]) case 'h': help = 1; break; + case 'l': + logfile = optarg; + break; + case 'q': + loglevel = DC_LOGLEVEL_NONE; + break; + case 'v': + loglevel++; + break; default: return EXIT_FAILURE; } @@ -105,9 +135,15 @@ main (int argc, char *argv[]) "\n" "Options:\n" #ifdef HAVE_GETOPT_LONG - " -h, --help Show help message\n" + " -h, --help Show help message\n" + " -l, --logfile Logfile\n" + " -q, --quiet Quiet mode\n" + " -v, --verbose Verbose mode\n" #else - " -h Show help message\n" + " -h Show help message\n" + " -l Logfile\n" + " -q Quiet mode\n" + " -v Verbose mode\n" #endif "\n"); return EXIT_SUCCESS; @@ -120,6 +156,25 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + // Initialize the logfile. + message_set_logfile (logfile); + + // Initialize a library context. + status = dc_context_new (&context); + if (status != DC_STATUS_SUCCESS) { + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Setup the logging. + dc_context_set_loglevel (context, loglevel); + dc_context_set_logfunc (context, logfunc, NULL); + // Execute the command. - return command->run (argc, argv); + exitcode = command->run (argc, argv, context); + +cleanup: + dc_context_free (context); + message_set_logfile (NULL); + return exitcode; } diff --git a/examples/dctool.h b/examples/dctool.h index 6443caf..db4f527 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -22,12 +22,14 @@ #ifndef DCTOOL_H #define DCTOOL_H +#include + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct dctool_command_t { - int (*run) (int argc, char *argv[]); + int (*run) (int argc, char *argv[], dc_context_t *context); const char *name; const char *description; const char *usage; From f0e5edc50b042019d05849725c1a10946b1367ed Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 07:51:17 +0100 Subject: [PATCH 04/16] Setup the device descriptor. --- examples/Makefile.am | 2 + examples/common.c | 152 +++++++++++++++++++++++++++++++++++++++++++ examples/common.h | 45 +++++++++++++ examples/dctool.c | 60 ++++++++++++++++- examples/dctool.h | 9 ++- 5 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 examples/common.c create mode 100644 examples/common.h diff --git a/examples/Makefile.am b/examples/Makefile.am index ddbade4..29f4bb6 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -5,6 +5,8 @@ bin_PROGRAMS = \ dctool dctool_SOURCES = \ + common.h \ + common.c \ dctool.h \ dctool.c \ utils.h \ diff --git a/examples/common.c b/examples/common.c new file mode 100644 index 0000000..6cb0867 --- /dev/null +++ b/examples/common.c @@ -0,0 +1,152 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 + +#include "common.h" +#include "utils.h" + +#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) + +typedef struct backend_table_t { + const char *name; + dc_family_t type; +} backend_table_t; + +static const backend_table_t g_backends[] = { + {"solution", DC_FAMILY_SUUNTO_SOLUTION}, + {"eon", DC_FAMILY_SUUNTO_EON}, + {"vyper", DC_FAMILY_SUUNTO_VYPER}, + {"vyper2", DC_FAMILY_SUUNTO_VYPER2}, + {"d9", DC_FAMILY_SUUNTO_D9}, + {"eonsteel", DC_FAMILY_SUUNTO_EONSTEEL}, + {"aladin", DC_FAMILY_UWATEC_ALADIN}, + {"memomouse", DC_FAMILY_UWATEC_MEMOMOUSE}, + {"smart", DC_FAMILY_UWATEC_SMART}, + {"meridian", DC_FAMILY_UWATEC_MERIDIAN}, + {"sensus", DC_FAMILY_REEFNET_SENSUS}, + {"sensuspro", DC_FAMILY_REEFNET_SENSUSPRO}, + {"sensusultra", DC_FAMILY_REEFNET_SENSUSULTRA}, + {"vtpro", DC_FAMILY_OCEANIC_VTPRO}, + {"veo250", DC_FAMILY_OCEANIC_VEO250}, + {"atom2", DC_FAMILY_OCEANIC_ATOM2}, + {"nemo", DC_FAMILY_MARES_NEMO}, + {"puck", DC_FAMILY_MARES_PUCK}, + {"darwin", DC_FAMILY_MARES_DARWIN}, + {"iconhd", DC_FAMILY_MARES_ICONHD}, + {"ostc", DC_FAMILY_HW_OSTC}, + {"frog", DC_FAMILY_HW_FROG}, + {"ostc3", DC_FAMILY_HW_OSTC3}, + {"edy", DC_FAMILY_CRESSI_EDY}, + {"leonardo", DC_FAMILY_CRESSI_LEONARDO}, + {"n2ition3", DC_FAMILY_ZEAGLE_N2ITION3}, + {"cobalt", DC_FAMILY_ATOMICS_COBALT}, + {"predator", DC_FAMILY_SHEARWATER_PREDATOR}, + {"petrel", DC_FAMILY_SHEARWATER_PETREL}, + {"nitekq", DC_FAMILY_DIVERITE_NITEKQ}, + {"aqualand", DC_FAMILY_CITIZEN_AQUALAND}, + {"idive", DC_FAMILY_DIVESYSTEM_IDIVE}, +}; + +dc_family_t +dctool_family_type (const char *name) +{ + for (unsigned int i = 0; i < C_ARRAY_SIZE (g_backends); ++i) { + if (strcmp (name, g_backends[i].name) == 0) + return g_backends[i].type; + } + + return DC_FAMILY_NULL; +} + +const char * +dctool_family_name (dc_family_t type) +{ + for (unsigned int i = 0; i < C_ARRAY_SIZE (g_backends); ++i) { + if (g_backends[i].type == type) + return g_backends[i].name; + } + + return NULL; +} + +dc_status_t +dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t family, unsigned int model) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + + dc_iterator_t *iterator = NULL; + rc = dc_descriptor_iterator (&iterator); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error creating the device descriptor iterator."); + return rc; + } + + dc_descriptor_t *descriptor = NULL, *current = NULL; + while ((rc = dc_iterator_next (iterator, &descriptor)) == DC_STATUS_SUCCESS) { + if (name) { + const char *vendor = dc_descriptor_get_vendor (descriptor); + const char *product = dc_descriptor_get_product (descriptor); + + size_t n = strlen (vendor); + if (strncasecmp (name, vendor, n) == 0 && name[n] == ' ' && + strcasecmp (name + n + 1, product) == 0) + { + current = descriptor; + break; + } else if (strcasecmp (name, product) == 0) { + current = descriptor; + break; + } + } else { + if (family == dc_descriptor_get_type (descriptor)) { + if (model == dc_descriptor_get_model (descriptor)) { + // Exact match found. Return immediately. + dc_descriptor_free (current); + current = descriptor; + break; + } else { + // Possible match found. Keep searching for an exact match. + // If no exact match is found, the first match is returned. + if (current == NULL) { + current = descriptor; + descriptor = NULL; + } + } + } + } + + dc_descriptor_free (descriptor); + } + + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_DONE) { + dc_descriptor_free (current); + dc_iterator_free (iterator); + ERROR ("Error iterating the device descriptors."); + return rc; + } + + dc_iterator_free (iterator); + + *out = current; + + return DC_STATUS_SUCCESS; +} diff --git a/examples/common.h b/examples/common.h new file mode 100644 index 0000000..8393fd6 --- /dev/null +++ b/examples/common.h @@ -0,0 +1,45 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 DCTOOL_COMMON_H +#define DCTOOL_COMMON_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_family_t +dctool_family_type (const char *name); + +const char * +dctool_family_name (dc_family_t type); + +dc_status_t +dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t family, unsigned int model); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DCTOOL_COMMON_H */ diff --git a/examples/dctool.c b/examples/dctool.c index 501e33a..48f3687 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -31,6 +31,10 @@ #include #endif +#include +#include + +#include "common.h" #include "dctool.h" #include "utils.h" @@ -81,18 +85,25 @@ main (int argc, char *argv[]) int exitcode = EXIT_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS; dc_context_t *context = NULL; + dc_descriptor_t *descriptor = NULL; // Default option values. unsigned int help = 0; dc_loglevel_t loglevel = DC_LOGLEVEL_WARNING; const char *logfile = NULL; + const char *device = NULL; + dc_family_t family = DC_FAMILY_NULL; + unsigned int model = 0; // Parse the command-line options. int opt = 0; - const char *optstring = NOPERMUTATION "hl:qv"; + const char *optstring = NOPERMUTATION "hd:f:m:l:qv"; #ifdef HAVE_GETOPT_LONG struct option options[] = { {"help", no_argument, 0, 'h'}, + {"device", required_argument, 0, 'd'}, + {"family", required_argument, 0, 'f'}, + {"model", required_argument, 0, 'm'}, {"logfile", required_argument, 0, 'l'}, {"quiet", no_argument, 0, 'q'}, {"verbose", no_argument, 0, 'v'}, @@ -106,6 +117,15 @@ main (int argc, char *argv[]) case 'h': help = 1; break; + case 'd': + device = optarg; + break; + case 'f': + family = dctool_family_type (optarg); + break; + case 'm': + model = strtoul (optarg, NULL, 0); + break; case 'l': logfile = optarg; break; @@ -136,11 +156,17 @@ main (int argc, char *argv[]) "Options:\n" #ifdef HAVE_GETOPT_LONG " -h, --help Show help message\n" + " -d, --device Device name\n" + " -f, --family Device family type\n" + " -m, --model Device model number\n" " -l, --logfile Logfile\n" " -q, --quiet Quiet mode\n" " -v, --verbose Verbose mode\n" #else " -h Show help message\n" + " -d Device name\n" + " -f Family type\n" + " -m Model number\n" " -l Logfile\n" " -q Quiet mode\n" " -v Verbose mode\n" @@ -170,10 +196,40 @@ main (int argc, char *argv[]) dc_context_set_loglevel (context, loglevel); dc_context_set_logfunc (context, logfunc, NULL); + if (command->config & DCTOOL_CONFIG_DESCRIPTOR) { + // Check mandatory arguments. + if (device == NULL && family == DC_FAMILY_NULL) { + message ("No device name or family type specified.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Search for a matching device descriptor. + status = dctool_descriptor_search (&descriptor, device, family, model); + if (status != DC_STATUS_SUCCESS) { + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Fail if no device descriptor found. + if (descriptor == NULL) { + if (device) { + message ("No supported device found: %s\n", + device); + } else { + message ("No supported device found: %s, 0x%X\n", + dctool_family_name (family), model); + } + exitcode = EXIT_FAILURE; + goto cleanup; + } + } + // Execute the command. - exitcode = command->run (argc, argv, context); + exitcode = command->run (argc, argv, context, descriptor); cleanup: + dc_descriptor_free (descriptor); dc_context_free (context); message_set_logfile (NULL); return exitcode; diff --git a/examples/dctool.h b/examples/dctool.h index db4f527..b091c83 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -23,13 +23,20 @@ #define DCTOOL_H #include +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +typedef enum dctool_config_t { + DCTOOL_CONFIG_NONE = 0, + DCTOOL_CONFIG_DESCRIPTOR = 1, +} dctool_config_t; + typedef struct dctool_command_t { - int (*run) (int argc, char *argv[], dc_context_t *context); + int (*run) (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor); + unsigned int config; const char *name; const char *description; const char *usage; From 28d87592945acf85e03ffeb948728773b9ea2d6d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 08:21:34 +0100 Subject: [PATCH 05/16] Setup the cancel signal handler. --- examples/dctool.c | 23 +++++++++++++++++++++++ examples/dctool.h | 3 +++ 2 files changed, 26 insertions(+) diff --git a/examples/dctool.c b/examples/dctool.c index 48f3687..0bc3c55 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef HAVE_GETOPT_H #include #endif @@ -50,6 +51,8 @@ static const dctool_command_t *g_commands[] = { NULL }; +static volatile sig_atomic_t g_cancel = 0; + const dctool_command_t * dctool_command_find (const char *name) { @@ -67,6 +70,23 @@ dctool_command_find (const char *name) return g_commands[i]; } +int +dctool_cancel_cb (void *userdata) +{ + return g_cancel; +} + +static void +sighandler (int signum) +{ +#ifndef _WIN32 + // Restore the default signal handler. + signal (signum, SIG_DFL); +#endif + + g_cancel = 1; +} + static void logfunc (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata) { @@ -182,6 +202,9 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + // Setup the cancel signal handler. + signal (SIGINT, sighandler); + // Initialize the logfile. message_set_logfile (logfile); diff --git a/examples/dctool.h b/examples/dctool.h index b091c83..f8a08ac 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -42,6 +42,9 @@ typedef struct dctool_command_t { const char *usage; } dctool_command_t; +int +dctool_cancel_cb (void *userdata); + #ifdef __cplusplus } #endif /* __cplusplus */ From 655e9ad312ac68521dc219145b38825ca6a6f027 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 08:48:02 +0100 Subject: [PATCH 06/16] Add a default event handler. --- examples/common.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ examples/common.h | 3 +++ 2 files changed, 47 insertions(+) diff --git a/examples/common.c b/examples/common.c index 6cb0867..f892120 100644 --- a/examples/common.c +++ b/examples/common.c @@ -24,6 +24,12 @@ #include "common.h" #include "utils.h" +#ifdef _WIN32 +#define DC_TICKS_FORMAT "%I64d" +#else +#define DC_TICKS_FORMAT "%lld" +#endif + #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) typedef struct backend_table_t { @@ -88,6 +94,44 @@ dctool_family_name (dc_family_t type) return NULL; } +void +dctool_event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *userdata) +{ + const dc_event_progress_t *progress = (const dc_event_progress_t *) data; + const dc_event_devinfo_t *devinfo = (const dc_event_devinfo_t *) data; + const dc_event_clock_t *clock = (const dc_event_clock_t *) data; + const dc_event_vendor_t *vendor = (const dc_event_vendor_t *) data; + + switch (event) { + case DC_EVENT_WAITING: + message ("Event: waiting for user action\n"); + break; + case DC_EVENT_PROGRESS: + message ("Event: progress %3.2f%% (%u/%u)\n", + 100.0 * (double) progress->current / (double) progress->maximum, + progress->current, progress->maximum); + break; + case DC_EVENT_DEVINFO: + message ("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n", + devinfo->model, devinfo->model, + devinfo->firmware, devinfo->firmware, + devinfo->serial, devinfo->serial); + break; + case DC_EVENT_CLOCK: + message ("Event: systime=" DC_TICKS_FORMAT ", devtime=%u\n", + clock->systime, clock->devtime); + break; + case DC_EVENT_VENDOR: + message ("Event: vendor="); + for (unsigned int i = 0; i < vendor->size; ++i) + message ("%02X", vendor->data[i]); + message ("\n"); + break; + default: + break; + } +} + dc_status_t dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t family, unsigned int model) { diff --git a/examples/common.h b/examples/common.h index 8393fd6..1aa19fb 100644 --- a/examples/common.h +++ b/examples/common.h @@ -36,6 +36,9 @@ dctool_family_type (const char *name); const char * dctool_family_name (dc_family_t type); +void +dctool_event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *userdata); + dc_status_t dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t family, unsigned int model); From 5188f5c25f88136625326d10847e18b5feff8ee6 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 08:55:04 +0100 Subject: [PATCH 07/16] Add a helper function for translating status codes. --- examples/common.c | 31 +++++++++++++++++++++++++++++++ examples/common.h | 3 +++ 2 files changed, 34 insertions(+) diff --git a/examples/common.c b/examples/common.c index f892120..d329635 100644 --- a/examples/common.c +++ b/examples/common.c @@ -72,6 +72,37 @@ static const backend_table_t g_backends[] = { {"idive", DC_FAMILY_DIVESYSTEM_IDIVE}, }; +const char * +dctool_errmsg (dc_status_t status) +{ + switch (status) { + case DC_STATUS_SUCCESS: + return "Success"; + case DC_STATUS_UNSUPPORTED: + return "Unsupported operation"; + case DC_STATUS_INVALIDARGS: + return "Invalid arguments"; + case DC_STATUS_NOMEMORY: + return "Out of memory"; + case DC_STATUS_NODEVICE: + return "No device found"; + case DC_STATUS_NOACCESS: + return "Access denied"; + case DC_STATUS_IO: + return "Input/output error"; + case DC_STATUS_TIMEOUT: + return "Timeout"; + case DC_STATUS_PROTOCOL: + return "Protocol error"; + case DC_STATUS_DATAFORMAT: + return "Data format error"; + case DC_STATUS_CANCELLED: + return "Cancelled"; + default: + return "Unknown error"; + } +} + dc_family_t dctool_family_type (const char *name) { diff --git a/examples/common.h b/examples/common.h index 1aa19fb..09f5524 100644 --- a/examples/common.h +++ b/examples/common.h @@ -30,6 +30,9 @@ extern "C" { #endif /* __cplusplus */ +const char * +dctool_errmsg (dc_status_t status); + dc_family_t dctool_family_type (const char *name); From 8f61648a39f8341bbc9e4980d2ebd0c8349c770a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 09:00:06 +0100 Subject: [PATCH 08/16] Add helper functions for reading/writing binary files. --- examples/common.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ examples/common.h | 6 +++++ 2 files changed, 71 insertions(+) diff --git a/examples/common.c b/examples/common.c index d329635..ee552cd 100644 --- a/examples/common.c +++ b/examples/common.c @@ -20,6 +20,12 @@ */ #include +#include + +#ifdef _WIN32 +#include +#include +#endif #include "common.h" #include "utils.h" @@ -225,3 +231,62 @@ dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t f return DC_STATUS_SUCCESS; } + +void +dctool_file_write (const char *filename, dc_buffer_t *buffer) +{ + FILE *fp = NULL; + + // Open the file. + if (filename) { + fp = fopen (filename, "wb"); + } else { + fp = stdout; +#ifdef _WIN32 + // Change from text mode to binary mode. + _setmode (_fileno (fp), _O_BINARY); +#endif + } + if (fp == NULL) + return; + + // Write the entire buffer to the file. + fwrite (dc_buffer_get_data (buffer), 1, dc_buffer_get_size (buffer), fp); + + // Close the file. + fclose (fp); +} + +dc_buffer_t * +dctool_file_read (const char *filename) +{ + FILE *fp = NULL; + + // Open the file. + if (filename) { + fp = fopen (filename, "rb"); + } else { + fp = stdin; +#ifdef _WIN32 + // Change from text mode to binary mode. + _setmode (_fileno (fp), _O_BINARY); +#endif + } + if (fp == NULL) + return NULL; + + // Allocate a memory buffer. + dc_buffer_t *buffer = dc_buffer_new (0); + + // Read the entire file into the buffer. + size_t n = 0; + unsigned char block[1024] = {0}; + while ((n = fread (block, 1, sizeof (block), fp)) > 0) { + dc_buffer_append (buffer, block, n); + } + + // Close the file. + fclose (fp); + + return buffer; +} diff --git a/examples/common.h b/examples/common.h index 09f5524..55dbb91 100644 --- a/examples/common.h +++ b/examples/common.h @@ -45,6 +45,12 @@ dctool_event_cb (dc_device_t *device, dc_event_type_t event, const void *data, v dc_status_t dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t family, unsigned int model); +void +dctool_file_write (const char *filename, dc_buffer_t *buffer); + +dc_buffer_t * +dctool_file_read (const char *filename); + #ifdef __cplusplus } #endif /* __cplusplus */ From 57a54f824c460d224ab451893863df0fddda5111 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 14:24:40 +0100 Subject: [PATCH 09/16] Add helper functions for converting hexadecimal data. --- examples/common.c | 36 ++++++++++++++++++++++++++++++++++++ examples/common.h | 3 +++ 2 files changed, 39 insertions(+) diff --git a/examples/common.c b/examples/common.c index ee552cd..fb7f73e 100644 --- a/examples/common.c +++ b/examples/common.c @@ -232,6 +232,42 @@ dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t f return DC_STATUS_SUCCESS; } +static unsigned char +hex2dec (unsigned char value) +{ + if (value >= '0' && value <= '9') + return value - '0'; + else if (value >= 'A' && value <= 'F') + return value - 'A' + 10; + else if (value >= 'a' && value <= 'f') + return value - 'a' + 10; + else + return 0; +} + +dc_buffer_t * +dctool_convert_hex2bin (const char *str) +{ + // Get the length of the fingerprint data. + size_t nbytes = (str ? strlen (str) / 2 : 0); + if (nbytes == 0) + return NULL; + + // Allocate a memory buffer. + dc_buffer_t *buffer = dc_buffer_new (nbytes); + + // Convert the hexadecimal string. + for (unsigned int i = 0; i < nbytes; ++i) { + unsigned char msn = hex2dec (str[i * 2 + 0]); + unsigned char lsn = hex2dec (str[i * 2 + 1]); + unsigned char byte = (msn << 4) + lsn; + + dc_buffer_append (buffer, &byte, 1); + } + + return buffer; +} + void dctool_file_write (const char *filename, dc_buffer_t *buffer) { diff --git a/examples/common.h b/examples/common.h index 55dbb91..f17d578 100644 --- a/examples/common.h +++ b/examples/common.h @@ -45,6 +45,9 @@ dctool_event_cb (dc_device_t *device, dc_event_type_t event, const void *data, v dc_status_t dctool_descriptor_search (dc_descriptor_t **out, const char *name, dc_family_t family, unsigned int model); +dc_buffer_t * +dctool_convert_hex2bin (const char *str); + void dctool_file_write (const char *filename, dc_buffer_t *buffer); From 38c9ebc74e704167ca83bed8c718a626aaac622f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 15:36:29 +0100 Subject: [PATCH 10/16] Add the help command. --- examples/Makefile.am | 1 + examples/dctool.c | 83 +++++++++++++++++++++----------- examples/dctool.h | 8 ++++ examples/dctool_help.c | 105 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+), 27 deletions(-) create mode 100644 examples/dctool_help.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 29f4bb6..65a4024 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -9,5 +9,6 @@ dctool_SOURCES = \ common.c \ dctool.h \ dctool.c \ + dctool_help.c \ utils.h \ utils.c diff --git a/examples/dctool.c b/examples/dctool.c index 0bc3c55..28d94c1 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -48,6 +48,7 @@ #endif static const dctool_command_t *g_commands[] = { + &dctool_help, NULL }; @@ -70,6 +71,51 @@ dctool_command_find (const char *name) return g_commands[i]; } +void +dctool_command_showhelp (const dctool_command_t *command) +{ + if (command == NULL) { + unsigned int maxlength = 0; + for (size_t i = 0; g_commands[i] != NULL; ++i) { + unsigned int length = strlen (g_commands[i]->name); + if (length > maxlength) + maxlength = length; + } + printf ( + "A simple command line interface for the libdivecomputer library\n" + "\n" + "Usage:\n" + " dctool [options] []\n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" + " -d, --device Device name\n" + " -f, --family Device family type\n" + " -m, --model Device model number\n" + " -l, --logfile Logfile\n" + " -q, --quiet Quiet mode\n" + " -v, --verbose Verbose mode\n" +#else + " -h Show help message\n" + " -d Device name\n" + " -f Family type\n" + " -m Model number\n" + " -l Logfile\n" + " -q Quiet mode\n" + " -v Verbose mode\n" +#endif + "\n" + "Available commands:\n"); + for (size_t i = 0; g_commands[i] != NULL; ++i) { + printf (" %-*s%s\n", maxlength + 3, g_commands[i]->name, g_commands[i]->description); + } + printf ("\nSee 'dctool help ' for more information on a specific command.\n\n"); + } else { + printf ("%s\n\n%s\n", command->description, command->usage); + } +} + int dctool_cancel_cb (void *userdata) { @@ -165,34 +211,17 @@ main (int argc, char *argv[]) argv += optind; optind = RESET; - // Show help message. + // Translate the help option into a command. + char *argv_help[] = {(char *) "help", NULL, NULL}; if (help || argv[0] == NULL) { - printf ( - "A simple command line interface for the libdivecomputer library\n" - "\n" - "Usage:\n" - " dctool [options] []\n" - "\n" - "Options:\n" -#ifdef HAVE_GETOPT_LONG - " -h, --help Show help message\n" - " -d, --device Device name\n" - " -f, --family Device family type\n" - " -m, --model Device model number\n" - " -l, --logfile Logfile\n" - " -q, --quiet Quiet mode\n" - " -v, --verbose Verbose mode\n" -#else - " -h Show help message\n" - " -d Device name\n" - " -f Family type\n" - " -m Model number\n" - " -l Logfile\n" - " -q Quiet mode\n" - " -v Verbose mode\n" -#endif - "\n"); - return EXIT_SUCCESS; + if (argv[0]) { + argv_help[1] = argv[0]; + argv = argv_help; + argc = 2; + } else { + argv = argv_help; + argc = 1; + } } // Try to find the command. diff --git a/examples/dctool.h b/examples/dctool.h index f8a08ac..8edbc6a 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -42,6 +42,14 @@ typedef struct dctool_command_t { const char *usage; } dctool_command_t; +extern const dctool_command_t dctool_help; + +const dctool_command_t * +dctool_command_find (const char *name); + +void +dctool_command_showhelp (const dctool_command_t *command); + int dctool_cancel_cb (void *userdata); diff --git a/examples/dctool_help.c b/examples/dctool_help.c new file mode 100644 index 0000000..2848c24 --- /dev/null +++ b/examples/dctool_help.c @@ -0,0 +1,105 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include + +#include "dctool.h" +#include "utils.h" + +static int +dctool_help_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + // Default option values. + unsigned int help = 0; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "h"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_help); + return EXIT_SUCCESS; + } + + // Try to find the command. + const dctool_command_t *command = NULL; + if (argv[0] != NULL) { + command = dctool_command_find (argv[0]); + if (command == NULL) { + message ("Unknown command %s.\n", argv[0]); + return EXIT_FAILURE; + } + } + + // Show help message for the command. + dctool_command_showhelp (command); + + return EXIT_SUCCESS; +} + +const dctool_command_t dctool_help = { + dctool_help_run, + DCTOOL_CONFIG_NONE, + "help", + "Show basic help instructions", + "Usage:\n" + " dctool help [options] []\n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" +#else + " -h Show help message\n" +#endif +}; From 4dee95d352a3767d75fc1def0b25c1ae8d72bd33 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 17:36:32 +0100 Subject: [PATCH 11/16] Add the version command. --- examples/Makefile.am | 1 + examples/dctool.c | 1 + examples/dctool.h | 1 + examples/dctool_version.c | 94 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 examples/dctool_version.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 65a4024..186ec97 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -10,5 +10,6 @@ dctool_SOURCES = \ dctool.h \ dctool.c \ dctool_help.c \ + dctool_version.c \ utils.h \ utils.c diff --git a/examples/dctool.c b/examples/dctool.c index 28d94c1..da36120 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -49,6 +49,7 @@ static const dctool_command_t *g_commands[] = { &dctool_help, + &dctool_version, NULL }; diff --git a/examples/dctool.h b/examples/dctool.h index 8edbc6a..3b422bd 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -43,6 +43,7 @@ typedef struct dctool_command_t { } dctool_command_t; extern const dctool_command_t dctool_help; +extern const dctool_command_t dctool_version; const dctool_command_t * dctool_command_find (const char *name); diff --git a/examples/dctool_version.c b/examples/dctool_version.c new file mode 100644 index 0000000..deb0702 --- /dev/null +++ b/examples/dctool_version.c @@ -0,0 +1,94 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#include "dctool.h" + +static int +dctool_version_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + // Default option values. + unsigned int help = 0; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "h"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_version); + return EXIT_SUCCESS; + } + + printf ("libdivecomputer version %s\n", dc_version (NULL)); + + return EXIT_SUCCESS; +} + +const dctool_command_t dctool_version = { + dctool_version_run, + DCTOOL_CONFIG_NONE, + "version", + "Show version information", + "Usage:\n" + " dctool version [options]\n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" +#else + " -h Show help message\n" +#endif +}; From ff7c3f6901655416389aaa995bbc40c4042465d6 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 19:06:34 +0100 Subject: [PATCH 12/16] Add the list command. --- examples/Makefile.am | 1 + examples/dctool.c | 1 + examples/dctool.h | 1 + examples/dctool_list.c | 103 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 examples/dctool_list.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 186ec97..0356d90 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -11,5 +11,6 @@ dctool_SOURCES = \ dctool.c \ dctool_help.c \ dctool_version.c \ + dctool_list.c \ utils.h \ utils.c diff --git a/examples/dctool.c b/examples/dctool.c index da36120..7cf6728 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -50,6 +50,7 @@ static const dctool_command_t *g_commands[] = { &dctool_help, &dctool_version, + &dctool_list, NULL }; diff --git a/examples/dctool.h b/examples/dctool.h index 3b422bd..c1239a0 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -44,6 +44,7 @@ typedef struct dctool_command_t { extern const dctool_command_t dctool_help; extern const dctool_command_t dctool_version; +extern const dctool_command_t dctool_list; const dctool_command_t * dctool_command_find (const char *name); diff --git a/examples/dctool_list.c b/examples/dctool_list.c new file mode 100644 index 0000000..89e5386 --- /dev/null +++ b/examples/dctool_list.c @@ -0,0 +1,103 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#include "dctool.h" + +static int +dctool_list_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *dummy) +{ + // Default option values. + unsigned int help = 0; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "h"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_list); + return EXIT_SUCCESS; + } + + dc_iterator_t *iterator = NULL; + dc_descriptor_t *descriptor = NULL; + dc_descriptor_iterator (&iterator); + while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) { + printf ("%s %s\n", + dc_descriptor_get_vendor (descriptor), + dc_descriptor_get_product (descriptor)); + dc_descriptor_free (descriptor); + } + dc_iterator_free (iterator); + + return EXIT_SUCCESS; +} + +const dctool_command_t dctool_list = { + dctool_list_run, + DCTOOL_CONFIG_NONE, + "list", + "List supported devices", + "Usage:\n" + " dctool list [options]\n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" +#else + " -h Show help message\n" +#endif +}; From bfbb56c781f209694b51904572888132faeeef63 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 19:08:57 +0100 Subject: [PATCH 13/16] Add the dump command. --- examples/Makefile.am | 1 + examples/dctool.c | 1 + examples/dctool.h | 1 + examples/dctool_dump.c | 191 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 examples/dctool_dump.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 0356d90..fffc238 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -12,5 +12,6 @@ dctool_SOURCES = \ dctool_help.c \ dctool_version.c \ dctool_list.c \ + dctool_dump.c \ utils.h \ utils.c diff --git a/examples/dctool.c b/examples/dctool.c index 7cf6728..24f14c9 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -51,6 +51,7 @@ static const dctool_command_t *g_commands[] = { &dctool_help, &dctool_version, &dctool_list, + &dctool_dump, NULL }; diff --git a/examples/dctool.h b/examples/dctool.h index c1239a0..064ea9b 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -45,6 +45,7 @@ typedef struct dctool_command_t { extern const dctool_command_t dctool_help; extern const dctool_command_t dctool_version; extern const dctool_command_t dctool_list; +extern const dctool_command_t dctool_dump; const dctool_command_t * dctool_command_find (const char *name); diff --git a/examples/dctool_dump.c b/examples/dctool_dump.c new file mode 100644 index 0000000..ce59899 --- /dev/null +++ b/examples/dctool_dump.c @@ -0,0 +1,191 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#include "dctool.h" +#include "common.h" +#include "utils.h" + +static dc_status_t +dump (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, dc_buffer_t *fingerprint, dc_buffer_t *buffer) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *device = NULL; + + // Open the device. + message ("Opening the device (%s %s, %s).\n", + dc_descriptor_get_vendor (descriptor), + dc_descriptor_get_product (descriptor), + devname ? devname : "null"); + rc = dc_device_open (&device, context, descriptor, devname); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error opening the device."); + goto cleanup; + } + + // Register the event handler. + message ("Registering the event handler.\n"); + int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; + rc = dc_device_set_events (device, events, dctool_event_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the event handler."); + goto cleanup; + } + + // Register the cancellation handler. + message ("Registering the cancellation handler.\n"); + rc = dc_device_set_cancel (device, dctool_cancel_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the cancellation handler."); + goto cleanup; + } + + // Register the fingerprint data. + if (fingerprint) { + message ("Registering the fingerprint data.\n"); + rc = dc_device_set_fingerprint (device, dc_buffer_get_data (fingerprint), dc_buffer_get_size (fingerprint)); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the fingerprint data."); + goto cleanup; + } + } + + // Download the memory dump. + message ("Downloading the memory dump.\n"); + rc = dc_device_dump (device, buffer); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error downloading the memory dump."); + goto cleanup; + } + +cleanup: + dc_device_close (device); + return rc; +} + +static int +dctool_dump_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + int exitcode = EXIT_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; + dc_buffer_t *fingerprint = NULL; + dc_buffer_t *buffer = NULL; + + // Default option values. + unsigned int help = 0; + const char *fphex = NULL; + const char *filename = NULL; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "ho:p:"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"output", required_argument, 0, 'o'}, + {"fingerprint", required_argument, 0, 'p'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + case 'o': + filename = optarg; + break; + case 'p': + fphex = optarg; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_dump); + return EXIT_SUCCESS; + } + + // Convert the fingerprint to binary. + fingerprint = dctool_convert_hex2bin (fphex); + + // Allocate a memory buffer. + buffer = dc_buffer_new (0); + + // Download the memory dump. + status = dump (context, descriptor, argv[0], fingerprint, buffer); + if (status != DC_STATUS_SUCCESS) { + message ("ERROR: %s\n", dctool_errmsg (status)); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Write the memory dump to disk. + dctool_file_write (filename, buffer); + +cleanup: + dc_buffer_free (buffer); + dc_buffer_free (fingerprint); + return exitcode; +} + +const dctool_command_t dctool_dump = { + dctool_dump_run, + DCTOOL_CONFIG_DESCRIPTOR, + "dump", + "Download a memory dump", + "Usage:\n" + " dctool dump [options] \n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" + " -o, --output Output filename\n" + " -p, --fingerprint Fingerprint data (hexadecimal)\n" +#else + " -h Show help message\n" + " -o Output filename\n" + " -p Fingerprint data (hexadecimal)\n" +#endif +}; From 195c4c7a7e240ad820b3d9c7b1f8cd402d1c8f68 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 19:19:34 +0100 Subject: [PATCH 14/16] Add the download command. --- examples/Makefile.am | 1 + examples/dctool.c | 1 + examples/dctool.h | 1 + examples/dctool_download.c | 616 +++++++++++++++++++++++++++++++++++++ 4 files changed, 619 insertions(+) create mode 100644 examples/dctool_download.c diff --git a/examples/Makefile.am b/examples/Makefile.am index fffc238..35f41bc 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -12,6 +12,7 @@ dctool_SOURCES = \ dctool_help.c \ dctool_version.c \ dctool_list.c \ + dctool_download.c \ dctool_dump.c \ utils.h \ utils.c diff --git a/examples/dctool.c b/examples/dctool.c index 24f14c9..faba3a5 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -51,6 +51,7 @@ static const dctool_command_t *g_commands[] = { &dctool_help, &dctool_version, &dctool_list, + &dctool_download, &dctool_dump, NULL }; diff --git a/examples/dctool.h b/examples/dctool.h index 064ea9b..d4a17be 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -45,6 +45,7 @@ typedef struct dctool_command_t { extern const dctool_command_t dctool_help; extern const dctool_command_t dctool_version; extern const dctool_command_t dctool_list; +extern const dctool_command_t dctool_download; extern const dctool_command_t dctool_dump; const dctool_command_t * diff --git a/examples/dctool_download.c b/examples/dctool_download.c new file mode 100644 index 0000000..61c4127 --- /dev/null +++ b/examples/dctool_download.c @@ -0,0 +1,616 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include +#include + +#include "dctool.h" +#include "common.h" +#include "utils.h" + +typedef struct event_data_t { + const char *cachedir; + dc_event_devinfo_t devinfo; +} event_data_t; + +typedef struct dive_data_t { + FILE* ostream; + dc_device_t *device; + dc_buffer_t **fingerprint; + unsigned int number; +} dive_data_t; + +typedef struct sample_data_t { + FILE* ostream; + unsigned int nsamples; +} sample_data_t; + +static void +sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata) +{ + static const char *events[] = { + "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter", + "violation", "bookmark", "surface", "safety stop", "gaschange", + "safety stop (voluntary)", "safety stop (mandatory)", "deepstop", + "ceiling (safety stop)", "floor", "divetime", "maxdepth", + "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning", + "gaschange2"}; + static const char *decostop[] = { + "ndl", "safety", "deco", "deep"}; + + sample_data_t *sampledata = (sample_data_t *) userdata; + + switch (type) { + case DC_SAMPLE_TIME: + if (sampledata->nsamples++) + fprintf (sampledata->ostream, "\n"); + fprintf (sampledata->ostream, "\n"); + fprintf (sampledata->ostream, " \n", value.time / 60, value.time % 60); + break; + case DC_SAMPLE_DEPTH: + fprintf (sampledata->ostream, " %.2f\n", value.depth); + break; + case DC_SAMPLE_PRESSURE: + fprintf (sampledata->ostream, " %.2f\n", value.pressure.tank, value.pressure.value); + break; + case DC_SAMPLE_TEMPERATURE: + fprintf (sampledata->ostream, " %.2f\n", value.temperature); + break; + case DC_SAMPLE_EVENT: + if (value.event.type != SAMPLE_EVENT_GASCHANGE && value.event.type != SAMPLE_EVENT_GASCHANGE2) { + fprintf (sampledata->ostream, " %s\n", + value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]); + } + break; + case DC_SAMPLE_RBT: + fprintf (sampledata->ostream, " %u\n", value.rbt); + break; + case DC_SAMPLE_HEARTBEAT: + fprintf (sampledata->ostream, " %u\n", value.heartbeat); + break; + case DC_SAMPLE_BEARING: + fprintf (sampledata->ostream, " %u\n", value.bearing); + break; + case DC_SAMPLE_VENDOR: + fprintf (sampledata->ostream, " ", value.vendor.type, value.vendor.size); + for (unsigned int i = 0; i < value.vendor.size; ++i) + fprintf (sampledata->ostream, "%02X", ((unsigned char *) value.vendor.data)[i]); + fprintf (sampledata->ostream, "\n"); + break; + case DC_SAMPLE_SETPOINT: + fprintf (sampledata->ostream, " %.2f\n", value.setpoint); + break; + case DC_SAMPLE_PPO2: + fprintf (sampledata->ostream, " %.2f\n", value.ppo2); + break; + case DC_SAMPLE_CNS: + fprintf (sampledata->ostream, " %.1f\n", value.cns * 100.0); + break; + case DC_SAMPLE_DECO: + fprintf (sampledata->ostream, " %s\n", + value.deco.time, value.deco.depth, decostop[value.deco.type]); + break; + case DC_SAMPLE_GASMIX: + fprintf (sampledata->ostream, " %u\n", value.gasmix); + break; + default: + break; + } +} + +static dc_status_t +doparse (FILE *ostream, dc_device_t *device, const unsigned char data[], unsigned int size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_parser_t *parser = NULL; + + // Create the parser. + message ("Creating the parser.\n"); + rc = dc_parser_new (&parser, device); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error creating the parser."); + goto cleanup; + } + + // Register the data. + message ("Registering the data.\n"); + rc = dc_parser_set_data (parser, data, size); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the data."); + goto cleanup; + } + + // Parse the datetime. + message ("Parsing the datetime.\n"); + dc_datetime_t dt = {0}; + rc = dc_parser_get_datetime (parser, &dt); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the datetime."); + goto cleanup; + } + + fprintf (ostream, "%04i-%02i-%02i %02i:%02i:%02i\n", + dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second); + + // Parse the divetime. + message ("Parsing the divetime.\n"); + unsigned int divetime = 0; + rc = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the divetime."); + goto cleanup; + } + + fprintf (ostream, "%02u:%02u\n", + divetime / 60, divetime % 60); + + // Parse the maxdepth. + message ("Parsing the maxdepth.\n"); + double maxdepth = 0.0; + rc = dc_parser_get_field (parser, DC_FIELD_MAXDEPTH, 0, &maxdepth); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the maxdepth."); + goto cleanup; + } + + fprintf (ostream, "%.2f\n", + maxdepth); + + // Parse the temperature. + message ("Parsing the temperature.\n"); + for (unsigned int i = 0; i < 3; ++i) { + dc_field_type_t fields[] = {DC_FIELD_TEMPERATURE_SURFACE, + DC_FIELD_TEMPERATURE_MINIMUM, + DC_FIELD_TEMPERATURE_MAXIMUM}; + const char *names[] = {"surface", "minimum", "maximum"}; + + double temperature = 0.0; + rc = dc_parser_get_field (parser, fields[i], 0, &temperature); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the temperature."); + goto cleanup; + } + + if (rc != DC_STATUS_UNSUPPORTED) { + fprintf (ostream, "%.1f\n", + names[i], temperature); + } + } + + // Parse the gas mixes. + message ("Parsing the gas mixes.\n"); + unsigned int ngases = 0; + rc = dc_parser_get_field (parser, DC_FIELD_GASMIX_COUNT, 0, &ngases); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the gas mix count."); + goto cleanup; + } + + for (unsigned int i = 0; i < ngases; ++i) { + dc_gasmix_t gasmix = {0}; + rc = dc_parser_get_field (parser, DC_FIELD_GASMIX, i, &gasmix); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the gas mix."); + goto cleanup; + } + + fprintf (ostream, + "\n" + " %.1f\n" + " %.1f\n" + " %.1f\n" + "\n", + gasmix.helium * 100.0, + gasmix.oxygen * 100.0, + gasmix.nitrogen * 100.0); + } + + // Parse the tanks. + message ("Parsing the tanks.\n"); + unsigned int ntanks = 0; + rc = dc_parser_get_field (parser, DC_FIELD_TANK_COUNT, 0, &ntanks); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the tank count."); + goto cleanup; + } + + for (unsigned int i = 0; i < ntanks; ++i) { + const char *names[] = {"none", "metric", "imperial"}; + + dc_tank_t tank = {0}; + rc = dc_parser_get_field (parser, DC_FIELD_TANK, i, &tank); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the tank."); + goto cleanup; + } + + fprintf (ostream, "\n"); + if (tank.gasmix != DC_GASMIX_UNKNOWN) { + fprintf (ostream, + " %u\n", + tank.gasmix); + } + if (tank.type != DC_TANKVOLUME_NONE) { + fprintf (ostream, + " %s\n" + " %.1f\n" + " %.2f\n", + names[tank.type], tank.volume, tank.workpressure); + } + fprintf (ostream, + " %.2f\n" + " %.2f\n" + "\n", + tank.beginpressure, tank.endpressure); + } + + // Parse the dive mode. + message ("Parsing the dive mode.\n"); + dc_divemode_t divemode = DC_DIVEMODE_OC; + rc = dc_parser_get_field (parser, DC_FIELD_DIVEMODE, 0, &divemode); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the dive mode."); + goto cleanup; + } + + if (rc != DC_STATUS_UNSUPPORTED) { + const char *names[] = {"freedive", "gauge", "oc", "cc"}; + fprintf (ostream, "%s\n", + names[divemode]); + } + + // Parse the salinity. + message ("Parsing the salinity.\n"); + dc_salinity_t salinity = {DC_WATER_FRESH, 0.0}; + rc = dc_parser_get_field (parser, DC_FIELD_SALINITY, 0, &salinity); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the salinity."); + goto cleanup; + } + + if (rc != DC_STATUS_UNSUPPORTED) { + fprintf (ostream, "%.1f\n", + salinity.type, salinity.density); + } + + // Parse the atmospheric pressure. + message ("Parsing the atmospheric pressure.\n"); + double atmospheric = 0.0; + rc = dc_parser_get_field (parser, DC_FIELD_ATMOSPHERIC, 0, &atmospheric); + if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the atmospheric pressure."); + goto cleanup; + } + + if (rc != DC_STATUS_UNSUPPORTED) { + fprintf (ostream, "%.5f\n", + atmospheric); + } + + // Initialize the sample data. + sample_data_t sampledata = {0}; + sampledata.nsamples = 0; + sampledata.ostream = ostream; + + // Parse the sample data. + message ("Parsing the sample data.\n"); + rc = dc_parser_samples_foreach (parser, sample_cb, &sampledata); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error parsing the sample data."); + goto cleanup; + } + + if (sampledata.nsamples) + fprintf (ostream, "\n"); + +cleanup: + dc_parser_destroy (parser); + return rc; +} + +static int +dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata) +{ + dive_data_t *divedata = (dive_data_t *) userdata; + + divedata->number++; + + message ("Dive: number=%u, size=%u, fingerprint=", divedata->number, size); + for (unsigned int i = 0; i < fsize; ++i) + message ("%02X", fingerprint[i]); + message ("\n"); + + // Keep a copy of the most recent fingerprint. Because dives are + // guaranteed to be downloaded in reverse order, the most recent + // dive is always the first dive. + if (divedata->number == 1) { + dc_buffer_t *fp = dc_buffer_new (fsize); + dc_buffer_append (fp, fingerprint, fsize); + *divedata->fingerprint = fp; + } + + fprintf (divedata->ostream, "\n%u\n%u\n", divedata->number, size); + for (unsigned int i = 0; i < fsize; ++i) + fprintf (divedata->ostream, "%02X", fingerprint[i]); + fprintf (divedata->ostream, "\n"); + + doparse (divedata->ostream, divedata->device, data, size); + + fprintf (divedata->ostream, "\n"); + + return 1; +} + +static void +event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *userdata) +{ + const dc_event_devinfo_t *devinfo = (const dc_event_devinfo_t *) data; + + event_data_t *eventdata = (event_data_t *) userdata; + + // Forward to the default event handler. + dctool_event_cb (device, event, data, userdata); + + switch (event) { + case DC_EVENT_DEVINFO: + // Load the fingerprint from the cache. If there is no + // fingerprint present in the cache, a NULL buffer is returned, + // and the registered fingerprint will be cleared. + if (eventdata->cachedir) { + char filename[1024] = {0}; + dc_family_t family = DC_FAMILY_NULL; + dc_buffer_t *fingerprint = NULL; + + // Generate the fingerprint filename. + family = dc_device_get_type (device); + snprintf (filename, sizeof (filename), "%s/%s-%08X.bin", + eventdata->cachedir, dctool_family_name (family), devinfo->serial); + + // Read the fingerprint file. + fingerprint = dctool_file_read (filename); + + // Register the fingerprint data. + dc_device_set_fingerprint (device, + dc_buffer_get_data (fingerprint), + dc_buffer_get_size (fingerprint)); + + // Free the buffer again. + dc_buffer_free (fingerprint); + } + + // Keep a copy of the event data. It will be used for generating + // the fingerprint filename again after a (successful) download. + eventdata->devinfo = *devinfo; + break; + default: + break; + } +} + +static dc_status_t +download (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *cachedir, dc_buffer_t *fingerprint, FILE *ostream) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *device = NULL; + dc_buffer_t *ofingerprint = NULL; + + // Open the device. + message ("Opening the device (%s %s, %s).\n", + dc_descriptor_get_vendor (descriptor), + dc_descriptor_get_product (descriptor), + devname ? devname : "null"); + rc = dc_device_open (&device, context, descriptor, devname); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error opening the device."); + goto cleanup; + } + + // Initialize the event data. + event_data_t eventdata = {0}; + if (fingerprint) { + eventdata.cachedir = NULL; + } else { + eventdata.cachedir = cachedir; + } + + // Register the event handler. + message ("Registering the event handler.\n"); + int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; + rc = dc_device_set_events (device, events, event_cb, &eventdata); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the event handler."); + goto cleanup; + } + + // Register the cancellation handler. + message ("Registering the cancellation handler.\n"); + rc = dc_device_set_cancel (device, dctool_cancel_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the cancellation handler."); + goto cleanup; + } + + // Register the fingerprint data. + if (fingerprint) { + message ("Registering the fingerprint data.\n"); + rc = dc_device_set_fingerprint (device, dc_buffer_get_data (fingerprint), dc_buffer_get_size (fingerprint)); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the fingerprint data."); + goto cleanup; + } + } + + // Initialize the dive data. + dive_data_t divedata = {0}; + divedata.device = device; + divedata.ostream = ostream; + divedata.fingerprint = &ofingerprint; + divedata.number = 0; + + fprintf (ostream, "\n"); + + // Download the dives. + message ("Downloading the dives.\n"); + rc = dc_device_foreach (device, dive_cb, &divedata); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error downloading the dives."); + goto cleanup; + } + + fprintf (ostream, "\n"); + + // Store the fingerprint data. + if (cachedir && ofingerprint) { + char filename[1024] = {0}; + dc_family_t family = DC_FAMILY_NULL; + + // Generate the fingerprint filename. + family = dc_device_get_type (device); + snprintf (filename, sizeof (filename), "%s/%s-%08X.bin", + cachedir, dctool_family_name (family), eventdata.devinfo.serial); + + // Write the fingerprint file. + dctool_file_write (filename, ofingerprint); + } + +cleanup: + dc_buffer_free (ofingerprint); + dc_device_close (device); + return rc; +} + +static int +dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + int exitcode = EXIT_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; + dc_buffer_t *fingerprint = NULL; + FILE *ostream = NULL; + + // Default option values. + unsigned int help = 0; + const char *fphex = NULL; + const char *filename = NULL; + const char *cachedir = NULL; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "ho:p:c:"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"output", required_argument, 0, 'o'}, + {"fingerprint", required_argument, 0, 'p'}, + {"cache", required_argument, 0, 'c'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + case 'o': + filename = optarg; + break; + case 'p': + fphex = optarg; + break; + case 'c': + cachedir = optarg; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_download); + return EXIT_SUCCESS; + } + + // Convert the fingerprint to binary. + fingerprint = dctool_convert_hex2bin (fphex); + + // Open the output file. + ostream = fopen (filename, "w"); + if (ostream == NULL) { + message ("Failed to open the output file.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Download the dives. + status = download (context, descriptor, argv[0], cachedir, fingerprint, ostream); + if (status != DC_STATUS_SUCCESS) { + message ("ERROR: %s\n", dctool_errmsg (status)); + exitcode = EXIT_FAILURE; + goto cleanup; + } + +cleanup: + if (ostream) fclose (ostream); + dc_buffer_free (fingerprint); + return exitcode; +} + +const dctool_command_t dctool_download = { + dctool_download_run, + DCTOOL_CONFIG_DESCRIPTOR, + "download", + "Download the dives", + "Usage:\n" + " dctool download [options] \n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" + " -o, --output Output filename\n" + " -p, --fingerprint Fingerprint data (hexadecimal)\n" + " -c, --cache Cache directory\n" +#else + " -h Show help message\n" + " -o Output filename\n" + " -p Fingerprint data (hexadecimal)\n" + " -c Cache directory\n" +#endif +}; From ce472ffa19fcdd5fbbe6f86170e4d54b518fade2 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 19:46:48 +0100 Subject: [PATCH 15/16] Add the read and write commands. --- examples/Makefile.am | 2 + examples/dctool.c | 2 + examples/dctool.h | 2 + examples/dctool_read.c | 197 +++++++++++++++++++++++++++++++++++++++ examples/dctool_write.c | 200 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 403 insertions(+) create mode 100644 examples/dctool_read.c create mode 100644 examples/dctool_write.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 35f41bc..a0674ca 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -14,5 +14,7 @@ dctool_SOURCES = \ dctool_list.c \ dctool_download.c \ dctool_dump.c \ + dctool_read.c \ + dctool_write.c \ utils.h \ utils.c diff --git a/examples/dctool.c b/examples/dctool.c index faba3a5..d174372 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -53,6 +53,8 @@ static const dctool_command_t *g_commands[] = { &dctool_list, &dctool_download, &dctool_dump, + &dctool_read, + &dctool_write, NULL }; diff --git a/examples/dctool.h b/examples/dctool.h index d4a17be..a818aee 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -47,6 +47,8 @@ extern const dctool_command_t dctool_version; extern const dctool_command_t dctool_list; extern const dctool_command_t dctool_download; extern const dctool_command_t dctool_dump; +extern const dctool_command_t dctool_read; +extern const dctool_command_t dctool_write; const dctool_command_t * dctool_command_find (const char *name); diff --git a/examples/dctool_read.c b/examples/dctool_read.c new file mode 100644 index 0000000..85bd9de --- /dev/null +++ b/examples/dctool_read.c @@ -0,0 +1,197 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#include "dctool.h" +#include "common.h" +#include "utils.h" + +static dc_status_t +doread (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, unsigned int address, dc_buffer_t *buffer) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *device = NULL; + + // Open the device. + message ("Opening the device (%s %s, %s).\n", + dc_descriptor_get_vendor (descriptor), + dc_descriptor_get_product (descriptor), + devname ? devname : "null"); + rc = dc_device_open (&device, context, descriptor, devname); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error opening the device."); + goto cleanup; + } + + // Register the event handler. + message ("Registering the event handler.\n"); + int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; + rc = dc_device_set_events (device, events, dctool_event_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the event handler."); + goto cleanup; + } + + // Register the cancellation handler. + message ("Registering the cancellation handler.\n"); + rc = dc_device_set_cancel (device, dctool_cancel_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the cancellation handler."); + goto cleanup; + } + + // Read data from the internal memory. + message ("Reading data from the internal memory.\n"); + rc = dc_device_read (device, address, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer)); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error reading from the internal memory."); + goto cleanup; + } + +cleanup: + dc_device_close (device); + return rc; +} + +static int +dctool_read_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + int exitcode = EXIT_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; + dc_buffer_t *buffer = NULL; + + // Default option values. + unsigned int help = 0; + const char *filename = NULL; + unsigned int address = 0, have_address = 0; + unsigned int count = 0, have_count = 0; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "ha:c:o:"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"address", required_argument, 0, 'a'}, + {"count", required_argument, 0, 'c'}, + {"output", required_argument, 0, 'o'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + case 'a': + address = strtoul (optarg, NULL, 0); + have_address = 1; + break; + case 'c': + count = strtoul (optarg, NULL, 0); + have_count = 1; + break; + case 'o': + filename = optarg; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_read); + return EXIT_SUCCESS; + } + + // Check mandatory arguments. + if (!have_address || !have_count) { + message ("No memory address or byte count specified.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Allocate a memory buffer. + buffer = dc_buffer_new (count); + dc_buffer_resize (buffer, count); + if (buffer == NULL) { + message ("Failed to allocate a memory buffer.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Read data from the internal memory. + status = doread (context, descriptor, argv[0], address, buffer); + if (status != DC_STATUS_SUCCESS) { + message ("ERROR: %s\n", dctool_errmsg (status)); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Write the buffer to file. + dctool_file_write (filename, buffer); + +cleanup: + dc_buffer_free (buffer); + return exitcode; +} + +const dctool_command_t dctool_read = { + dctool_read_run, + DCTOOL_CONFIG_DESCRIPTOR, + "read", + "Read data from the internal memory", + "Usage:\n" + " dctool read [options] \n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" + " -a, --address
Memory address\n" + " -c, --count Number of bytes\n" + " -o, --output Output filename\n" +#else + " -h Show help message\n" + " -a
Memory address\n" + " -c Number of bytes\n" + " -o Output filename\n" +#endif +}; diff --git a/examples/dctool_write.c b/examples/dctool_write.c new file mode 100644 index 0000000..4bf193b --- /dev/null +++ b/examples/dctool_write.c @@ -0,0 +1,200 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#include "dctool.h" +#include "common.h" +#include "utils.h" + +static dc_status_t +dowrite (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, unsigned int address, dc_buffer_t *buffer) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *device = NULL; + + // Open the device. + message ("Opening the device (%s %s, %s).\n", + dc_descriptor_get_vendor (descriptor), + dc_descriptor_get_product (descriptor), + devname ? devname : "null"); + rc = dc_device_open (&device, context, descriptor, devname); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error opening the device."); + goto cleanup; + } + + // Register the event handler. + message ("Registering the event handler.\n"); + int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; + rc = dc_device_set_events (device, events, dctool_event_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the event handler."); + goto cleanup; + } + + // Register the cancellation handler. + message ("Registering the cancellation handler.\n"); + rc = dc_device_set_cancel (device, dctool_cancel_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the cancellation handler."); + goto cleanup; + } + + // Write data to the internal memory. + message ("Writing data to the internal memory.\n"); + rc = dc_device_write (device, address, dc_buffer_get_data (buffer), dc_buffer_get_size (buffer)); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error writing to the internal memory."); + goto cleanup; + } + +cleanup: + dc_device_close (device); + return rc; +} + +static int +dctool_write_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + int exitcode = EXIT_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; + dc_buffer_t *buffer = NULL; + + // Default option values. + unsigned int help = 0; + const char *filename = NULL; + unsigned int address = 0, have_address = 0; + unsigned int count = 0, have_count = 0; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "ha:c:i:"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"address", required_argument, 0, 'a'}, + {"count", required_argument, 0, 'c'}, + {"input", required_argument, 0, 'i'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + case 'a': + address = strtoul (optarg, NULL, 0); + have_address = 1; + break; + case 'c': + count = strtoul (optarg, NULL, 0); + have_count = 1; + break; + case 'i': + filename = optarg; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_write); + return EXIT_SUCCESS; + } + + // Check mandatory arguments. + if (!have_address) { + message ("No memory address specified.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Read the buffer from file. + buffer = dctool_file_read (filename); + if (buffer == NULL) { + message ("Failed to read the input file.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Check the number of bytes (if provided) + if (have_count && count != dc_buffer_get_size (buffer)) { + message ("Number of bytes doesn't match file length.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Write data to the internal memory. + status = dowrite (context, descriptor, argv[0], address, buffer); + if (status != DC_STATUS_SUCCESS) { + message ("ERROR: %s\n", dctool_errmsg (status)); + exitcode = EXIT_FAILURE; + goto cleanup; + } + +cleanup: + dc_buffer_free (buffer); + return exitcode; +} + +const dctool_command_t dctool_write = { + dctool_write_run, + DCTOOL_CONFIG_DESCRIPTOR, + "write", + "Write data to the internal memory", + "Usage:\n" + " dctool write [options] \n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" + " -a, --address
Memory address\n" + " -c, --count Number of bytes\n" + " -i, --input Input filename\n" +#else + " -h Show help message\n" + " -a
Memory address\n" + " -c Number of bytes\n" + " -i Input filename\n" +#endif +}; From 013882f3f20c5659f46f57dbd069b0915dce6176 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 28 Dec 2015 20:28:22 +0100 Subject: [PATCH 16/16] Add the fwupdate command. --- examples/Makefile.am | 1 + examples/dctool.c | 1 + examples/dctool.h | 1 + examples/dctool_fwupdate.c | 179 +++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 examples/dctool_fwupdate.c diff --git a/examples/Makefile.am b/examples/Makefile.am index a0674ca..cb13b64 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -16,5 +16,6 @@ dctool_SOURCES = \ dctool_dump.c \ dctool_read.c \ dctool_write.c \ + dctool_fwupdate.c \ utils.h \ utils.c diff --git a/examples/dctool.c b/examples/dctool.c index d174372..4d70c0e 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -55,6 +55,7 @@ static const dctool_command_t *g_commands[] = { &dctool_dump, &dctool_read, &dctool_write, + &dctool_fwupdate, NULL }; diff --git a/examples/dctool.h b/examples/dctool.h index a818aee..a0e09d7 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -49,6 +49,7 @@ extern const dctool_command_t dctool_download; extern const dctool_command_t dctool_dump; extern const dctool_command_t dctool_read; extern const dctool_command_t dctool_write; +extern const dctool_command_t dctool_fwupdate; const dctool_command_t * dctool_command_find (const char *name); diff --git a/examples/dctool_fwupdate.c b/examples/dctool_fwupdate.c new file mode 100644 index 0000000..13b4fe3 --- /dev/null +++ b/examples/dctool_fwupdate.c @@ -0,0 +1,179 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * 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 +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "dctool.h" +#include "common.h" +#include "utils.h" + +static dc_status_t +fwupdate (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *hexfile) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *device = NULL; + + // Open the device. + message ("Opening the device (%s %s, %s).\n", + dc_descriptor_get_vendor (descriptor), + dc_descriptor_get_product (descriptor), + devname ? devname : "null"); + rc = dc_device_open (&device, context, descriptor, devname); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error opening the device."); + goto cleanup; + } + + // Register the event handler. + message ("Registering the event handler.\n"); + int events = DC_EVENT_PROGRESS; + rc = dc_device_set_events (device, events, dctool_event_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the event handler."); + goto cleanup; + } + + // Register the cancellation handler. + message ("Registering the cancellation handler.\n"); + rc = dc_device_set_cancel (device, dctool_cancel_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the cancellation handler."); + goto cleanup; + } + + // Update the firmware. + message ("Updating the firmware.\n"); + switch (dc_device_get_type (device)) { + case DC_FAMILY_HW_OSTC: + rc = hw_ostc_device_fwupdate (device, hexfile); + break; + case DC_FAMILY_HW_OSTC3: + rc = hw_ostc3_device_fwupdate (device, hexfile); + break; + default: + rc = DC_STATUS_UNSUPPORTED; + break; + } + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error updating the firmware."); + goto cleanup; + } + +cleanup: + dc_device_close (device); + return rc; +} + +static int +dctool_fwupdate_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + int exitcode = EXIT_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; + + // Default option values. + unsigned int help = 0; + const char *filename = NULL; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "hf:"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"firmware", required_argument, 0, 'f'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'f': + filename = optarg; + break; + case 'h': + help = 1; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_fwupdate); + return EXIT_SUCCESS; + } + + // Check mandatory arguments. + if (!filename) { + message ("No firmware file specified.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Update the firmware. + status = fwupdate (context, descriptor, argv[0], filename); + if (status != DC_STATUS_SUCCESS) { + message ("ERROR: %s\n", dctool_errmsg (status)); + exitcode = EXIT_FAILURE; + goto cleanup; + } + +cleanup: + return exitcode; +} + +const dctool_command_t dctool_fwupdate = { + dctool_fwupdate_run, + DCTOOL_CONFIG_DESCRIPTOR, + "fwupdate", + "Update the firmware", + "Usage:\n" + " dctool fwupdate [options]\n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" + " -f, --firmware Firmware filename\n" +#else + " -h Show help message\n" + " -f Firmware filename\n" +#endif +};