<commit_before>/*
 * libdivecomputer
 *
 * Copyright (C) 2013 <NAME>
 * Copyright (C) 2014 <NAME>
 *
 * 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 <string.h> // memcmp, memcpy
#include <stdlib.h> // malloc, free
#include <stdio.h>  // FILE, fopen

#include <libdivecomputer/hw_ostc3.h>

#include "context-private.h"
#include "device-private.h"
#include "serial.h"
#include "array.h"
#include "aes.h"

#ifdef _MSC_VER
#define snprintf _snprintf
#endif

#define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable)

#define EXITCODE(rc) \
( \
	rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \
)

#define SZ_DISPLAY    16
#define SZ_CUSTOMTEXT 60
#define SZ_VERSION    (SZ_CUSTOMTEXT + 4)
#define SZ_MEMORY     0x200000
#define SZ_CONFIG     4
#define SZ_FIRMWARE   0x01E000        // 120KB
#define SZ_FIRMWARE_BLOCK    0x1000   //   4KB
#define FIRMWARE_AREA      0x3E0000

#define RB_LOGBOOK_SIZE  256
#define RB_LOGBOOK_COUNT 256

#define S_BLOCK_READ 0x20
#define S_BLOCK_WRITE 0x30
#define S_ERASE    0x42
#define S_READY    0x4C
#define READY      0x4D
#define S_UPGRADE  0x50
#define HEADER     0x61
#define CLOCK      0x62
#define CUSTOMTEXT 0x63
#define DIVE       0x66
#define IDENTITY   0x69
#define DISPLAY    0x6E
#define READ       0x72
#define WRITE      0x77
#define RESET      0x78
#define INIT       0xBB
#define EXIT       0xFF

typedef enum hw_ostc3_state_t {
	OPEN,
	DOWNLOAD,
	SERVICE,
	REBOOTING,
} hw_ostc3_state_t;

typedef struct hw_ostc3_device_t {
	dc_device_t base;
	serial_t *port;
	unsigned char fingerprint[5];
	hw_ostc3_state_t state;
} hw_ostc3_device_t;

typedef struct hw_ostc3_firmware_t {
	unsigned char data[SZ_FIRMWARE];
	unsigned int checksum;
} hw_ostc3_firmware_t;

// This key is used both for the Ostc3 and its cousin,
// the Ostc Sport.
// The Frog uses a similar protocol, and with another key.
static const unsigned char ostc3_key[16] = {
	0xF1, 0xE9, 0xB0, 0x30,
	0x45, 0x6F, 0xBE, 0x55,
	0xFF, 0xE7, 0xF8, 0x31,
	0x13, 0x6C, 0xF2, 0xFE
};

static dc_status_t hw_ostc3_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t hw_ostc3_device_close (dc_device_t *abstract);

static const dc_device_vtable_t hw_ostc3_device_vtable = {
	DC_FAMILY_HW_OSTC3,
	hw_ostc3_device_set_fingerprint, /* set_fingerprint */
	NULL, /* read */
	NULL, /* write */
	NULL, /* dump */
	hw_ostc3_device_foreach, /* foreach */
	hw_ostc3_device_close /* close */
};


static int
hw_ostc3_strncpy (unsigned char *data, unsigned int size, const char *text)
{
	// Check the maximum length.
	size_t length = (text ? strlen (text) : 0);
	if (length > size) {
		return -1;
	}

	// Copy the text.
	if (length)
		memcpy (data, text, length);

	// Pad with spaces.
	memset (data + length, 0x20, size - length);

	return 0;
}


