Merge with Jef's upstream libdivecomputer updates: - support new Ratio iX3M 2021 model IDs - support Mares Horizon, and fix the Mares Genius layout - add support for Shearwood Sage - various warning fixes, other minor details * 'master' of git://github.com/libdivecomputer/libdivecomputer: (23 commits) Wait before sending the firmware data Add support for the new iX3M 2021 models Avoid generating the SIGPIPE signal Use an unsigned value to represent the undefined state Use an unsigned integer for the number of dives Use the cross-platform socket file descriptor type Limit the size to INT_MAX Define DC_TIMEZONE_NONE as a signed integer Use an unsigned integer for the length Fix -Wsign-compare compiler warnings Fix -Wshadow compiler warnings Fix -Wcast-qual compiler warning Fix -Wswitch compiler warning Remove unused variables Implement the rbt sample Use some more descriptive variable names Verify the oxygen and helium percentage Add support for the Mares Horizon Swap the object major and minor version Fix the Mares Genius memory layout ...
357 lines
8.1 KiB
C
357 lines
8.1 KiB
C
/*
|
|
* libdivecomputer
|
|
*
|
|
* Copyright (C) 2012 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define NOGDI
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "context-private.h"
|
|
#include "timer.h"
|
|
|
|
struct dc_context_t {
|
|
dc_loglevel_t loglevel;
|
|
dc_logfunc_t logfunc;
|
|
void *userdata;
|
|
#ifdef ENABLE_LOGGING
|
|
char msg[16384 + 32];
|
|
dc_timer_t *timer;
|
|
#endif
|
|
};
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
/*
|
|
* A wrapper for the vsnprintf function, which will always null terminate the
|
|
* string and returns a negative value if the destination buffer is too small.
|
|
*/
|
|
static int
|
|
l_vsnprintf (char *str, size_t size, const char *format, va_list ap)
|
|
{
|
|
int n;
|
|
|
|
if (size == 0)
|
|
return -1;
|
|
|
|
#ifdef _MSC_VER
|
|
/*
|
|
* The non-standard vsnprintf implementation provided by MSVC doesn't null
|
|
* terminate the string and returns a negative value if the destination
|
|
* buffer is too small.
|
|
*/
|
|
n = _vsnprintf (str, size - 1, format, ap);
|
|
if (n == size - 1 || n < 0)
|
|
str[size - 1] = 0;
|
|
#else
|
|
/*
|
|
* The C99 vsnprintf function will always null terminate the string. If the
|
|
* destination buffer is too small, the return value is the number of
|
|
* characters that would have been written if the buffer had been large
|
|
* enough.
|
|
*/
|
|
n = vsnprintf (str, size, format, ap);
|
|
if (n >= 0 && (size_t) n >= size)
|
|
n = -1;
|
|
#endif
|
|
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
l_snprintf (char *str, size_t size, const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
int n;
|
|
|
|
va_start (ap, format);
|
|
n = l_vsnprintf (str, size, format, ap);
|
|
va_end (ap);
|
|
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
l_hexdump (char *str, size_t size, const unsigned char data[], size_t n)
|
|
{
|
|
const unsigned char ascii[] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
|
|
|
if (size == 0 || size > INT_MAX)
|
|
return -1;
|
|
|
|
/* The maximum number of bytes. */
|
|
size_t maxlength = (size - 1) / 2;
|
|
|
|
/* The actual number of bytes. */
|
|
size_t length = (n > maxlength ? maxlength : n);
|
|
|
|
for (size_t i = 0; i < length; ++i) {
|
|
/* Set the most-significant nibble. */
|
|
unsigned char msn = (data[i] >> 4) & 0x0F;
|
|
str[i * 2 + 0] = ascii[msn];
|
|
|
|
/* Set the least-significant nibble. */
|
|
unsigned char lsn = data[i] & 0x0F;
|
|
str[i * 2 + 1] = ascii[lsn];
|
|
}
|
|
|
|
/* Null terminate the hex string. */
|
|
str[length * 2] = 0;
|
|
|
|
return (n > maxlength ? -1 : (int) (length * 2));
|
|
}
|
|
|
|
static void
|
|
loghandler (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"};
|
|
|
|
dc_usecs_t now = 0;
|
|
dc_timer_now (context->timer, &now);
|
|
|
|
unsigned long seconds = now / 1000000;
|
|
unsigned long microseconds = now % 1000000;
|
|
|
|
if (loglevel == DC_LOGLEVEL_ERROR || loglevel == DC_LOGLEVEL_WARNING) {
|
|
fprintf (stderr, "[%li.%06li] %s: %s [in %s:%d (%s)]\n",
|
|
seconds, microseconds,
|
|
loglevels[loglevel], msg, file, line, function);
|
|
} else {
|
|
fprintf (stderr, "[%li.%06li] %s: %s\n",
|
|
seconds, microseconds,
|
|
loglevels[loglevel], msg);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
dc_status_t
|
|
dc_context_new (dc_context_t **out)
|
|
{
|
|
dc_context_t *context = NULL;
|
|
|
|
if (out == NULL)
|
|
return DC_STATUS_INVALIDARGS;
|
|
|
|
context = (dc_context_t *) malloc (sizeof (dc_context_t));
|
|
if (context == NULL)
|
|
return DC_STATUS_NOMEMORY;
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
context->loglevel = DC_LOGLEVEL_WARNING;
|
|
context->logfunc = loghandler;
|
|
#else
|
|
context->loglevel = DC_LOGLEVEL_NONE;
|
|
context->logfunc = NULL;
|
|
#endif
|
|
context->userdata = NULL;
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
memset (context->msg, 0, sizeof (context->msg));
|
|
context->timer = NULL;
|
|
dc_timer_new (&context->timer);
|
|
#endif
|
|
|
|
*out = context;
|
|
|
|
return DC_STATUS_SUCCESS;
|
|
}
|
|
|
|
dc_status_t
|
|
dc_context_free (dc_context_t *context)
|
|
{
|
|
if (context == NULL)
|
|
return DC_STATUS_SUCCESS;
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
dc_timer_free (context->timer);
|
|
#endif
|
|
free (context);
|
|
|
|
return DC_STATUS_SUCCESS;
|
|
}
|
|
|
|
dc_status_t
|
|
dc_context_set_loglevel (dc_context_t *context, dc_loglevel_t loglevel)
|
|
{
|
|
if (context == NULL)
|
|
return DC_STATUS_INVALIDARGS;
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
context->loglevel = loglevel;
|
|
#endif
|
|
|
|
return DC_STATUS_SUCCESS;
|
|
}
|
|
|
|
dc_status_t
|
|
dc_context_set_logfunc (dc_context_t *context, dc_logfunc_t logfunc, void *userdata)
|
|
{
|
|
if (context == NULL)
|
|
return DC_STATUS_INVALIDARGS;
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
context->logfunc = logfunc;
|
|
context->userdata = userdata;
|
|
#endif
|
|
|
|
return DC_STATUS_SUCCESS;
|
|
}
|
|
|
|
dc_status_t
|
|
dc_context_log (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *format, ...)
|
|
{
|
|
#ifdef ENABLE_LOGGING
|
|
va_list ap;
|
|
#endif
|
|
|
|
if (context == NULL)
|
|
return DC_STATUS_INVALIDARGS;
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
if (loglevel > context->loglevel)
|
|
return DC_STATUS_SUCCESS;
|
|
|
|
if (context->logfunc == NULL)
|
|
return DC_STATUS_SUCCESS;
|
|
|
|
va_start (ap, format);
|
|
l_vsnprintf (context->msg, sizeof (context->msg), format, ap);
|
|
va_end (ap);
|
|
|
|
context->logfunc (context, loglevel, file, line, function, context->msg, context->userdata);
|
|
#endif
|
|
|
|
return DC_STATUS_SUCCESS;
|
|
}
|
|
|
|
dc_status_t
|
|
dc_context_syserror (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, int errcode)
|
|
{
|
|
const char *errmsg = NULL;
|
|
|
|
#ifdef _WIN32
|
|
char buffer[256];
|
|
|
|
DWORD rc = FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, errcode, 0, buffer, sizeof (buffer), NULL);
|
|
|
|
/* Remove the CRLF and period at the end of the error message. */
|
|
while (rc > 0 && (
|
|
buffer[rc - 1] == '\n' ||
|
|
buffer[rc - 1] == '\r' ||
|
|
buffer[rc - 1] == '.'))
|
|
{
|
|
buffer[rc - 1] = '\0';
|
|
rc--;
|
|
}
|
|
|
|
if (rc > 0)
|
|
errmsg = buffer;
|
|
#elif defined (HAVE_STRERROR_R)
|
|
char buffer[256];
|
|
|
|
int rc = strerror_r (errcode, buffer, sizeof (buffer));
|
|
if (rc == 0)
|
|
errmsg = buffer;
|
|
#else
|
|
/* Fallback to the non-threadsafe function. */
|
|
errmsg = strerror (errcode);
|
|
#endif
|
|
|
|
if (errmsg == NULL)
|
|
errmsg = "Unknown system error";
|
|
|
|
return dc_context_log (context, loglevel, file, line, function, "%s (%d)", errmsg, errcode);
|
|
}
|
|
|
|
dc_status_t
|
|
dc_context_hexdump (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *prefix, const unsigned char data[], unsigned int size)
|
|
{
|
|
#ifdef ENABLE_LOGGING
|
|
int n;
|
|
#endif
|
|
|
|
if (context == NULL || prefix == NULL)
|
|
return DC_STATUS_INVALIDARGS;
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
if (loglevel > context->loglevel)
|
|
return DC_STATUS_SUCCESS;
|
|
|
|
if (context->logfunc == NULL)
|
|
return DC_STATUS_SUCCESS;
|
|
|
|
n = l_snprintf (context->msg, sizeof (context->msg), "%s: size=%u, data=", prefix, size);
|
|
|
|
if (n >= 0) {
|
|
n = l_hexdump (context->msg + n, sizeof (context->msg) - n, data, size);
|
|
}
|
|
|
|
context->logfunc (context, loglevel, file, line, function, context->msg, context->userdata);
|
|
#endif
|
|
|
|
return DC_STATUS_SUCCESS;
|
|
}
|
|
|
|
unsigned int
|
|
dc_context_get_transports (dc_context_t *context)
|
|
{
|
|
UNUSED(context);
|
|
|
|
return DC_TRANSPORT_SERIAL
|
|
#if defined(HAVE_LIBUSB)
|
|
| DC_TRANSPORT_USB
|
|
#endif
|
|
#if defined(HAVE_HIDAPI)
|
|
| DC_TRANSPORT_USBHID
|
|
#elif defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
|
| DC_TRANSPORT_USBHID
|
|
#endif
|
|
| DC_TRANSPORT_USBSTORAGE
|
|
#ifdef _WIN32
|
|
#ifdef HAVE_AF_IRDA_H
|
|
| DC_TRANSPORT_IRDA
|
|
#endif
|
|
#ifdef HAVE_WS2BTH_H
|
|
| DC_TRANSPORT_BLUETOOTH
|
|
#endif
|
|
#else /* _WIN32 */
|
|
#ifdef HAVE_LINUX_IRDA_H
|
|
| DC_TRANSPORT_IRDA
|
|
#endif
|
|
#ifdef HAVE_BLUEZ
|
|
| DC_TRANSPORT_BLUETOOTH
|
|
#endif
|
|
#endif /* _WIN32 */
|
|
;
|
|
}
|