static dc_status_t
hw_ostc3_transfer (hw_ostc3_device_t *device,
                  dc_event_progress_t *progress,
                  unsigned char cmd,
                  const unsigned char input[],
                  unsigned int isize,
                  unsigned char output[],
                  unsigned int osize)
{
	dc_device_t *abstract = (dc_device_t *) device;

	if (device_is_cancelled (abstract))
		return DC_STATUS_CANCELLED;

	// Send the command.
	unsigned char command[1] = {cmd};
	int n = serial_write (device->port, command, sizeof (command));
	if (n != sizeof (command)) {
		ERROR (abstract->context, "Failed to send the command.");
		return EXITCODE (n);
	}

	// Read the echo.
	unsigned char echo[1] = {0};
	n = serial_read (device->port, echo, sizeof (echo));
	if (n != sizeof (echo)) {
		ERROR (abstract->context, "Failed to receive the echo.");
		return EXITCODE (n);
	}

	// Verify the echo.
	if (memcmp (echo, command, sizeof (command)) != 0) {
		ERROR (abstract->context, "Unexpected echo.");
		return DC_STATUS_PROTOCOL;
	}

	if (input) {
		// Send the input data packet.
		n = serial_write (device->port, input, isize);
		if (n != isize) {
			ERROR (abstract->context, "Failed to send the data packet.");
			return EXITCODE (n);
		}
	}

	if (output) {
		unsigned int nbytes = 0;
		while (nbytes < osize) {
			// Set the minimum packet size.
			unsigned int len = 1024;

			// Increase the packet size if more data is immediately available.
			int available = serial_get_received (device->port);
			if (available > len)
				len = available;

			// Limit the packet size to the total size.
			if (nbytes + len > osize)
				len = osize - nbytes;

			// Read the packet.
			n = serial_read (device->port, output + nbytes, len);
			if (n != len) {
				ERROR (abstract->context, "Failed to receive the answer.");
				return EXITCODE (n);
			}

			// Update and emit a progress event.
			if (progress) {
				progress->current += len;
				device_event_emit ((dc_device_t *) device, DC_EVENT_PROGRESS, progress);
			}

			nbytes += len;
		}
	}

	if (cmd != EXIT) {
		// Read the ready byte.
		unsigned char ready[1] = {0};
		unsigned char expected = (device->state == SERVICE ? S_READY : READY);
		n = serial_read (device->port, ready, sizeof (ready));
		if (n != sizeof (ready)) {
			ERROR (abstract->context, "Failed to receive the ready byte.");
			return EXITCODE (n);
		}

		// Verify the ready byte.
		if (ready[0] != expected) {
			ERROR (abstract->context, "Unexpected ready byte.");
			return DC_STATUS_PROTOCOL;
		}
	}

	return DC_STATUS_SUCCESS;
}


dc_status_t
hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name)
{
	if (out == NULL)
		return DC_STATUS_INVALIDARGS;

	// Allocate memory.
	hw_ostc3_device_t *device = (hw_ostc3_device_t *) malloc (sizeof (hw_ostc3_device_t));
	if (device == NULL) {
		ERROR (context, "Failed to allocate memory.");
		return DC_STATUS_NOMEMORY;
	}

	// Initialize the base class.
	device_init (&device->base, context, &hw_ostc3_device_vtable);

	// Set the default values.
	device->port = NULL;
	memset (device->fingerprint, 0, sizeof (device->fingerprint));

	// Open the device.
	int rc = serial_open (&device->port, context, name);<commit_msg>Detect and report unsupported commands.<commit_after>/*
 * libdivecomputer
 *
 * Copyright (C) 2013 <NAME>
 * Copyright (C) 2014 <NAME>
 *
 * 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 <string.h> // memcmp, memcpy
#include <stdlib.h> // malloc, free
#include <stdio.h>  // FILE, fopen

#include <libdivecomputer/hw_ostc3.h>

#include "context-private.h"
#include "device-private.h"
#include "serial.h"
#include "array.h"
#include "aes.h"

#ifdef _MSC_VER
#define snprintf _snprintf
#endif

#define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable)

#define EXITCODE(rc) \
( \
	rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \
)

#define SZ_DISPLAY    16
#define SZ_CUSTOMTEXT 60
#define SZ_VERSION    (SZ_CUSTOMTEXT + 4)
#define SZ_MEMORY     0x200000
#define SZ_CONFIG     4
#define SZ_FIRMWARE   0x01E000        // 120KB
#define SZ_FIRMWARE_BLOCK    0x1000   //   4KB
#define FIRMWARE_AREA      0x3E0000

#define RB_LOGBOOK_SIZE  256
#define RB_LOGBOOK_COUNT 256

#define S_BLOCK_READ 0x20
#define S_BLOCK_WRITE 0x30
#define S_ERASE    0x42
#define S_READY    0x4C
#define READY      0x4D
#define S_UPGRADE  0x50
#define HEADER     0x61
#define CLOCK      0x62
#define CUSTOMTEXT 0x63
#define DIVE       0x66
#define IDENTITY   0x69
#define DISPLAY    0x6E
#define READ       0x72
#define WRITE      0x77
#define RESET      0x78
#define INIT       0xBB
#define EXIT       0xFF

typedef enum hw_ostc3_state_t {
	OPEN,
	DOWNLOAD,
	SERVICE,
	REBOOTING,
} hw_ostc3_state_t;

typedef struct hw_ostc3_device_t {
	dc_device_t base;
	serial_t *port;
	unsigned char fingerprint[5];
	hw_ostc3_state_t state;
} hw_ostc3_device_t;

typedef struct hw_ostc3_firmware_t {
	unsigned char data[SZ_FIRMWARE];
	unsigned int checksum;
} hw_ostc3_firmware_t;

// This key is used both for the Ostc3 and its cousin,
// the Ostc Sport.
// The Frog uses a similar protocol, and with another key.
static const unsigned char ostc3_key[16] = {
	0xF1, 0xE9, 0xB0, 0x30,
	0x45, 0x6F, 0xBE, 0x55,
	0xFF, 0xE7, 0xF8, 0x31,
	0x13, 0x6C, 0xF2, 0xFE
};

static dc_status_t hw_ostc3_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t hw_ostc3_device_close (dc_device_t *abstract);

static const dc_device_vtable_t hw_ostc3_device_vtable = {
	DC_FAMILY_HW_OSTC3,
	hw_ostc3_device_set_fingerprint, /* set_fingerprint */
	NULL, /* read */
	NULL, /* write */
	NULL, /* dump */
	hw_ostc3_device_foreach, /* foreach */
	hw_ostc3_device_close /* close */
};


static int
hw_ostc3_strncpy (unsigned char *data, unsigned int size, const char *text)
{
	// Check the maximum length.
	size_t length = (text ? strlen (text) : 0);
	if (length > size) {
		return -1;
	}

	// Copy the text.
	if (length)
		memcpy (data, text, length);

	// Pad with spaces.
	memset (data + length, 0x20, size - length);

	return 0;
}


static dc_status_t
hw_ostc3_transfer (hw_ostc3_device_t *device,
                  dc_event_progress_t *progress,
                  unsigned char cmd,
                  const unsigned char input[],
                  unsigned int isize,
                  unsigned char output[],
                  unsigned int osize)
{
	dc_device_t *abstract = (dc_device_t *) device;

	if (device_is_cancelled (abstract))
		return DC_STATUS_CANCELLED;

	// Get the correct ready byte for the current state.
	const unsigned char ready = (device->state == SERVICE ? S_READY : READY);

	// Send the command.
	unsigned char command[1] = {cmd};
	int n = serial_write (device->port, command, sizeof (command));
	if (n != sizeof (command)) {
		ERROR (abstract->context, "Failed to send the command.");
		return EXITCODE (n);
	}

	// Read the echo.
	unsigned char echo[1] = {0};
	n = serial_read (device->port, echo, sizeof (echo));
	if (n != sizeof (echo)) {
		ERROR (abstract->context, "Failed to receive the echo.");
		return EXITCODE (n);
	}

	// Verify the echo.
	if (memcmp (echo, command, sizeof (command)) != 0) {
		if (echo[0] == ready) {
			ERROR (abstract->context, "Unsupported command.");
			return DC_STATUS_UNSUPPORTED;
		} else {
			ERROR (abstract->context, "Unexpected echo.");
			return DC_STATUS_PROTOCOL;
		}
	}

	if (input) {
		// Send the input data packet.
		n = serial_write (device->port, input, isize);
		if (n != isize) {
			ERROR (abstract->context, "Failed to send the data packet.");
			return EXITCODE (n);
		}
	}

	if (output) {
		unsigned int nbytes = 0;
		while (nbytes < osize) {
			// Set the minimum packet size.
			unsigned int len = 1024;

			// Increase the packet size if more data is immediately available.
			int available = serial_get_received (device->port);
			if (available > len)
				len = available;

			// Limit the packet size to the total size.
			if (nbytes + len > osize)
				len = osize - nbytes;

			// Read the packet.
			n = serial_read (device->port, output + nbytes, len);
			if (n != len) {
				ERROR (abstract->context, "Failed to receive the answer.");
				return EXITCODE (n);
			}

			// Update and emit a progress event.
			if (progress) {
				progress->current += len;
				device_event_emit ((dc_device_t *) device, DC_EVENT_PROGRESS, progress);
			}

			nbytes += len;
		}
	}

	if (cmd != EXIT) {
		// Read the ready byte.
		unsigned char answer[1] = {0};
		n = serial_read (device->port, answer, sizeof (answer));
		if (n != sizeof (answer)) {
			ERROR (abstract->context, "Failed to receive the ready byte.");
			return EXITCODE (n);
		}

		// Verify the ready byte.
		if (answer[0] != ready) {
			ERROR (abstract->context, "Unexpected ready byte.");
			return DC_STATUS_PROTOCOL;
		}
	}

	return DC_STATUS_SUCCESS;
}


dc_status_t
hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name)
{
	if (out == NULL)
		return DC_STATUS_INVALIDARGS;

	// Allocate memory.
	hw_ostc3_device_t *device = (hw_ostc3_device_t *) malloc (sizeof (hw_ostc3_device_t));
	if (device == NULL) {
		ERROR (context, "Failed to allocate memory.");
		return DC_STATUS_NOMEMORY;
	}

	// Initialize the base class.
	device_init (&device->base, context, &hw_ostc3_device_vtable);

	// Set the default values.
	device->port = NULL;
	memset (device->fingerprint, 0, sizeof (device->fingerprint));

	// Open the device.
	int rc = serial_open (&device->port, context, name);
{'fraction_non_alphanumeric': 0.1066953097028285, 'fraction_numerical': 0.027139276763336915, 'mean_word_length': 3.4964584674822925, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 62, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2588173', 'n_tokens_mistral': 5304, 'n_tokens_neox': 4849, 'n_words': 1527}