diff --git a/configure.ac b/configure.ac
index 047b3f0..d130337 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,7 +74,7 @@ AC_MSG_RESULT([$os_win32])
 AM_CONDITIONAL([OS_WIN32], [test "$os_win32" = "yes"])
 
 # Checks for USB support.
-PKG_CHECK_MODULES([LIBUSB], [libusb-1.0], [have_libusb=yes], [have_libusb=no])
+#PKG_CHECK_MODULES([LIBUSB], [libusb-1.0], [have_libusb=yes], [have_libusb=no])
 if test "$have_libusb" = "yes"; then
 	AC_DEFINE([HAVE_LIBUSB], [1], [libusb support])
 	AC_SUBST([DEPENDENCIES], [libusb-1.0])
diff --git a/examples/Makefile.am b/examples/Makefile.am
index d77db45..3037ff5 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,13 +1,16 @@
-AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
+AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/src
 LDADD = $(top_builddir)/src/libdivecomputer.la
 
 bin_PROGRAMS = \
+	simulator \
 	universal \
 	ostc-fwupdate
 
 COMMON = common.c common.h \
 	utils.c utils.h
 
+simulator_SOURCES = simulation.c $(COMMON)
+
 universal_SOURCES = universal.c $(COMMON)
 
 ostc_fwupdate_SOURCES = hw_ostc_fwupdate.c $(COMMON)
diff --git a/examples/simulation.c b/examples/simulation.c
new file mode 100644
index 0000000..e6126b9
--- /dev/null
+++ b/examples/simulation.c
@@ -0,0 +1,3922 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <time.h>
+#include <assert.h>
+
+#include <unistd.h>
+
+#include <libdivecomputer/suunto.h>
+#include <libdivecomputer/reefnet.h>
+#include <libdivecomputer/uwatec.h>
+#include <libdivecomputer/oceanic.h>
+#include <libdivecomputer/mares.h>
+#include <libdivecomputer/cressi.h>
+#include <libdivecomputer/zeagle.h>
+
+#include <libdivecomputer/buffer.h>
+
+#include "checksum.h"
+#include "array.h"
+#include "utils.h"
+#include "serial.h"
+#include "irda.h"
+
+#define SUUNTO_EON_MEMORY_SIZE 0x900
+#define REEFNET_SENSUSULTRA_MEMORY_DATA_SIZE 2080768
+
+#define MARES_NEMO_MEMORY_SIZE 0x4000
+#define MARES_NEMO_PACKET_SIZE 0x20
+
+#define NAK 0xA8
+#define ACK 0x60
+
+#define USEBREAK 1
+
+#define MAXSIZE (2 * 1024 * 1024)
+
+#define DC_FAMILY_DIVESYSTEM_IDIVE (DC_FAMILY_CITIZEN_AQUALAND + 1)
+#define DC_FAMILY_COCHRAN_COMMANDER (DC_FAMILY_CITIZEN_AQUALAND + 2)
+
+static int
+error (double percentage)
+{
+	double random = rand () / (double) RAND_MAX;
+
+	if (random <= percentage)
+		return 1;
+	else
+		return 0;
+}
+
+static int
+myrandom (int n)
+{
+	return rand() / (RAND_MAX / n + 1);
+}
+/*
+static unsigned int
+myerror (double percentage, unsigned int n)
+{
+	double pct = rand() / (double) RAND_MAX;
+
+	if (pct > percentage)
+		return 0;
+
+	return rand() / (RAND_MAX / n + 1);
+}
+*/
+
+static void
+debug (const unsigned char data[], unsigned int size)
+{
+	message ("size=%u, data=", size);
+	for (unsigned int i = 0; i < size; ++i)
+		message ("%02X ", data[i]);
+	message ("\n");
+}
+
+static unsigned int
+advance (unsigned int position, int offset, int is_spyder)
+{
+	const unsigned int begin = (is_spyder ? 0x4C : 0x71), end = 0x2000;
+
+	int current = position + offset;
+	if (current >= end)
+		return begin + (current - end);
+	if (current < begin)
+		return end - (begin - current);
+
+	return current;
+}
+
+static void
+send_package (serial_t *device, const unsigned char data[], unsigned int offset, unsigned int len, unsigned char command, int is_spyder)
+{
+	unsigned char checksum = 0x00;
+	checksum = checksum_xor_uint8 ((unsigned char*)&command, 1, checksum);
+	checksum = checksum_xor_uint8 ((unsigned char*)&len, 1, checksum);
+
+	serial_write (device, &command, 1);
+	serial_write (device, &len, 1);
+
+	unsigned int current = offset;
+	for (unsigned int i = 0; i < len; ++i) {
+		serial_write (device, data + current, 1);
+		checksum = checksum_xor_uint8 (data + current, 1, checksum);
+		if (current <= (is_spyder ? 0x4C : 0x71))
+			current = 0x2000;
+		current--;
+	}
+	serial_write (device, &checksum, 1);
+
+	message ("package: begin=0x%04x, end=0x%04x, len=%i\n", offset, current, len);
+}
+
+static unsigned int
+send_next_dive (serial_t *device, const unsigned char data[], unsigned int offset, unsigned char command, int is_spyder)
+{
+	unsigned int previous = offset;
+	unsigned int current = offset;
+	for (unsigned int i = 0; ; ++i) {
+		if (data[current] == 0x82) {
+			send_package (device, data, previous, 0, command, is_spyder);
+			return previous;
+		}
+		unsigned int peek = advance (current, (is_spyder ? -2 : -4), is_spyder);
+		if (i > 0 && data[peek] == 0x80) {
+			unsigned int len = i % 32;
+			if (len)
+				send_package (device, data, previous, len, command, is_spyder);
+			return current;
+		}
+
+		if (current <= (is_spyder ? 0x4C : 0x71))
+			current = 0x2000;
+		current--;
+
+		if ((i + 1) % 32 == 0) {
+			send_package (device, data, previous, 32, command, is_spyder);
+			previous = current;
+		}
+	}
+}
+
+
+static void
+simulation_raw (serial_t *device, unsigned char data[], unsigned int size)
+{
+	//serial_write (device, data, size);
+	for (unsigned int i = 0; i < size; ++i)
+		serial_write (device, data + i, 1);
+}
+
+
+static void
+simulation_nemo (serial_t *device, unsigned char data[], unsigned int size)
+{
+	unsigned char garbage = 0x11;
+	unsigned char header = 0xEE;
+
+	for (unsigned int i = 0; i < 0x058c; ++i)
+		serial_write (device, &garbage, 1);
+
+	for (unsigned int i = 0; i < 20; ++i)
+		serial_write (device, &header, 1);
+
+	unsigned int nbytes = 0;
+	while (nbytes < MARES_NEMO_MEMORY_SIZE) {
+		unsigned char crc = checksum_add_uint8 (data + nbytes, MARES_NEMO_PACKET_SIZE, 0x00);
+
+		for (unsigned int i = 0; i < 2; ++i) {
+			serial_write (device, data + nbytes, MARES_NEMO_PACKET_SIZE);
+			serial_write (device, &crc, 1);
+			#ifdef _WIN32
+				serial_sleep (device, 10); /* Mares IRIS needs this. */
+			#endif
+		}
+
+		nbytes += MARES_NEMO_PACKET_SIZE;
+	}
+}
+
+
+static void
+mares_puck_convert_binary_to_ascii (const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize)
+{
+	assert (osize == 2 * isize);
+
+	const unsigned char ascii[] = {
+		'0', '1', '2', '3', '4', '5', '6', '7',
+		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+	for (unsigned int i = 0; i < isize; ++i) {
+		// Set the most-significant nibble.
+		unsigned char msn = (input[i] >> 4) & 0x0F;
+		output[i * 2 + 0] = ascii[msn];
+
+		// Set the least-significant nibble.
+		unsigned char lsn = input[i] & 0x0F;
+		output[i * 2 + 1] = ascii[lsn];
+
+		unsigned char value = input[i];
+		for (unsigned int j = 0; j < 2; ++j) {
+			unsigned int n = (1 - j) * 4;
+			unsigned char x = (value >> n) & 0x0F;
+			output[i * 2 + j] = ascii[x];
+		}
+	}
+}
+
+static void
+mares_puck_convert_ascii_to_binary (const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize)
+{
+	assert (isize == 2 * osize);
+
+	for (unsigned int i = 0; i < osize; ++i) {
+		unsigned char value = 0;
+		for (unsigned int j = 0; j < 2; ++j) {
+			unsigned char number = 0;
+			unsigned char ascii = input[i * 2 + j];
+			if (ascii >= '0' && ascii <= '9')
+				number = ascii - '0';
+			else if (ascii >= 'A' && ascii <= 'F')
+				number = 10 + ascii - 'A';
+			else if (ascii >= 'a' && ascii <= 'f')
+				number = 10 + ascii - 'a';
+			else
+				message ("Invalid charachter.");
+
+			value <<= 4;
+			value += number;
+		}
+		output[i] = value;
+	}
+}
+
+#define DACOR
+
+static void
+simulation_puck (serial_t *device, unsigned char data[], unsigned int size, int echo)
+{
+	const unsigned char header = '<', trailer = '>';
+	unsigned char ascii[12] = {0}, raw[5] = {0};
+#ifdef DACOR
+	unsigned char packet[2 * 64 + 2] = {0};
+#else
+	unsigned char packet[2 * 32 + 2] = {0};
+#endif
+	unsigned char checksum;
+
+	for (;;) {
+		serial_read (device, ascii, sizeof (ascii));
+		mares_puck_convert_ascii_to_binary (ascii + 1, sizeof (ascii) - 2, raw, sizeof (raw));
+		unsigned int command = raw[0];
+		unsigned int address = raw[1] + (raw[2] << 8);
+		unsigned int length = raw[3];
+		switch (command) {
+		case 0x51:
+			message ("read_memory(%04x,%i)\n", address, length);
+			#ifdef _WIN32
+				serial_sleep (device, 10); /* Mares DRAK needs this. */
+			#endif
+			if (echo) {
+				serial_write (device, ascii, sizeof (ascii)); /* Dacor needs an echo */
+			}
+			serial_write (device, &header, 1);
+			mares_puck_convert_binary_to_ascii (data + address, length, packet, 2 * length);
+			checksum = checksum_add_uint8 (packet, 2 * length, 0x00);
+			mares_puck_convert_binary_to_ascii (&checksum, 1, packet + 2 * length, 2);
+			serial_write (device, packet, 2 * length + 2);
+			serial_write (device, &trailer, 1);
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command);
+			break;
+		}
+	}
+}
+
+static void
+simulation_iconhd (serial_t *device, unsigned char data[], unsigned int size, const unsigned char version[])
+{
+	unsigned char command[8] = {0};//, checksum = 0;
+	unsigned int csize = 0;//, len = 0, address = 0;
+	//unsigned int offset = 0, ndives = 0;
+
+	const unsigned char eof[] = {0xEA};
+	const unsigned char ack[] = {0xAA};
+
+	unsigned int address = 0, length = 0;
+
+	const unsigned char id[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x63,
+	0x6F, 0x6E, 0x20, 0x48, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x30, 0x30, 0x2E, 0x31, 0x30, 0x2E, 0x31, 0x31, 0x00, 0x0A,
+	0x0B, 0x00, 0x31, 0x31, 0x2D, 0x31, 0x32, 0x2D, 0x30, 0x39, 0x2D, 0x8E,
+	0x49, 0x63, 0x6F, 0x6E, 0x20, 0x48, 0x44, 0x20, 0x28, 0x43, 0x29, 0x20,
+	0x4D, 0x61, 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 140 bytes
+
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 0xC2: // C2 67
+			csize = 2;
+			serial_read (device, command + 1, csize - 1);
+			message ("%02X %02X\n", command[0], command[1]);
+			serial_write (device, ack, sizeof (ack));
+			if (version)
+				serial_write (device, version, sizeof (id));
+			else
+				serial_write (device, id, sizeof (id));
+			serial_write (device, eof, sizeof (eof));
+			break;
+		case 0xE7: // E7 42 + 8 byte COMMAND
+			csize = 2;
+			serial_read (device, command + 1, csize - 1);
+			message ("%02X %02X\n", command[0], command[1]);
+			serial_write (device, ack, sizeof (ack));
+			serial_read (device, command, 8);
+			message ("%02X %02X %02X %02X\n%02X %02X %02X %02X\n",
+				command[0], command[1], command[2], command[3],
+				command[4], command[5], command[6], command[7]);
+
+			address = command[0] + (command[1] << 8) + (command[2] << 16) + (command[3] << 24);
+			length  = command[4] + (command[5] << 8) + (command[6] << 16) + (command[7] << 24);
+			message ("read_memory(0x%08X,0x%08X)\n", address, length);
+
+			{
+				//serial_write (device, data, size);
+				unsigned int nbytes = 0;
+				while (nbytes < length) {
+					unsigned int len = 1024;
+					if (nbytes + len > length)
+						len = length - nbytes;
+					message ("nbytes=%u, len=%u\n", nbytes, len);
+					serial_write (device, data + address + nbytes, len);
+#ifdef _WIN32
+					serial_sleep (device, 10); // For Mares DiveOrganizer
+#endif
+					nbytes += len;
+				}
+				serial_write (device, eof, sizeof (eof));
+			}
+			break;
+		default:
+			message ("Unknown command 0x%02X\n", command[0]);
+			break;
+		}
+	}
+}
+
+static void
+aladin_set_clock (unsigned char data[], long clock_device, long clock_host)
+{
+	// Clock calibration.
+	time_t now = time (NULL);
+	long clock = clock_device + (now - clock_host) * 2;
+	data[0x7fb + 4] = (clock      ) & 0xFF;
+	data[0x7fa + 4] = (clock >>  8) & 0xFF;
+	data[0x7f9 + 4] = (clock >> 16) & 0xFF;
+	data[0x7f8 + 4] = (clock >> 24) & 0xFF;
+
+	message ("clock_host=%lu, now=%lu\n", clock_host, now);
+	message ("clock_device=%lu, clock=%lu\n", clock_device, clock);
+
+	message ("checksum=%02X %02X\n", data[2048], data[2049]);
+
+	// Checksum
+	unsigned short checksum = checksum_add_uint16 (data, 2048, 0x0000);
+	data[2048] = (checksum     ) & 0xFF;
+	data[2049] = (checksum >> 8) & 0xFF;
+
+	message ("checksum=%02X %02X\n", data[2048], data[2049]);
+}
+
+
+static void
+simulation_aladin (serial_t *device, unsigned char data[], unsigned int size, long clock_device, long clock_host)
+{
+	const unsigned char header_original[4] = {0xAA, 0xAA, 0xAA, 0x00};
+	const unsigned char header_reversed[4] = {'U', 'U', 'U', 0};
+	const unsigned char garbage[4] = {0xFF, 0xFF, 0xFF, 0xFF};
+	const unsigned char divinglog[2] = {0xFF, 0x00};
+
+	unsigned char buffer[2050] = {0};
+	memcpy (buffer, data, size);
+
+	if (memcmp (data, header_reversed, 4) == 0) {
+		message ("Header: reversed\n");
+		array_reverse_bits (buffer, size);
+	} else if (memcmp (data, header_original, 4) == 0) {
+		message ("Header: original\n");
+	} else {
+		message ("Header: unknown\n");
+	}
+
+	// Use the device timestamp that is stored in the data.
+	/*clock_device =
+		(data[0x7f8 + 4] << 24) +
+		(data[0x7f9 + 4] << 16) +
+		(data[0x7fa + 4] << 8) +
+		 data[0x7fb + 4];*/
+
+	if (clock_device == 0)
+		clock_host = 757382400; // 1994-01-01 00:00:00 UTC
+
+	aladin_set_clock (buffer, clock_device, clock_host);
+
+	/*unsigned short crc = checksum_add_uint16 (buffer, size, 0x0000);
+	memcpy (buffer + 2048, &crc, sizeof (crc));*/
+
+	array_reverse_bits (buffer, sizeof (buffer));
+
+	// Send some garbage
+	for (unsigned int i = 0; i < 200; ++i) {
+		serial_write (device, garbage, 1);
+		serial_sleep (device, 10);
+	}
+
+#if 1
+	serial_write (device, buffer, sizeof (buffer));
+#else
+	for (unsigned int i = 0; i < sizeof (buffer); ++i) {
+		serial_write (device, buffer + i, 1);
+		if ((i % 128) == 0)
+			serial_sleep (device, 1);
+	}
+#endif
+
+	// Diving Log needs this.
+	serial_write (device, divinglog, sizeof (divinglog));
+
+	// Garbage
+	serial_write (device, garbage, sizeof (garbage));
+}
+
+
+static int
+test_memomouse (const unsigned char *data, unsigned int size, const unsigned char *fdata, unsigned int fsize, void *userdata)
+{
+	memcpy (userdata, data + 11, 4);
+
+	return 0;
+}
+
+
+static void
+memomouse_set_clock (unsigned char data[], unsigned int size, long clock_device, long clock_host)
+{
+	// Clock calibration.
+	time_t now = time (NULL);
+	long clock = clock_device + (now - clock_host) * 2;
+	data[2 + 1] = (clock      ) & 0xFF;
+	data[2 + 2] = (clock >>  8) & 0xFF;
+	data[2 + 3] = (clock >> 16) & 0xFF;
+	data[2 + 4] = (clock >> 24) & 0xFF;
+
+	message ("clock_host=%lu, now=%lu\n", clock_host, now);
+	message ("clock_device=%lu, clock=%lu\n", clock_device, clock);
+
+	// Checksum
+	data[size + 2] = checksum_xor_uint8 (data, size + 2, 0x00);
+}
+
+
+typedef struct {
+	unsigned int timestamp;
+	unsigned int skip, length;
+} memomouse_data_t;
+
+
+static int
+test_memomouse_length (const unsigned char *data, unsigned int size, const unsigned char *fdata, unsigned int fsize, void *userdata)
+{
+	//memcpy (userdata, data + 11, 4);
+
+	memomouse_data_t *x = (memomouse_data_t *) userdata;
+
+	unsigned int timestamp = data[11] + (data[12] <<  8) +
+		(data[13] << 16) + (data[14] << 24);
+
+	if (timestamp > x->timestamp)
+		x->length += size;
+	else
+		x->skip += size;
+
+	return 1;
+}
+
+
+static void
+simulation_memomouse (serial_t *device, unsigned char data[], unsigned int size, long clock_device, long clock_host)
+{
+	const unsigned char ack = ACK;//, nak = NAK, abc = 0x40;
+	const unsigned char id[] = {0x50, 0xE0, 0x00, 0x92, 0x62, 0x6A, 0x8C, 0x74, 0x0C, 0x0C, 0x82, 0x50};
+	//const unsigned char id_corrupt[] = {0x50, 0xE0, 0x00, 0x0A, 0x62, 0x6A, 0x8C, 0x74, 0x0C, 0x0C, 0x82, 0x1A};
+
+	// Use the device timestamp that is stored in the data.
+	//clock_device = data[1] + (data[2] << 8) + (data[3] << 16) + (data[4] << 24);
+
+	if (clock_device == 0)
+		clock_host = 757382400; // 1994-01-01 00:00:00 UTC
+
+	unsigned char timestamp[4] = {0};
+	uwatec_memomouse_extract_dives (NULL, data, size, test_memomouse, &timestamp);
+
+	unsigned char command[9] = {0};
+	unsigned char packet[128] = {0};
+
+	unsigned char *package = malloc (size + 3);
+	if (package == NULL)
+		return;
+
+	package[0] = (size     ) & 0xFF;
+	package[1] = (size >> 8) & 0xFF;
+	memcpy (package + 2, data, size);
+	memcpy (package + 2 + 1, timestamp, 4);
+	package[size + 2] = checksum_xor_uint8 (package, size + 2, 0x00);
+
+	serial_flush (device, SERIAL_QUEUE_BOTH);
+
+for (;;) {
+start:
+	message ("Stage 1 (NAK)\n");
+	for (;;) {
+		serial_read (device, command, 1); // NAK
+		unsigned char abc = command[0];
+		array_reverse_bits (&abc, 1);
+		message ("reply=%02x %02x\n", command[0], abc);
+		if (command[0] == NAK)
+			break;
+	}
+
+	message ("Stage 2 (ID)\n");
+	for (;;) {
+		serial_write (device, id, sizeof (id)); // ID
+		serial_read (device, command, 1); // ACK
+		unsigned char abc = command[0];
+		array_reverse_bits (&abc, 1);
+		message ("reply=%02x %02x\n", command[0], abc);
+		if (command[0] == ACK)
+			break;
+	}
+
+	message ("Stage 3 (CMD)\n");
+	serial_read (device, command, sizeof (command)); // CMD
+	serial_write (device, &ack, 1); // ACK
+
+	array_reverse_bits (command, sizeof (command));
+	unsigned int xtimestamp = command[4] + (command[5] <<  8) +
+		(command[6] << 16) + (command[7] << 24);
+	message ("timestamp=%u\n", xtimestamp);
+
+#if 1 // Timestamp code
+
+	memomouse_data_t x = {0, 0, 0};
+	x.timestamp = xtimestamp;
+	uwatec_memomouse_extract_dives (NULL, data, size, test_memomouse_length, &x);
+	message ("skip=%u, length=%u\n", x.skip, x.length);
+
+	size = x.length + 5;
+	package[0] = (size     ) & 0xFF;
+	package[1] = (size >> 8) & 0xFF;
+	memcpy (package + 2, data, 5);
+	memcpy (package + 2 + 1, timestamp, 4);
+	memcpy (package + 2 + 5, data + 5 + x.skip, x.length);
+	package[size + 2] = checksum_xor_uint8 (package, size + 2, 0x00);
+
+#endif
+
+	const unsigned int delay = 5000, step = 100;
+	for (unsigned int i = 0; i < (delay / step); ++i) {
+		int dsr = serial_get_line (device, SERIAL_LINE_DSR);
+		int cts = serial_get_line (device, SERIAL_LINE_CTS);
+
+		message ("dsr=%i, cts=%i\n", dsr, cts);
+		if (dsr == 0) goto start;
+
+		serial_sleep (device, step);
+	}
+
+	//serial_sleep (device, delay);
+
+	memomouse_set_clock (package, size, clock_device, clock_host);
+
+	message ("Stage 4 (DATA)\n");
+/*for (;;) {
+	serial_write (device, id, sizeof (id)); // ID
+	serial_read (device, command, 1); // ACK
+	message ("reply=%02x\n", command[0]);
+	if (command[0] == ACK)
+		break;
+}*/
+
+	// TODO: Send package.
+#if 1
+	unsigned int i = 0;
+	unsigned int offset = 0;
+	while (offset < size + 3) {
+	/*unsigned int i = 0;
+	for (;;) {
+		unsigned int offset = i * 126;*/
+		unsigned int len = 126;
+		if (offset + len > size + 3)
+			len = size + 3 - offset;
+		packet[0] = (unsigned char) len;
+		memcpy (packet + 1, package + offset, len);
+		packet[len + 1] = checksum_xor_uint8 (packet, len + 1, 0x00);
+		array_reverse_bits (packet, len + 2);
+		message ("packet(%u,%u)\n", i, len);
+		serial_write (device, packet, len + 2);
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case NAK:
+			message ("NAK\n");
+			// TODO: Send package again.
+			break;
+		case ACK:
+			message ("ACK\n");
+			// TODO: Send next package.
+			offset += len;
+			i++;
+			break;
+		default:
+			message ("Unknown command %02x\n", command[0]);
+			break;
+		}
+	}
+#endif
+	serial_sleep (device, 5000);
+}
+}
+
+static void
+sensus_set_clock (unsigned char handshake[], long clock_device, long clock_host)
+{
+	// Clock calibration.
+	time_t now = time (NULL);
+	long clock = clock_device + (now - clock_host);
+	handshake[8] = (clock      ) & 0xFF;
+	handshake[9] = (clock >>  8) & 0xFF;
+	handshake[10] = (clock >> 16) & 0xFF;
+	handshake[11] = (clock >> 24) & 0xFF;
+}
+
+
+static void
+simulation_sensus (serial_t *device, unsigned char data[], unsigned int size, long clock_device, long clock_host)
+{
+	unsigned char handshake[12] = {'O', 'K', '1', '1', 16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	unsigned char command[1] = {0};
+	unsigned short checksum = checksum_add_uint16 (data, size, 0x00);
+
+	assert (size == 32768);
+
+	int active = 0;
+
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 0x0A:
+			message ("handshake()\n");
+			sensus_set_clock (handshake, clock_device, clock_host);
+			serial_write (device, handshake, sizeof (handshake));
+			active = 1;
+			break;
+		case 0x00:
+			if (active) {
+				message ("cancel()\n");
+				active = 0;
+			} else {
+				message ("handshake()\n");
+				sensus_set_clock (handshake, clock_device, clock_host);
+				serial_write (device, handshake, sizeof (handshake));
+				active = 1;
+			}
+			break;
+		case 0x40:
+			message ("dump()\n");
+			serial_write (device, "DATA", 4);
+			for (unsigned int i = 0; i < 32768; i += 512) {
+				serial_write (device, data + i, 512);
+				serial_sleep (device, 10);
+			}
+			//serial_write (device, data, size);
+			serial_write (device, &checksum, sizeof (checksum));
+			serial_write (device, "END", 3);
+			active = 0;
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+
+static void
+sensuspro_set_clock (unsigned char handshake[], long clock_device, long clock_host)
+{
+	// Clock calibration.
+	time_t now = time (NULL);
+	long clock = clock_device + (now - clock_host);
+	handshake[6] = (clock      ) & 0xFF;
+	handshake[7] = (clock >>  8) & 0xFF;
+	handshake[8] = (clock >> 16) & 0xFF;
+	handshake[9] = (clock >> 24) & 0xFF;
+
+	// Checksum
+	unsigned short checksum = checksum_crc_ccitt_uint16 (handshake, 10);
+	handshake[10] = (checksum     ) & 0xFF;
+	handshake[11] = (checksum >> 8) & 0xFF;
+}
+
+static void
+simulation_sensuspro (serial_t *device, unsigned char data[], unsigned int size, unsigned char version[], long clock_device, long clock_host)
+{
+	//unsigned short crc = checksum_crc_ccitt_uint16 (data, size - 2);
+
+	//unsigned short checksum = 0;
+	unsigned char command[1] = {0};
+	unsigned char handshake[] = {0x02, 0xFF, 255, 10, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	/*unsigned short checksum = checksum_crc_ccitt_uint16 (handshake, sizeof (handshake) - 2);
+	handshake[sizeof (handshake) - 2] = (checksum     ) & 0xFF;
+	handshake[sizeof (handshake) - 1] = (checksum >> 8) & 0xFF;*/
+
+	if (version)
+		memcpy (handshake, version, sizeof (handshake) - 2);
+
+	serial_set_timeout (device, 1000);
+	serial_flush (device, SERIAL_QUEUE_BOTH);
+
+	unsigned int i = 0;
+	for (;;) {
+#if !defined (_WIN32) || !USEBREAK
+		serial_sleep (device, 1000);
+#endif
+		sensuspro_set_clock (handshake, clock_device, clock_host);
+#if defined (_WIN32) && USEBREAK
+		serial_wait_break (device);
+#endif
+		message ("handshake ()\n");
+		serial_write (device, handshake, sizeof (handshake));
+		int rc = serial_read (device, command, 1);
+		/*while (rc == 1 && command[0] == 0x00) {
+			message ("break ()\n");
+			rc = serial_read (device, command, 1);
+		}*/
+		if (rc != 1)
+			continue;
+		switch (command[0]) {
+		case 0xB4:
+			message ("dump ()\n");
+			for (i = 0; i < (size - 2); i += 512) {
+				message ("i=%u\n", i);
+				serial_write (device, data + i, 512);
+				//serial_sleep (device, 10);
+			}
+			message ("last=%u\n", i);
+			serial_write (device, data + size - 2, 2);
+			//serial_write (device, data, size);
+			break;
+		case 0xB5:
+			serial_read (device, command, 1);
+			message ("write_interval (%u)\n", command[0]);
+			handshake[3] = command[0];
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+
+static void
+sensusultra_set_clock (unsigned char handshake[], long devtime, long systime)
+{
+	// Clock calibration.
+	time_t now = time (NULL);
+	long clock = devtime + (now - systime);
+	handshake[4] = (clock      ) & 0xFF;
+	handshake[5] = (clock >>  8) & 0xFF;
+	handshake[6] = (clock >> 16) & 0xFF;
+	handshake[7] = (clock >> 24) & 0xFF;
+
+	// Checksum
+	unsigned short checksum = checksum_crc_ccitt_uint16 (handshake, 26 - 2);
+	handshake[26 - 2] = (checksum     ) & 0xFF;
+	handshake[26 - 1] = (checksum >> 8) & 0xFF;
+}
+
+
+static int
+sensusultra_read (serial_t *device, unsigned char data[], unsigned int size)
+{
+	const unsigned char prompt[] = {0xA5};
+
+	for (unsigned int i = 0; i < size; ++i) {
+		serial_write (device, prompt, sizeof (prompt));
+		int rc = serial_read (device, data + i, 1);
+		if (rc != 1)
+			return i;
+	}
+
+	return size;
+}
+
+static void
+simulation_sensusultra (serial_t *device, unsigned char data[], unsigned int size, unsigned char version[], long clock_device, long clock_host)
+{
+	int rc = 0;
+	unsigned int offset = 0;
+	unsigned char prompt[] = {0xA5};
+	unsigned char command[2] = {0};
+	unsigned char handshake[26] = {
+			0x01, 0x03, // VERSION
+			0x00, 0x01, // SERIAL
+			//0xFF, 0xFF, // SERIAL
+			0x00, 0x00, 0x00, 0x00, // TIME
+			//devtime & 0xFF, (devtime >> 8) & 0xFF, (devtime >> 16) & 0xFF, (devtime >> 24) & 0xFF,
+			0x01, 0x00, // BOOT_COUNT (1)
+			0x00, 0x00, 0x00, 0x00, // BOOT_TIME (0)
+			0x00, 0x01, // DIVE_COUNT
+			0x0a, 0x00, // INTERVAL (10)
+			0x57, 0x04, // THRESHOLD (1111)
+			0x0F, 0x00, // ENDCOUNT (15)
+			0x01, 0x00, // AVERAGING (1)
+			0x00, 0x00}; // CRC
+	unsigned char sense[8] = {
+			0x00, 0x00, // VOLTAGE
+			0x00, 0x00, // TEMPERATURE
+			0x00, 0x00, // PRESSURE
+			0x00, 0x00}; // CRC
+
+	if (version) {
+		memcpy (handshake, version, sizeof (handshake) - 2);
+		clock_device = array_uint32_le (version + 4);
+	}
+
+	unsigned short checksum = checksum_crc_ccitt_uint16 (sense, sizeof (sense) - 2);
+	sense[sizeof (sense) - 2] = (checksum     ) & 0xFF;
+	sense[sizeof (sense) - 1] = (checksum >> 8) & 0xFF;
+
+	serial_flush (device, SERIAL_QUEUE_BOTH);
+	serial_set_timeout (device, 750); // FIXME: 100ms
+
+	for (;;) {
+		serial_sleep (device, 1000);
+		serial_flush (device, SERIAL_QUEUE_BOTH);
+		message ("handshake()\n");
+		sensusultra_set_clock (handshake, clock_device, clock_host);
+		serial_write (device, handshake, sizeof (handshake));
+		rc = sensusultra_read (device, command, 2);
+		if (rc != 2)
+			continue;
+		unsigned short code = command[0] + (command[1] << 8);
+		unsigned short page = 0, crc = 0, value = 0/*, count = 0*/;
+		switch (code) {
+		case 0xB420:
+			message ("read_user() NOT IMPLEMENTED\n");
+			break;
+		case 0xB421:
+			message ("read_data()\n");
+			page = 0;
+			while (page < 4064) {
+			//for (unsigned int i = 0; i < 4064; ++i) {
+				//serial_sleep (device, 100);
+				message ("page=%u\n", page);
+				crc = checksum_crc_ccitt_uint16 (data + size - 512 * (page + 1), 512);
+				serial_write (device, &page, sizeof (page));
+				serial_write (device, data + size - 512 * (page + 1), 512);
+				serial_write (device, &crc, sizeof (crc));
+				serial_write (device, prompt, sizeof (prompt));
+				rc = serial_read (device, command, 1);
+				if (rc != 1) {
+					// Timeout
+					message ("response: timeout\n");
+					break;
+				}
+				if (command[0] == prompt[0]) {
+					// Accept
+					message ("response: accept\n");
+					page++;
+				} else if (command[0] == 0x00) {
+					// Reject
+					message ("response: reject\n");
+				} else {
+					// Unknown
+					message ("response: unkown (0x%02x)\n", command[0]);
+					break;
+				}
+			}
+			break;
+		case 0xB430:
+			message ("write_user() NOT IMPLEMENTED\n");
+			break;
+		case 0xB440:
+			message ("sense()\n");
+			serial_write (device, sense, sizeof (sense));
+			break;
+		case 0xB410:
+		case 0xB411:
+		case 0xB412:
+		case 0xB413:
+			message ("set_parameter()\n");
+			rc = sensusultra_read (device, command, 2);
+			if (rc != 2)
+				continue;
+			value = command[0] + (command[1] << 8);
+			message ("parameter: value=%i\n", value);
+			offset = 0x10 + 2 * (code - 0xB410);
+			handshake[offset + 0] = (value     ) & 0xFF;
+			handshake[offset + 1] = (value >> 8) & 0xFF;
+			break;
+		default:
+			message ("Unknown command 0x%04x\n", code);
+			break;
+		}
+	}
+}
+/*
+static void
+v2c2_read_command (serial_t *device)
+{
+	unsigned char command[128] = {0}, checksum = 0;
+
+	for (;;) {
+		serial_set_timeout (device, -1);
+
+		// Read packet: CMD N(2) DATA(N) CRC
+		int n = serial_read (device, command + 0, 3);
+		if (n != 3) {
+			message ("Ouch!\n");
+			continue;
+		}
+
+		unsigned int ndata = (command[1] << 8) + command[2];
+		unsigned int csize = 3 + ndata + 1;
+		message ("ndata=%u\n", ndata);
+
+		serial_set_timeout (device, 1000);
+
+		n = serial_read (device, command + 3, ndata + 1);
+		if (n != ndata + 1) {
+			message ("Ouch!\n");
+			continue;
+		}
+
+		checksum = checksum_xor_uint8 (command, csize - 1, 0x00);
+		if (command[csize - 1] != checksum) {
+			message ("Ouch!\n");
+			continue;
+		}
+
+		message ("command=");
+		for (unsigned int i = 0; i < csize; ++i) {
+			message ("%02x ", command[i]);
+		}
+		message ("\n");
+
+	}
+}
+*/
+
+static void
+simulation_vyper2_or_d9 (serial_t *device, unsigned char data[], unsigned int size, unsigned char id[], int vyper2)
+{
+	unsigned char command[128] = {0}, checksum = 0;
+	unsigned int csize = 0, len = 0, address = 0, nparams = 0;
+	unsigned int isversion = 0;
+
+	unsigned char version[8] = {0x0F, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00};
+	memcpy (version + 3, id, 4);
+	version[7] = checksum_xor_uint8 (version, 7, 0x00);
+
+	for (;;) {
+		//v2c2_read_command (device);
+
+		serial_read (device, command, 1);
+		/*if (!isversion && command[0] != 0x0F)
+			goto unknown;*/
+		switch (command[0]) {
+		case 0x0F:
+			csize = 4;
+			if (vyper2) {
+				serial_read (device, command + 1, csize - 1);
+			} else {
+				serial_write (device, command, 1);
+				for (unsigned int i = 1; i < csize; ++i) {
+					serial_read (device, command + i, 1);
+					serial_write (device, command + i, 1);
+				}
+			}
+			message ("version()\n");
+			serial_write (device, version, sizeof (version));
+			isversion = 1;
+			break;
+		case 0x05:
+			csize = 7;
+			if (vyper2) {
+				serial_read (device, command + 1, csize - 1);
+			} else {
+				serial_write (device, command, 1);
+				for (unsigned int i = 1; i < csize; ++i) {
+					serial_read (device, command + i, 1);
+					serial_write (device, command + i, 1);
+				}
+			}
+			nparams = (command[1] << 8) + command[2];
+			address = (command[3] << 8) + command[4];
+			len = command[5];
+			message ("read_memory(%04x,%i)\n", address, len);
+			if (len < 8) {
+				message ("Too small!\n");
+				break;
+			}
+			command[2] += len;
+			checksum = 0x00;
+			checksum = checksum_xor_uint8 (command, csize - 1, checksum);
+			checksum = checksum_xor_uint8 (data + address, len, checksum);
+			/*if (myrandom (5) == 0) {
+				message ("Garbage\n");
+				unsigned char garbage = 0xFF;
+				serial_write (device, &garbage, 1);
+			}*/
+			serial_write (device, command, csize - 1);
+			serial_write (device, data + address, len);
+
+			/*if (myrandom (20) == 0) {
+				message ("Garbage\n");
+				unsigned char garbage = 0xFF;
+				serial_write (device, &garbage, 1);
+			} else*/
+			serial_write (device, &checksum, 1);
+			//debug (data + address, len);
+			break;
+		case 0x06:
+			csize = 7 - 1;
+			if (vyper2) {
+				serial_read (device, command + 1, csize - 1);
+			} else {
+				serial_read (device, command + 1, csize - 1);
+			}
+			address = (command[3] << 8) + command[4];
+			len = command[5];
+			if (vyper2) {
+				serial_read (device, command + 6, len + 1);
+			} else {
+				serial_read (device, command + 6, len);
+				serial_write (device, command, len + 6);
+				serial_read (device, command + 6 + len, 1);
+				serial_write (device, command + 6 + len, 1);
+			}
+			csize += len + 1;
+			message ("write_memory(%04x,%i)\n", address, len);
+			command[2] -= len;
+			checksum = 0x00;
+			checksum = checksum_xor_uint8 (command, 6, checksum);
+			serial_write (device, command, 6);
+			serial_write (device, &checksum, 1);
+			memcpy (data + address, command + 6, len);
+			//debug (data + address, len);
+			break;
+		case 0x20:
+			csize = 4;
+			if (vyper2) {
+				serial_read (device, command + 1, csize - 1);
+			} else {
+				serial_write (device, command, 1);
+				for (unsigned int i = 1; i < csize; ++i) {
+					serial_read (device, command + i, 1);
+					serial_write (device, command + i, 1);
+				}
+			}
+			message ("reset_maxdepth()\n");
+			serial_write (device, command, csize);
+			break;
+		case 0x0B:
+			csize = 4;
+			if (vyper2) {
+				serial_read (device, command + 1, csize - 1);
+			} else {
+				serial_write (device, command, 1);
+				for (unsigned int i = 1; i < csize; ++i) {
+					serial_read (device, command + i, 1);
+					serial_write (device, command + i, 1);
+				}
+			}
+			message ("reset()\n");
+			serial_write (device, command, csize);
+			break;
+		default:
+			//unknown:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+//#define FAST
+
+static void
+simulation_vyper (serial_t *device, unsigned char data[], unsigned int size)
+{
+	unsigned char command[37] = {0}, checksum = 0;
+	unsigned int csize = 0, len = 0, address = 0;
+	unsigned int offset = 0, ndives = 0;
+
+	int is_spyder = 0;
+	if (data[0x24] == 20 || data[0x24] == 30 || data[0x24] == 60) {
+		if (data[0x16] == 0x01)
+			if (data[0x17] == 0x01 || data[0x17] == 0x02)
+				is_spyder = 1;
+	}
+	message ("is_spyder=%i\n", is_spyder);
+
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 0x05:
+			csize = 5;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			len = command[3];
+			message ("read_memory(%04x,%i)\n", address, len);
+			checksum = 0x00;
+			checksum = checksum_xor_uint8 (command, csize - 1, checksum);
+			checksum = checksum_xor_uint8 (data + address, len, checksum);
+#ifndef FAST
+			serial_sleep (device, 500);
+#endif
+			serial_write (device, command, csize - 1);
+			serial_write (device, data + address, len);
+			serial_write (device, &checksum, 1);
+			break;
+		case 0x06:
+			csize = 4;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			len = command[3];
+			serial_read (device, command + 4, len + 1);
+			csize += len + 1;
+			message ("write_memory(%04x,%i)\n", address, len);
+			checksum = 0x00;
+			checksum = checksum_xor_uint8 (command, 4, checksum);
+#ifndef FAST
+			serial_sleep (device, 500);
+#endif
+			serial_write (device, command, 4);
+			serial_write (device, &checksum, 1);
+			memcpy (data + address, command + 4, len);
+			break;
+		case 0x07:
+			csize = 3;
+			serial_read (device, command + 1, csize - 1);
+			message ("prepare_write_memory()\n");
+#ifndef FAST
+			serial_sleep (device, 500);
+#endif
+			serial_write (device, command, csize);
+			break;
+		case 0x08:
+			if (is_spyder)
+				offset = (data[0x1C] << 8) + data[0x1D] - 1;
+			else
+				offset = (data[0x51] << 8) + data[0x52] - 1;
+			ndives = 0;
+		case 0x09:
+			csize = 3;
+			serial_read (device, command + 1, csize - 1);
+			message ("read_dive(%i,0x%04x)\n", ndives+1, offset);
+#ifndef FAST
+			serial_sleep (device, 300);
+#endif
+			offset = send_next_dive (device, data, offset, command[0], is_spyder);
+			ndives++;
+			break;
+		case 'A':
+			csize = 3;
+			serial_read (device, command + 1, csize - 1);
+			message ("detect_interface()\n");
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+static void
+simulation_eon (serial_t *device, unsigned char data[], unsigned int size)
+{
+	unsigned char command[1] = {0};
+	unsigned char checksum = checksum_add_uint8 (data, SUUNTO_EON_MEMORY_SIZE /*size*/, 0x00);
+
+	message ("checksum=0x%02x\n", checksum);
+	message ("checksum=0x%02x\n", data[SUUNTO_EON_MEMORY_SIZE]);
+
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 'P':
+			message ("profile()\n");
+			for (unsigned int i = 0; i < SUUNTO_EON_MEMORY_SIZE /*size*/; ++i) {
+				serial_write (device, data + i, 1);
+				if ((i % 32) == 0)
+					serial_sleep (device, 10);
+			}
+			//serial_write (device, data, size);
+			serial_write (device, &checksum, 1);
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+
+static void
+simulation_ostc (serial_t *device, unsigned char data[], unsigned int size)
+{
+	int n = 0;
+	unsigned char command[1] = {0};
+	unsigned char eeprom0[256] = {0};
+	unsigned char eeprom1[256] = {0};
+	unsigned char eeprom2[256] = {0};
+	unsigned char clock[6] = {0};
+	unsigned char md2hash[16] = {0};
+	unsigned char temperature[2] = {0};
+
+	unsigned char preamble[6] = {0};
+	unsigned char battery[2] = {0};
+	unsigned char firmware[2] = {0};
+
+	unsigned char pic[] = {0x57};
+	unsigned char ack[] = {'K'};
+	unsigned char nak[] = {'N'};
+	//unsigned char fwdata[0x17F40];
+	unsigned char block[69];
+	unsigned int blocksize = 0x40;
+	unsigned int fwsize = 0x17F40;
+
+	memcpy (preamble, data + 1 - 1, sizeof (preamble)); // preamble
+	memcpy (eeprom0, data + 7 - 1, sizeof (eeprom0)); // eeprom0
+	memcpy (battery, data + 263 - 1, sizeof (battery)); // battery
+	memcpy (firmware, data + 265 - 1, sizeof (firmware)); // firmware
+	if (size >= 33034 + 256)
+		memcpy (eeprom1, data + 33034, sizeof (eeprom1)); // firmware
+
+	/* a    hw_ostc_dump/foreach
+	 * g,j,m  hw_ostc_eeprom_read  (device, bank, data, size);
+	 * d,i,n  hw_ostc_eeprom_write (device, bank, data, size);
+	 * b,f  hw_ostc_parameter_write (device, parameter, data, size); CLOCK/TEMP
+	 * k    hw_ostc_tissue_save  (device);
+	 * h    hw_ostc_tissue_reset (device);
+	 * e    hw_ostc_md2_read (device, data, size);
+	 * c    hw_ostc_simulate (device, data, size);
+	 */
+
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 'a':
+			message ("read profiles\n");
+			for (unsigned int i = 0; i < size; ++i) {
+				serial_write (device, data + i, 1);
+				if ((i % 512) == 0)
+					serial_sleep (device, 10);
+			}
+			//serial_write (device, data, size);
+			break;
+		case 'b':
+			message ("write clock\n");
+			serial_read (device, clock, sizeof (clock));
+			break;
+		case 'c':
+			message ("simulator mode\n");
+			while (1) {
+				serial_read (device, command, 1);
+				message ("depth=%.1fm (%.1fbar)\n",
+					command[0] - 10.0, command[0] / 10.0);
+				if (command[0] == 10)
+					break;
+			}
+			break;
+		case 'd':
+			message ("write eeprom bank 0\n");
+			serial_write (device, command, 1);
+			for (unsigned int i = 0; i < sizeof (eeprom0) - 4; ++i) {
+				serial_read (device, eeprom0 + i + 4, 1);
+				serial_write (device, eeprom0 + i + 4, 1);
+			}
+			break;
+		case 'e':
+			message ("read MD2 Hash fingerprint\n");
+			serial_write (device, firmware, sizeof (firmware));
+			serial_write (device, md2hash, sizeof (md2hash));
+			break;
+		case 'f':
+			message ("write temperature calibration\n");
+			serial_write (device, command, 1);
+			serial_read (device, temperature, sizeof (temperature));
+			break;
+		case 'g':
+			message ("read eeprom bank 0\n");
+			serial_write (device, eeprom0, sizeof (eeprom0));
+			break;
+		case 'h':
+			message ("reset deco data\n");
+			serial_write (device, command, 1);
+			break;
+		case 'i':
+			message ("write eeprom bank 1\n");
+			serial_write (device, command, 1);
+			for (unsigned int i = 0; i < sizeof (eeprom1) - 4; ++i) {
+				serial_read (device, eeprom1 + i + 4, 1);
+				serial_write (device, eeprom1 + i + 4, 1);
+			}
+			break;
+		case 'j':
+			message ("read eeprom bank 1\n");
+			serial_write (device, eeprom1, sizeof (eeprom1));
+			break;
+		case 'k':
+			message ("save tissue data\n");
+			serial_write (device, command, 1);
+			break;
+		case 'm':
+			message ("read eeprom bank 2\n");
+			serial_write (device, eeprom2, sizeof (eeprom2));
+			break;
+		case 'n':
+			message ("write eeprom bank 2\n");
+			serial_write (device, command, 1);
+			for (unsigned int i = 0; i < sizeof (eeprom2) - 4; ++i) {
+				serial_read (device, eeprom2 + i + 4, 1);
+				serial_write (device, eeprom2 + i + 4, 1);
+			}
+			break;
+		case 0xC1:
+			message ("fwupdate\n");
+			serial_write (device, pic, sizeof(pic));
+			serial_write (device, ack, sizeof(ack));
+			serial_set_timeout (device, 1000);
+			while (1) {
+				n = serial_read (device, block, 4);
+				if (n != 4) {
+					message ("timeout\n");
+					break;
+				}
+				unsigned int address = array_uint24_be(block + 0);
+				unsigned int length = block[3];
+				n = serial_read (device, block + 4, length + 1);
+				if (n != length + 1) {
+					message ("nak\n");
+					serial_write (device, nak, sizeof(nak));
+					continue;
+				}
+				unsigned char csum = checksum_add_uint8 (block, 4 + length + 1, 0x00);
+				message ("address=%06x, length=%u, n=%u, csum=%02x\n", address, length, n, csum);
+				if (csum != 0 /*|| error(0.25)*/) {
+					message ("nak\n");
+					serial_write (device, nak, sizeof(nak));
+					continue;
+				}
+				serial_write (device, ack, sizeof(ack));
+			}
+			serial_set_timeout (device, -1);
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+
+static void
+simulation_ostc3 (serial_t *device, unsigned char data[], unsigned int size, unsigned char id[], unsigned int idsize)
+{
+	int n = 0;
+	const unsigned char service[] = {0x4B, 0xAB, 0xCD, 0xEF};
+	unsigned char ready[1] = {0x4D};
+	unsigned char empty[256] = {0};
+	unsigned char command[4] = {0};
+	unsigned char clock[6] = {0};
+	unsigned char custom[60] = {0};
+	unsigned char display[16] = {0};
+	unsigned char dive[1] = {0};
+	unsigned char hardware[1] = {0};
+	unsigned char version[sizeof(custom) + 4] = {0};
+	unsigned char fwheader[16+4] = {0};
+	//unsigned char fwinfo[16] = {0x16, 0x00, 0x00, 0x09, 0x09, 0x01, 0x30, 0x01, 0x00, 0x09, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00};
+	unsigned char fwversion[256][4] = {0};
+	unsigned char fwid = 0;
+
+	const unsigned int headersize = 256, compactsize = 16;
+
+	//memset(fwversion, 0xFF, sizeof(fwversion));
+	memset(empty, 0xFF, sizeof(empty));
+
+	if (idsize >= sizeof(hardware)) {
+		memcpy (hardware, id, sizeof(hardware));
+	}
+
+/*
+
+TYPE    CMD     ECHO    INPUT       OUTPUT      READY
+=====================================================
+
+init    0xBB    N       N           N           0x4D
+exit    0xFF    Y       N           N           N
+header  0x61    N       N           data(65536) 0x4D
+clock   0x62    Y       data(6)     N           0x4D
+custom  0x63    Y       data(60)    N           0x4D
+dive    0x66    Y       data(1)     data(n)     0x4D
+version 0x69    Y       N           data(64)    0x4D
+display 0x6E    Y       data(16)    N           0x4D
+compact 0x6D    N       N           data(4096)  0x4D
+*/
+
+	struct {
+		unsigned char *data;
+		unsigned int size;
+	} dives[256];
+
+	unsigned int offset = 0;
+	unsigned int ndives = 0;
+	while (offset + headersize < size) {
+		unsigned int marker = array_uint16_le (data + offset);
+		if (marker != 0xFAFA)
+			break;
+
+		// Calculate the profile length.
+		unsigned int length = headersize + array_uint24_le (data + offset + 9) - 3;
+
+		// Workaround for a bug in older firmware versions.
+		unsigned int firmware = array_uint16_be (data + offset + 0x30);
+		if (firmware < 93)
+			length -= 3;
+
+		dives[ndives].data = data + offset;
+		dives[ndives].size = length;
+
+		offset += length;
+		ndives++;
+	}
+
+	message ("ndives=%u\n", ndives);
+
+	unsigned int idx = 0;
+	unsigned int length = 0, nbytes = 0;
+
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 0xAA: // Init service mode
+			message ("Init service\n");
+			serial_read (device, command + 1, 3);
+			serial_write (device, service, sizeof(service));
+			ready[0] = 0x4C;
+			break;
+		case 0xBB: // Init download mode
+			message ("Init download\n");
+			serial_write (device, command, 1);
+			ready[0] = 0x4D;
+			break;
+		case 0xFF: // Exit
+			message ("Exit\n");
+			serial_write (device, command, 1);
+			break;
+		case 0x61: // Header
+			message ("Header\n");
+			serial_write (device, command, 1);
+			for (unsigned int i = 0; i < ndives; ++i) {
+				idx = ndives - 1 - i;
+				serial_write (device, dives[idx].data, headersize);
+			}
+			for (unsigned int i = ndives; i < 256; ++i) {
+				serial_write (device, empty, headersize);
+			}
+			break;
+#if 0
+		case 0x6D: // Compact Header
+			message ("Header\n");
+			serial_write (device, command, 1);
+			serial_write (device, data, compactsize);
+			for (unsigned int i = 1; i < 256; ++i) {
+				serial_write (device, empty, compactsize);
+			}
+			break;
+#endif
+		case 0x62: // Clock
+			message ("Clock\n");
+			serial_write (device, command, 1);
+			serial_read (device, clock, sizeof(clock));
+			break;
+		case 0x63: // Custom
+			message ("Custom\n");
+			serial_write (device, command, 1);
+			serial_read (device, custom, sizeof(custom));
+			break;
+		case 0x66: // Dive
+			message ("Dive\n");
+			serial_write (device, command, 1);
+			serial_read (device, dive, sizeof(dive));
+			idx = ndives - 1 - dive[0];
+			serial_write (device, dives[idx].data, dives[idx].size);
+			break;
+		case 0x69: // Version
+			message ("Version\n");
+			serial_write (device, command, 1);
+			serial_write (device, version, sizeof(version));
+			break;
+		case 0x6A: // Hardware
+			message ("Hardware\n");
+			serial_write (device, command, 1);
+			serial_write (device, hardware, sizeof(hardware));
+			break;
+		case 0x6B: // Firmware info
+			message ("Firmware info\n");
+			serial_write (device, command, 1);
+			serial_read (device, &fwid, 1);
+			serial_write (device, fwversion[fwid], sizeof(fwversion[0]));
+			break;
+		case 0x6E: // Display
+			message ("Display\n");
+			serial_write (device, command, 1);
+			serial_read (device, display, sizeof(display));
+			break;
+#if 0
+		case 0x73: // RTE
+		case 0x74: // Firmware
+		case 0x76: // Fonts
+			message ("RTE/Firmware/Fonts (%02x)\n", command[0]);
+#else
+		case 0x73: // Firmware upload
+			message ("Firmware upload\n");
+#endif
+			serial_write (device, command, 1);
+			serial_read (device, fwheader, sizeof(fwheader));
+			length = array_uint32_be(fwheader);
+			fwid = fwheader[4];
+			nbytes = 0;
+			while (nbytes < length) {
+				unsigned char buffer[4*1024];
+				unsigned int len = length - nbytes;
+				if (len > sizeof(buffer)) {
+					len = sizeof(buffer);
+				}
+				serial_read (device, buffer, len);
+				nbytes += len;
+			}
+			memcpy(fwversion[fwid], fwheader + 12, 4);
+			message ("Received %u bytes\n", sizeof(fwheader) + nbytes);
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+		if (command[0] != 0xFF) {
+			serial_write (device, ready, sizeof(ready));
+		}
+	}
+}
+
+static void
+simulation_solution (serial_t *device, unsigned char data[], unsigned int size)
+{
+	unsigned char command[3] = {0}, reply[3] = {0};
+
+for (;;) {
+
+	//PC sends                0xFF
+	serial_read (device, command, 1);
+	message ("data=%02x\n", command[0]);
+	if (command[0] != 0xFF)
+		message ("Unexpected answer byte(s).\n");
+
+	//Solution sends          0x3F
+	reply[0] = 0x3F;
+	serial_write (device, reply, 1);
+	//PC sends                0x4D, 0x01,0x01
+	serial_read (device, command, 3);
+	message ("data=%02x %02x %02x\n", command[0], command[1], command[2]);
+	if (command[0] != 0x4D || command[1] != 0x01 || command[2] != 0x01)
+		message ("Unexpected answer byte(s).\n");
+
+	for (unsigned int i = 1; i < 256; ++i) {
+		message ("Iteration %i (%02x)\n", i, i);
+		//Solution sends      0x01,i,data[i]
+		reply[0] = 0x01;
+		reply[1] = i;
+		reply[2] = data[i];
+		serial_write (device, reply, 3);
+		//PC sends            i
+		serial_read (device, command, 1);
+		message ("data=%02x\n", command[0]);
+		if (command[0] != i)
+			message ("Unexpected answer byte(s).\n");
+
+		//Solution sends data[i]
+		reply[0] = data[i];
+		//if (i == 1)
+			//reply[0] = 0xFF;
+		serial_write (device, reply, 1);
+		//PC sends       0x0D
+		serial_read (device, command, 1);
+		message ("data=%02x\n", command[0]);
+		if (command[0] != 0x0D)
+			message ("Unexpected answer byte(s).\n");
+	}
+	serial_sleep (device, 100);
+	message("loop end\n");
+	//Solution sends     0x02,0x00,0x80
+	reply[0] = 0x02;
+	reply[1] = 0x00;
+	reply[2] = 0x80;
+	serial_write (device, reply, 3);
+	message("marker a\n");
+	//PC sends           0x80
+	serial_read (device, command, 1);
+	message ("data=%02x\n", command[0]);
+	if (command[0] != 0x80)
+		message ("Unexpected answer byte(s).\n");
+	message("marker b\n");
+
+	//Solution sends     0x80
+	reply[0] = 0x80;
+	serial_write (device, reply, 1);
+	message("marker c\n");
+	//PC sends           0x20
+	serial_read (device, command, 1);
+	message ("data=%02x\n", command[0]);
+	if (command[0] != 0x20)
+		message ("Unexpected answer byte(s).\n");
+	message("marker d\n");
+
+	serial_sleep (device, 100);
+
+	//Solution sends     0x3F
+	reply[0] = 0x3F;
+	serial_write (device, reply, 1);
+	message("marker e\n");
+
+}
+
+}
+
+#if 0
+static int
+patch_utc_offset (/*const*/ unsigned char *data, unsigned int size, const unsigned char *fdata, unsigned int fsize, void *userdata)
+{
+	int utc_offset = * ((int *) userdata);
+
+	//message ("utc=%i\n", utc_offset);
+
+	data[16] = utc_offset * 4;
+
+	//memset (data + 12, 0, 4);
+
+	//message ("utc=%i, %i\n", (signed int)data[16], utc_offset);
+
+	return 1;
+}
+#endif
+
+static int
+test (const unsigned char *data, unsigned int size, const unsigned char *fdata, unsigned int fsize, void *userdata)
+{
+	memcpy (userdata, data + 8, 4);
+
+	return 0;
+}
+
+
+static void
+smart_set_clock (unsigned char data[], long clock_device, long clock_host)
+{
+	time_t now = time (NULL);
+	long clock = clock_device + 2 * (now - clock_host);
+	data[0] = (clock      ) & 0xFF;
+	data[1] = (clock >>  8) & 0xFF;
+	data[2] = (clock >> 16) & 0xFF;
+	data[3] = (clock >> 24) & 0xFF;
+}
+
+static int
+test_smart_length (const unsigned char *data, unsigned int size, const unsigned char *fdata, unsigned int fsize, void *userdata)
+{
+	memomouse_data_t *x = (memomouse_data_t *) userdata;
+
+	/*unsigned int timestamp = data[11] + (data[12] <<  8) +
+		(data[13] << 16) + (data[14] << 24);*/
+
+	unsigned int timestamp = array_uint32_le (data + 8);
+
+	if (timestamp > x->timestamp)
+		x->length += size;
+	else
+		x->skip += size;
+
+	return 1;
+}
+
+static void
+simulation_smart (unsigned char data[], unsigned int size, unsigned char model, unsigned int serial, long clock_device, long clock_host)
+{
+#ifndef NOIRDA
+	if (clock_device == 0)
+		clock_host = 946684800; // 2000-01-01 00:00:00 UTC
+
+	int rc = 0;
+	dc_context_t *context = NULL;
+	irda_t *device = NULL;
+	unsigned char command[9] = {0};
+	unsigned char answer[4] = {0};
+	//unsigned int nbytes = 0;
+
+	unsigned char timestamp[4] = {0};
+	uwatec_smart_extract_dives (NULL, data, size, test, &timestamp);
+
+#if 0
+	int utc_offset = -7;
+	uwatec_smart_extract_dives (NULL, data, size, patch_utc_offset, &utc_offset);
+#endif
+
+	memomouse_data_t x = {0, 0, 0};
+
+begin:
+	irda_socket_open (&device, context);
+	irda_socket_bind_lsap (device, 1);
+	irda_socket_listen (device, 0);
+	irda_socket_accept (device);
+
+	for (;;) {
+		memset (command, 0, sizeof (command));
+		rc = irda_socket_read (device, command, 1);
+		if (rc <= 0)
+			break;
+		switch (command[0]) {
+		case 0x13:
+			message ("Handshake (stage 0).\n");
+			message ("rc=%i, command=%02x\n", rc, command[0]);
+			answer[0] = 0x01;
+			irda_socket_write (device, answer, 1);
+			break;
+		case 0x1B:
+			message ("Handshake (stage 1).\n");
+			message ("rc=%i, command=%02x\n", rc, command[0]);
+			answer[0] = 0x01;
+			irda_socket_write (device, answer, 1);
+			break;
+		case 0x1C:
+			message ("Handshake (stage 2).\n");
+			rc = irda_socket_read (device, command + 1, 5 - 1);
+			message ("rc=%i, command=%02x%02x%02x%02x%02x\n", rc, command[0], command[1], command[2], command[3], command[4]);
+			answer[0] = 0x01;
+			irda_socket_write (device, answer, 1);
+			break;
+		case 0x1A:
+			message ("Dive Computer Time.\n");
+			message ("rc=%i, command=%02x\n", rc, command[0]);
+			//memcpy (answer, timestamp, 4);
+			smart_set_clock (answer, clock_device, clock_host);
+			irda_socket_write (device, answer, 4);
+			break;
+		case 0x14:
+			message ("Serial Number\n");
+			message ("rc=%i, command=%02x\n", rc, command[0]);
+			answer[0] = (serial      ) & 0xFF;
+			answer[1] = (serial >> 8 ) & 0xFF;
+			answer[2] = (serial >> 16) & 0xFF;
+			answer[3] = (serial >> 24) & 0xFF;
+			irda_socket_write (device, answer, 4);
+			break;
+		case 0x10:
+			message ("Dive Computer Model.\n");
+			message ("rc=%i, command=%02x\n", rc, command[0]);
+			answer[0] = model;
+			irda_socket_write (device, answer, 1);
+			break;
+		case 0xC6:
+			message ("Data Length.\n");
+			rc = irda_socket_read (device, command + 1, 9 - 1);
+			message ("rc=%i, command=%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", rc,
+				command[0], command[1], command[2], command[3], command[4],
+				command[5], command[6], command[7], command[8]);
+#if 1
+			x.timestamp = array_uint32_le (command + 1);
+			x.length = x.skip = 0;
+			uwatec_smart_extract_dives (NULL, data, size, test_smart_length, &x);
+#else
+			x.length = size;
+			x.skip = 0;
+#endif
+			message ("skip=%u, length=%u\n", x.skip, x.length);
+			answer[0] = (x.length      ) & 0xFF;
+			answer[1] = (x.length >> 8 ) & 0xFF;
+			answer[2] = (x.length >> 16) & 0xFF;
+			answer[3] = (x.length >> 24) & 0xFF;
+			irda_socket_write (device, answer, 4);
+			break;
+		case 0xC4:
+			message ("Data.\n");
+			rc = irda_socket_read (device, command + 1, 9 - 1);
+			message ("rc=%i, command=%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", rc,
+				command[0], command[1], command[2], command[3], command[4],
+				command[5], command[6], command[7], command[8]);
+#if 1
+			x.timestamp = array_uint32_le (command + 1);
+			x.length = x.skip = 0;
+			uwatec_smart_extract_dives (NULL, data, size, test_smart_length, &x);
+#else
+			x.length = size;
+			x.skip = 0;
+#endif
+			answer[0] = ((x.length + 4)      ) & 0xFF;
+			answer[1] = ((x.length + 4) >> 8 ) & 0xFF;
+			answer[2] = ((x.length + 4) >> 16) & 0xFF;
+			answer[3] = ((x.length + 4) >> 24) & 0xFF;
+			irda_socket_write (device, answer, 4);
+			irda_socket_write (device, data + x.skip, x.length);
+			/*nbytes = 0;
+			while (nbytes < size) {
+				unsigned int len = 32;
+				if (nbytes + len > size)
+					len = size - nbytes;
+				int n = irda_socket_write (device, data + nbytes, len);
+				if (n < 0) {
+					message ("FATAL ERROR\n");
+					break;
+				}
+				nbytes += n;
+			}*/
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+	message ("exit\n");
+	irda_socket_close (device);
+	goto begin;
+#endif
+}
+
+
+static void
+simulation_meridian (serial_t *device, unsigned char data[], unsigned int size, unsigned char model, unsigned int serial, long clock_device, long clock_host)
+{
+	const unsigned char ack = 0x11, nak = 0x66;
+	unsigned char command[255 + 11];
+	unsigned char response[255 + 6];
+	unsigned char *answer = response + 5;
+
+	if (clock_device == 0)
+		clock_host = 946684800; // 2000-01-01 00:00:00 UTC
+
+	for (;;) {
+		unsigned csize = 0, asize = 0;
+
+		// Read the packet.
+		serial_read (device, command, 11);
+		csize = command[7];
+		serial_read (device, command + 11, csize + 1);
+		debug(command, csize + 12);
+
+		// Send the echo.
+		serial_write (device, command, csize + 12);
+
+		switch (command[11]) {
+		case 0x13:
+			message ("Handshake (stage 0).\n");
+			answer[0] = 0x01;
+			asize = 1;
+			break;
+		case 0x1B:
+			message ("Handshake (stage 1).\n");
+			answer[0] = 0x01;
+			asize = 1;
+			break;
+		case 0x1C:
+			message ("Handshake (stage 2).\n");
+			answer[0] = 0x01;
+			asize = 1;
+			break;
+		case 0x1A:
+			message ("Dive Computer Time.\n");
+			smart_set_clock (answer, clock_device, clock_host);
+			asize = 4;
+			break;
+		case 0x14:
+			message ("Serial Number\n");
+			answer[0] = (serial      ) & 0xFF;
+			answer[1] = (serial >> 8 ) & 0xFF;
+			answer[2] = (serial >> 16) & 0xFF;
+			answer[3] = (serial >> 24) & 0xFF;
+			asize = 4;
+			break;
+		case 0x10:
+			message ("Dive Computer Model.\n");
+			answer[0] = model;
+			asize = 1;
+			break;
+		case 0xC6:
+			message ("Data Length.\n");
+			answer[0] = (size      ) & 0xFF;
+			answer[1] = (size >> 8 ) & 0xFF;
+			answer[2] = (size >> 16) & 0xFF;
+			answer[3] = (size >> 24) & 0xFF;
+			asize = 4;
+			break;
+		case 0xC4:
+			message ("Data.\n");
+			answer[0] = ((size + 4)      ) & 0xFF;
+			answer[1] = ((size + 4) >> 8 ) & 0xFF;
+			answer[2] = ((size + 4) >> 16) & 0xFF;
+			answer[3] = ((size + 4) >> 24) & 0xFF;
+			asize = 4;
+			break;
+		case 0xFF:
+			if (command[12] == 0x10) {
+			message ("Feature Set\n");
+			unsigned int feature = 0;//1036746750;
+			answer[0] = (feature      ) & 0xFF;
+			answer[1] = (feature >> 8 ) & 0xFF;
+			answer[2] = (feature >> 16) & 0xFF;
+			answer[3] = (feature >> 24) & 0xFF;
+			asize = 4;
+			}
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[11]);
+			continue;
+		}
+
+		response[0] = ((asize + 1)      ) & 0xFF;
+		response[1] = ((asize + 1) >>  8) & 0xFF;
+		response[2] = ((asize + 1) >> 16) & 0xFF;
+		response[3] = ((asize + 1) >> 24) & 0xFF;
+		response[4] = command[11];
+		response[5 + asize] = checksum_xor_uint8 (response, asize + 5, 0x00);
+
+		serial_write (device, &ack, sizeof(ack));
+		serial_write (device, response, asize + 6);
+
+		if (command[11] == 0xC4) {
+			const unsigned int packetsize = 0x80;
+
+			unsigned int nbytes = 0;
+			while (nbytes < size) {
+				unsigned int len = size - nbytes;
+				if (len > packetsize)
+					len = packetsize;
+
+				unsigned char header[] = {
+					((len + 1)      ) & 0xFF,
+					((len + 1) >>  8) & 0xFF,
+					((len + 1) >> 16) & 0xFF,
+					((len + 1) >> 24) & 0xFF,
+					command[11]
+				};
+				unsigned char csum = 0x00;
+				csum = checksum_xor_uint8 (header, sizeof (header), csum);
+				csum = checksum_xor_uint8 (data + nbytes, len, csum);
+
+				serial_write (device, header, sizeof (header));
+				serial_write (device, data + nbytes, len);
+				serial_write (device, &csum, sizeof (csum));
+				#ifdef WIN32
+				serial_sleep (device, 10);
+				#endif
+
+				nbytes += len;
+			}
+		}
+	}
+}
+
+
+#define SKIPNULL 1
+
+
+static void
+simulation_atom2 (serial_t *device, unsigned char data[], unsigned int size, const unsigned char id[])
+{
+	unsigned char command[22] = {0}, checksum = 0;
+	unsigned short checksum16 = 0;
+	unsigned int csize = 0, address = 0;
+
+	const unsigned char nak = 0xA5, ack = 0x5A;
+	const double probability = 0;//0.05;
+
+	unsigned char unknown[3] = {0x5A, 0x04, 0xD6};
+	unsigned char handshake[3] = {nak, nak, nak};
+	unsigned char quit[1] = {nak};
+
+	unsigned char version[17] = {0};
+	memcpy (version, id, 16);
+	version[16] = checksum_add_uint8 (version, 16, 0x00);
+/*
+	unsigned char dummy[16] = {
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+	dc_buffer_t *buffer = dc_buffer_new (0);
+	dc_buffer_append (buffer, id, 16);
+	dc_buffer_append (buffer, data, size);
+	if (size % 0x8000 == 0x8000 - 0x10)
+		dc_buffer_append (buffer, dummy, sizeof (dummy));
+*/
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 0xA8: // Handshake (A8 99 00)
+			csize = 3 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			message ("handshake()=%02x %02x %02x\n", command[0], command[1], command[2]);
+			serial_write (device, handshake, sizeof (handshake));
+			break;
+		case 0x84: // Version (84 00)
+			csize = 2 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			message ("version()=%02x %02x\n", command[0], command[1]);
+			if (error (probability)) {
+				message ("nak()\n");
+				serial_write (device, &nak, 1);
+			} else {
+				serial_write (device, &ack, 1);
+				serial_write (device, version, sizeof (version));
+			}
+			break;
+		case 0xBA:
+			csize = 2 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			message ("unknown()=%02x %02x\n", command[0], command[1]);
+			serial_write (device, unknown, sizeof (unknown));
+			break;
+		case 0xB1: // Read (B1 HI LO 00)
+		//case 0xB4: // Read (B4 HI LO 00)
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			message ("read(%04x,1)=%02x %02x %02x %02x\n", address, command[0], command[1], command[2], command[3]);
+			if (16 * (address + 1) > size) {
+				message ("Overflow detected, ignoring command!\n");
+				break;
+			}
+			if (command[0] == 0xB1)
+				checksum = checksum_add_uint8 (data + 16 * address, 16, 0x00);
+			else
+				checksum = checksum_add_uint8 (data + 16 * address, 128, 0x00);
+			if (error (probability)) {
+				message ("nak()\n");
+				serial_write (device, &nak, 1);
+			} else {
+				serial_write (device, &ack, 1);
+				if (command[0] == 0xB1)
+					serial_write (device, data + 16 * address, 16);
+				else
+					serial_write (device, data + 16 * address, 128);
+				serial_write (device, &checksum, 1);
+			}
+			break;
+		case 0xB4: // Read (B4 HI LO 00)
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			message ("read(%04x,8)=%02x %02x %02x %02x\n", address, command[0], command[1], command[2], command[3]);
+			checksum = checksum_add_uint8 (data + 16 * address, 128, 0x00);
+			serial_write (device, &ack, 1);
+			//serial_write (device, data + 16 * address, 128);
+			//serial_write (device, &checksum, 1);
+			{
+				unsigned char tmp[128 + 1];
+				memcpy (tmp, data + 16 * address, 128);
+				memcpy (tmp + 128, &checksum, 1);
+				serial_write (device, tmp, sizeof (tmp));
+			}
+			break;
+		case 0xB8: // Read (B8 HI LO 00) - return 256 bytes with 2 byte crc
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			message ("read(%04x,16)=%02x %02x %02x %02x\n", address, command[0], command[1], command[2], command[3]);
+			checksum16 = checksum_add_uint16 (data + 16 * address, 256, 0x00);
+			serial_write (device, &ack, 1);
+			{
+				unsigned char tmp[256 + 2];
+				memcpy (tmp, data + 16 * address, 256);
+				memcpy (tmp + 256, &checksum16, 2);
+				serial_write (device, tmp, sizeof (tmp));
+			}
+			break;
+		case 0xB2: // Write (22+2)
+			/*csize = 22;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			message ("write(%04x)\n", address);
+			memcpy (data + address, command + 4, 16);
+			serial_write (device, &ack, 1);
+			serial_write (device, &ack, 1);*/
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			message ("prepare_write(%04x)\n", address);
+			#if SKIPNULL
+			if (serial_get_received(device) == 1) {
+				serial_read (device, command + 1 + csize - 1, 1);
+			}
+			#endif
+			serial_write (device, &ack, 1);
+			csize = 18 - SKIPNULL;
+			serial_read (device, command + 4, csize);
+			message ("write(%04x)\n", address);
+			memcpy (data + 16 * address, command + 4, 16);
+			serial_write (device, &ack, 1);
+			message ("command=");
+			for (unsigned int i = 0; i < 22; ++i)
+				message ("%02x ", command[i]);
+			message ("\n");
+			break;
+		case 0x6A: // Quit (6A 05 A5 00)
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			message ("quit()=%02x %02x %02x %02x\n", command[0], command[1], command[2], command[3]);
+			serial_write (device, quit, sizeof (quit));
+			break;
+		case 0x91: // Keepalive (91 05 A5 00)
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			message ("keepalive()=%02x %02x %02x %02x\n", command[0], command[1], command[2], command[3]);
+			serial_write (device, &ack, 1);
+			break;
+		default:
+			if (!SKIPNULL || command[0] != 0x00)
+				message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+static void
+simulation_veo250 (serial_t *device, unsigned char data[], unsigned int size, const unsigned char version[], unsigned int vsize)
+{
+	unsigned char command[22] = {0}, checksum = 0;
+	unsigned int csize = 0, address = 0, first = 0, last = 0;
+
+	//const unsigned char garbage[3] = {0xFF, 0xFF, 0xFF};
+
+	const unsigned char nak = 0xA5, ack = 0x5A;
+	//const double probability = 0;//0.05;
+
+	const unsigned char init[14] = {
+		0x50, 0x50, 0x53, 0x2D, 0x2D, 0x4F, 0x4B,
+		0x5F, 0x56, 0x32, 0x2E, 0x30, 0x30, 0x00};
+	const unsigned char quit[3] = {0x5A, 0xA5, 0xA5};
+
+
+	unsigned int answer = 0;
+
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 0x55: // Init (55 00)
+			csize = 2;
+			serial_read (device, command + 1, csize - 1);
+			message ("init()\n");
+			serial_write (device, init, sizeof (init));
+			break;
+		case 0x98: // Quit (98 00)
+			csize = 2;
+			serial_read (device, command + 1, csize - 1);
+			message ("quit()\n");
+			break;
+		case 0x90: // Version (90 00)
+			csize = 2;
+			serial_read (device, command + 1, csize - 1);
+			message ("version()\n");
+			/*if (error (probability)) {
+				message ("nak()\n");
+				serial_write (device, &nak, 1);
+			} else {
+				serial_write (device, &ack, 1);
+				serial_write (device, version, sizeof (version));
+			}*/
+			//serial_write (device, &garbage, 1);
+			serial_write (device, &ack, 1);
+			serial_write (device, version, vsize);
+			checksum = checksum_add_uint8 (version, vsize, 0x00);
+			serial_write (device, &checksum, 1);
+			serial_write (device, &nak, 1);
+			break;
+		case 0x20: // Read (6+19) TODO
+			#if WIN32
+			csize = 5;
+			#else
+			csize = 6;
+			#endif
+			serial_read (device, command + 1, csize - 1);
+			address = command[2] + (command[1] << 8);
+			first = command[1] + (command[2] << 8);
+			last  = command[3] + (command[4] << 8);
+			message ("read(%04x,%04x,%u)=%02x %02x %02x %02x %02x %02x\n", first, last, last - first + 1,
+				command[0], command[1], command[2], command[3], command[4], command[5]);
+			if (first > last) {
+				message ("Bogus command ignored!\n");
+				continue;
+			}
+
+			answer = myrandom (20);
+
+			switch (answer) {
+			/*case 0: // TIMEOUT
+				message ("ERROR: Timeout\n");
+				break;
+			case 1: // NAK
+				message ("ERROR: NAK\n");
+				serial_write (device, &nak, 1);
+				break;
+			case 2: // GARBAGE
+				message ("ERROR: Garbage\n");
+				serial_write (device, garbage, sizeof (garbage));
+				break;*/
+			/*case 3: // WRONG PAGE
+				first += 1;
+				last += 2;
+				message ("Wrong page (%04x, %04x)\n", first, last);*/
+			default: // ACK + DATA
+				serial_write (device, &ack, 1);
+				for (unsigned int i = 0; i < last - first + 1; ++i) {
+					checksum = checksum_add_uint8 (data + 16 * (first + i), 16, 0x00);
+					serial_write (device, data + 16 * (first + i), 16);
+					serial_write (device, &checksum, 1);
+					//serial_write (device, garbage, 1);
+				}
+				serial_write (device, &nak, 1);
+				break;
+			}
+
+#if 0
+			csize = 6;
+			serial_read (device, command + 1, csize - 1);
+			address = command[1] + (command[2] << 8);
+			message ("read(%04x)\n", address);
+			/*checksum = checksum_add_uint8 (data + 16 * address, 16, 0x00);
+			if (error (probability)) {
+				message ("nak()\n");
+				serial_write (device, &nak, 1);
+			} else {
+				serial_write (device, &ack, 1);
+				serial_write (device, data + 16 * address, 16);
+				serial_write (device, &checksum, 1);
+			}*/
+			//serial_write (device, version, sizeof (version));
+
+			checksum = checksum_add_uint8 (data + 16 * address, 16, 0x00);
+			if (answer) {
+				serial_write (device, &ack, 1);
+				serial_write (device, data + 16 * address, 16);
+				serial_write (device, &checksum, 1);
+				serial_write (device, &nak, 1);
+				answer = 0;
+			} else {
+				//message ("multi\n");
+				serial_write (device, &ack, 1);
+				//for (unsigned int i = 0; i < 5; ++i) {
+					serial_write (device, data + 16 * address, 16);
+					serial_write (device, &checksum, 1);
+				//}
+				serial_write (device, &nak, 1);
+				answer = 1;
+			}
+#endif
+			break;
+#if 0
+		case 0xB2: // Write (22+2)
+			/*csize = 22;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			message ("write(%04x)\n", address);
+			memcpy (data + address, command + 4, 16);
+			serial_write (device, &ack, 1);
+			serial_write (device, &ack, 1);*/
+			csize = 4;
+			serial_read (device, command + 1, csize - 1);
+			address = (command[1] << 8) + command[2];
+			message ("prepare_write(%04x)\n", address);
+			serial_write (device, &nak, 1);
+			csize = 18;
+			serial_read (device, command + 4, csize);
+			message ("write(%04x)\n", address);
+			memcpy (data + 16 * address, command + 4, 16);
+			serial_write (device, &ack, 1);
+			message ("command=");
+			for (unsigned int i = 0; i < 22; ++i)
+				message ("%02x ", command[i]);
+			message ("\n");
+			break;
+#endif
+		case 0x91: // Keepalive (4+3)
+			csize = 4;
+			serial_read (device, command + 1, csize - 1);
+			message ("keepalive()\n");
+			serial_write (device, quit, sizeof (quit));
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+
+static void
+simulation_vtpro (serial_t *device, unsigned char data[], unsigned int size, const unsigned char version[], dc_buffer_t *logbook)
+{
+	unsigned char command[22] = {0}, checksum = 0;
+	unsigned int csize = 0, address = 0, first = 0, last = 0;
+
+	const unsigned char ack = 0x5A, /*nak = 0xA5,*/ end = 0x51;//, garbage = 0xFF;
+
+/*
+AA 00 (init?)
+6A 05 A5 00 (quit?)
+88 00 (handshake?)
+72 03 00 00 (version deel 1?)
+72 03 10 00 (version deel 2?)
+18 00 (?)
+6A 08 00 00 (keepalive?)
+34 HI LO HI LO 00
+*/
+
+	const unsigned char init_mod[] = {
+		0x4D, 0x4F, 0x44, 0x2D, 0x2D, 0x4F, 0x4B,
+		0x5F, 0x56, 0x32, 0x2E, 0x30, 0x30};
+	const unsigned char init_intr[] = {
+        0x49, 0x4E, 0x54, 0x52, 0x2D, 0x4F, 0x4B,
+        0x5F, 0x56, 0x31, 0x2E, 0x31, 0x31};
+	const unsigned char calibrate[] = {0x5A, 0x26, 0x00};
+	//const unsigned char calibrate_garbage[] = {0xff, 0xff, 0xff};
+	const unsigned char keepalive[] = {0x5A, 0x51};
+	const unsigned char unknown2[] = {0x02};
+	const unsigned char empty[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+	//unsigned int answer = 0;
+
+	for (;;) {
+		serial_read (device, command, 1);
+		switch (command[0]) {
+		case 0xAA: // Init (AA 00)
+		case 0x20: // Init (20 00)
+			csize = 2 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			message ("init(0x%02x)\n", command[0]);
+			if (command[0] == 0xAA) {
+				serial_write (device, init_mod, sizeof (init_mod));
+			} else {
+				serial_write (device, init_intr, sizeof (init_intr));
+			}
+			break;
+		case 0x6A: // Quit (6A 05 A5 00) + Keepalive (6A 08 00 00)
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			if (command[1] == 0x05) {
+				message ("quit()\n");
+			} else {
+				message ("keepalive()\n");
+			}
+			serial_write (device, keepalive, sizeof (keepalive));
+			break;
+		case 0x88: // Version 0 (88 00)
+			csize = 2 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			message ("version(0)\n");
+			checksum = checksum_add_uint4 (version, 8, 0x00);
+			serial_write (device, &ack, 1);
+			serial_write (device, version, 8);
+			serial_write (device, &checksum, 1);
+			break;
+		case 0x72: // Version 1 (72 03 00 00) + Version 2 (72 03 10 00)
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			if (command[2] == 0x00) {
+				message ("version(1)\n");
+				checksum = checksum_add_uint4 (version, 8, 0x00);
+				serial_write (device, &ack, 1);
+				serial_write (device, version, 8);
+				serial_write (device, &checksum, 1);
+				serial_write (device, &end, 1);
+			} else {
+				message ("version(2)\n");
+				checksum = checksum_add_uint4 (version + 8, 8, 0x00);
+				serial_write (device, &ack, 1);
+				serial_write (device, version + 8, 8);
+				serial_write (device, &checksum, 1);
+				serial_write (device, &end, 1);
+			}
+			break;
+		case 0x18: // Calibrate (18 00)
+			csize = 2 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			message ("calibrate()\n");
+			//serial_sleep (device, 6000);
+			serial_write (device, calibrate, sizeof (calibrate));
+			break;
+		case 0x34: // Read (34 HI LO HI LO 00)
+			csize = 6 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			address = command[2] + (command[1] << 8);
+			first = command[2] + (command[1] << 8);
+			last  = command[4] + (command[3] << 8);
+			//message ("read(%04x)\n", address);
+			/*message ("read(%04x)=%02x %02x %02x %02x %02x %02x\n", address,
+				command[0], command[1], command[2], command[3], command[4], command[5]);*/
+			message ("read(%04x,%04x,%u)=%02x %02x %02x %02x %02x %02x\n", first, last, last - first + 1,
+				command[0], command[1], command[2], command[3], command[4], command[5]);
+			if (first > last || last - first + 1 > 32) {
+				message ("Bogus command ignored!\n");
+				continue;
+			}
+			serial_write (device, &ack, 1);
+			for (unsigned int i = 0; i < last - first + 1; ++i) {
+				checksum = checksum_add_uint8 (data + 16 * (first + i), 16, 0x00);
+				serial_write (device, data + 16 * (first + i), 16);
+				serial_write (device, &checksum, 1);
+			}
+			break;
+		case 0x52: // Read table index (52 XX XX 00)
+			csize = 4 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			address = command[2] + (command[1] << 8);
+			message ("read table index(%04x)=%02x %02x %02x\n", address,
+				command[0], command[1], command[2]);
+			serial_write (device, &ack, 1);
+			for (unsigned int i = 0; i < address + 1; ++i) {
+				if ((i+1) * 8 > dc_buffer_get_size(logbook)) {
+					checksum = checksum_add_uint4 (empty, 8, 0x00);
+					serial_write (device, empty, 8);
+				} else {
+					const unsigned char *p = dc_buffer_get_data(logbook);
+					checksum = checksum_add_uint4 (p + i * 8, 8, 0x00);
+					serial_write (device, p + i * 8, 8);
+				}
+				serial_write (device, &checksum, 1);
+			}
+			break;
+		case 0x48: // Unknown (48 00)
+			csize = 2 - SKIPNULL;
+			serial_read (device, command + 1, csize - 1);
+			address = command[2] + (command[1] << 8);
+			message ("unknown()=%02x\n",
+				command[0]);
+			serial_write (device, &ack, 1);
+			serial_write (device, unknown2, sizeof(unknown2));
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+static void
+simulation_seiko (serial_t *device, unsigned char data[], unsigned int size, unsigned char version[])
+{
+	const unsigned char trailer = 0x45;
+
+	unsigned char command[3];
+	unsigned int csize, address;
+
+	unsigned char id = 0x08;
+	if (version) {
+		id = version[0];
+	}
+
+	for (;;) {
+		serial_read (device, command, 1);
+		serial_write (device, command, 1);
+
+		switch (command[0]) {
+		case 0x41:
+			csize = 3;
+			for (unsigned int i = 1; i < csize; ++i) {
+				serial_read (device, command + i, 1);
+				serial_write (device, command + i, 1);
+			}
+			message ("0x%02x %02x %02x\n", command[0], command[1], command[2]);
+			serial_write (device, command, csize);
+			break;
+		case 0x44:
+			csize = 1;
+			message ("0x%02x\n", command[0]);
+			serial_write (device, &id, 1);
+			break;
+		case 0x0C:
+			csize = 1;
+			message ("0x%02x\n", command[0]);
+			serial_write (device, &trailer, 1);
+			break;
+		case 0x52:
+			csize = 3;
+			for (unsigned int i = 1; i < csize; ++i) {
+				serial_read (device, command + i, 1);
+				serial_write (device, command + i, 1);
+			}
+			address = (command[1] << 8) + command[2];
+			message ("read(%04x, %u, %u)\n", address, 128, (address * 32) % 128);
+			serial_write (device, data + address * 32, 128);
+			serial_write (device, &trailer, 1);
+			break;
+		case 0x46:
+			csize = 1;
+			message ("0x%02x\n", command[0]);
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+static void
+simulation_zeagle (serial_t *device, unsigned char data[], unsigned int size)
+{
+	const unsigned char header= 0x02, trailer = 0x03, cmd = 0x4D;
+
+	const unsigned char id[] = {0x02, 0x08, 0x00,
+		0x41, 0x44, 0x31, 0x31, 0x31, 0x30, 0x33, 0x30,
+		0x55, 0x03};
+
+	unsigned char command[13], length[2], checksum;
+	unsigned int csize, address, count;
+/*
+W: 02 08 00 4D 60 61 40 00 00 00 00 B2 03
+R:    08 00 4D 60 61 40 00 00 00 00 B2 03
+R: 02 41 00 4D FF(64) F3 03
+
+PACKET := 02 SIZE(2) DATA(SIZE) CRC 03
+CRC := ~SUM8(DATA) + 1 (Two's complement)
+
+READ_CMD := TYPE ADDR(2) NBYTES(2) 00 00 00
+READ_ANS := TYPE DATA(64)
+*/
+	for (;;) {
+		serial_read (device, command, 1);
+		if (command[0] != 0x02) {
+			message ("Unknown packet header (%02x)\n", command[0]);
+			continue;
+		}
+		serial_read (device, command + 1, 2);
+		csize = array_uint16_le (command + 1) + 5;
+		serial_read (device, command + 3, csize - 3);
+		//debug (command, csize);
+
+		switch (command[3]) {
+		case 0x41:
+			message ("init()\n");
+			serial_write (device, command, csize);
+			serial_write (device, id, sizeof (id));
+			break;
+		case 0x4D:
+			address = array_uint16_le (command + 4);
+			count = array_uint16_le (command + 6);
+			message ("read(%04x, %u)\n", address, count);
+			length[0] = ((count + 1)     ) & 0xFF;
+			length[1] = ((count + 1) >> 8) & 0xFF;
+			checksum = ~checksum_add_uint8 (data + address, count, cmd) + 1;
+			serial_write (device, command, csize);
+			serial_write (device, &header, 1);
+			serial_write (device, length, sizeof (length));
+			serial_write (device, &cmd, 1);
+			serial_write (device, data + address, count);
+			serial_write (device, &checksum, 1);
+			serial_write (device, &trailer, 1);
+			break;
+		default:
+			message ("Unknown command 0x%02x\n", command[0]);
+			break;
+		}
+	}
+}
+
+/* SLIP special character codes */
+#define END       0xC0 /* indicates end of packet */
+#define ESC       0xDB /* indicates byte stuffing */
+#define ESC_END   0xDC /* ESC ESC_END means END data byte */
+#define ESC_ESC   0xDD /* ESC ESC_ESC means ESC data byte */
+
+static int
+shearwater_predator_slip_write (serial_t *device, const unsigned char data[], unsigned int size)
+{
+	int n = 0;
+	const unsigned char end[] = {END};
+	const unsigned char esc_end[] = {ESC, ESC_END};
+	const unsigned char esc_esc[] = {ESC, ESC_ESC};
+
+	/* Send an initial END character to flush out any data that may have
+	   accumulated in the receiver due to line noise. */
+#if 0
+	n = serial_write (device, end, sizeof (end));
+	if (n != sizeof (end)) {
+		return -1;
+	}
+#endif
+
+	/* For each byte in the packet, send the appropriate character sequence. */
+	for (unsigned int i = 0; i < size; ++i) {
+		const unsigned char *seq = NULL;
+		unsigned int len = 0;
+		switch(data[i]) {
+		case END:
+			/* If it's the same code as an END character, we send a special two
+			   character code so as not to make the receiver think we sent an
+			   END. */
+			seq = esc_end;
+			len = sizeof (esc_end);
+			break;
+		case ESC:
+			/* If it's the same code as an ESC character, we send a special two
+			   character code so as not to make the receiver think we sent an
+			   ESC. */
+			seq = esc_esc;
+			len = sizeof (esc_esc);
+			break;
+		default:
+			/* Otherwise, we just send the character. */
+			seq = data + i;
+			len = 1;
+			break;
+		}
+
+		n = serial_write (device, seq, len);
+		if (n != len) {
+			return -1;
+		}
+	}
+
+	/* Tell the receiver that we're done sending the packet. */
+	n = serial_write (device, end, sizeof (end));
+	if (n != sizeof (end)) {
+		return -1;
+	}
+
+	return size;
+}
+
+
+static int
+shearwater_predator_slip_read (serial_t *device, unsigned char data[], unsigned int size)
+{
+	unsigned int received = 0;
+
+	/* Sit in a loop reading bytes until we put together a whole packet.
+	   Make sure not to copy them into the packet if we run out of room. */
+	while (1) {
+		unsigned char c = 0;
+		int n = 0;
+
+		/* Get a character to process. */
+		n = serial_read (device, &c, 1);
+		if (n != 1) {
+			return -1;
+		}
+
+		/* Handle byte stuffing if necessary. */
+		switch (c) {
+		case END:
+			/* If it's an END character then we're done with the packet. */
+			/* A minor optimization: if there is no data in the packet, ignore
+			   it. This is meant to avoid bothering IP with all the empty
+			   packets generated by the * duplicate END characters which are in
+			   turn sent to try to detect line noise. */
+			if (received)
+				return received;
+			else
+				break;
+		case ESC:
+			/* If it's the same code as an ESC character, wait and get another
+			   character and then figure out what to store in the packet based
+			   on that. */
+			n = serial_read (device, &c, 1);
+			if (n != 1) {
+				return -1;
+			}
+
+			/* If it's not one of these two, then we have a protocol violation.
+			   The best bet seems to be to leave the byte alone and just stuff
+			   it into the packet. */
+			switch (c) {
+			case ESC_END:
+				c = END;
+				break;
+			case ESC_ESC:
+				c = ESC;
+				break;
+			}
+			/* Fall-through */
+		default:
+			if (received < size)
+				data[received] = c;
+			received++;
+		}
+	}
+
+	return received;
+}
+
+static void
+simulation_predator (serial_t *device, unsigned char data[], unsigned int size)
+{
+	unsigned char req_init[] = {
+		0x35, 0x00, 0x34,
+		0xDD, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x80};
+	unsigned char req_block[] = {0x36, 0x00};
+	unsigned char req_quit[] = {0x37};
+
+	unsigned char resp_init[]      = { 0x75, 0x10, 0x82 };
+	unsigned char resp_block[]     = { 0x76, 0 }; // index 1 = block index, index 2+ = divelog data
+	unsigned char resp_quit[]    = { 0x77, 0x00 };
+
+	unsigned char *pred_buf = data;
+	unsigned int pred_len = size;
+
+	unsigned char *manifest_buf = data;
+	unsigned int manifest_len = 0x600;
+
+	unsigned char *dive_buf = data + 0x600;
+	unsigned int dive_len = size - 0x600;
+
+	unsigned int blocksize = 0x82;
+	unsigned int nbytes = 0;
+	unsigned char block = 1;
+
+	for (;;) {
+		unsigned char packet[254 + 4];
+		unsigned char response[254 + 4];
+		int n = shearwater_predator_slip_read (device, packet, sizeof (packet));
+		if (n < 4 || packet[0] != 0xFF || packet[1] != 0x01 || packet[3] != 0x00) {
+			message ("Invalid packet.\n");
+			continue;
+		}
+		debug (packet, n);
+		n -= 4;
+		//message ("n=%u.\n", n);
+
+		response[0] = 0x01;
+		response[1] = 0xFF;
+		response[2] = 0x00;
+		response[3] = 0x00;
+
+		if (n == sizeof (req_init) && packet[4] == req_init[0]/*memcmp (packet + 4, req_init, sizeof (req_init)) == 0*/) {
+			unsigned char *p = packet + 4;
+			unsigned int compression = p[1];
+			unsigned int address = (p[3] << 24) + (p[4] << 16) + (p[5] << 8) + p[6];
+			unsigned int length = (p[7] << 16) + (p[8] << 8) + p[9];
+
+			message ("address=%08x, size=%06x, compression=%02x\n", address, length, compression);
+
+			if (address == 0xDD000000 && length == 0x020080 && compression == 0x00) {
+				message ("Logbook command.\n");
+				data = pred_buf;
+				size = pred_len;
+				blocksize = 0x82;
+			} else if (address == 0xE0000000 && length == 0x600 && compression == 0x00) {
+				message ("Manifest command.\n");
+				data = manifest_buf;
+				size = manifest_len;
+				blocksize = 0x82;
+			} else if (address & 0xC0000000 && length == 0xFFFFFF && compression == 0x10) {
+				message ("Dive command.\n");
+				data = dive_buf;
+				size = dive_len;
+				blocksize = 0x92;
+			} else {
+				message ("ERROR!\n");
+				continue;
+			}
+
+			response[2] = sizeof (resp_init) + 1;
+			memcpy (response + 4, resp_init, sizeof (resp_init));
+			shearwater_predator_slip_write (device, response, sizeof (resp_init) + 4);
+
+			block = 1;
+			nbytes = 0;
+		} else if (n == sizeof (req_block) && memcmp (packet + 4, req_block, sizeof (req_block) - 1) == 0) {
+			message ("Block command.\n");
+
+			unsigned int len = blocksize - sizeof (resp_block);
+			if (nbytes + len > size) {
+				len = size - nbytes;
+			}
+			//message ("len=%u, size=%u\n", len, size);
+			if (len == 0) {
+				message ("Zero length!\n");
+				continue;
+			}
+
+			resp_block[1] = block;
+			response[2] = sizeof (resp_block) + 1 + len;
+			memcpy (response + 4, resp_block, sizeof (resp_block));
+			memcpy (response + 4 + sizeof (resp_block), data + nbytes, len);
+			shearwater_predator_slip_write (device, response, sizeof (resp_block) + 4 + len);
+
+			block++;
+			nbytes += len;
+		} else if (n == sizeof (req_quit) && memcmp (packet + 4, req_quit, sizeof (req_quit)) == 0) {
+			message ("Quit command.\n");
+			response[2] = sizeof (resp_quit) + 1;
+			memcpy (response + 4, resp_quit, sizeof (resp_quit));
+			shearwater_predator_slip_write (device, response, sizeof (resp_quit) + 4);
+
+			block = 1;
+			nbytes = 0;
+		} else {
+			message ("Unknown command.\n");
+		}
+	}
+}
+
+static void
+simulation_leonardo (serial_t *device, unsigned char data[], unsigned int size)
+{
+
+	const unsigned char req_dump[] = "{123DBA}";
+	const unsigned char resp_dump[] = "{!D5B3}";
+
+	serial_set_rts(device, 1);
+	serial_set_dtr(device, 0);
+
+	unsigned char csum_raw[2], csum_ascii[4];
+	unsigned int csum = checksum_crc_ccitt_uint16 (data, size);
+	csum_raw[0] = (csum	>> 8) & 0xFF;
+	csum_raw[1] = (csum	    ) & 0xFF;
+	mares_puck_convert_binary_to_ascii (csum_raw, sizeof (csum_raw), csum_ascii, sizeof (csum_ascii));
+
+	dc_buffer_t *buffer = dc_buffer_new(0);
+
+	for (;;) {
+
+		unsigned char command[20] = {0};
+
+		unsigned int header = 0, i = 0;
+		for (;;) {
+			unsigned char c = 0;
+			serial_read (device, &c, 1);
+
+			command[i] = c;
+
+			if (c == '{') {
+				header = 1;
+				i = 1;
+			} else if (header) {
+				i++;
+				if (c == '}')
+					break;
+			} else {
+				break;
+			}
+		}
+
+		debug (command, i);
+
+		if (i < 6 || (i % 2) != 0) {
+			message ("Ignoring %u bytes.\n", i);
+			continue;
+		}
+
+		unsigned char raw[sizeof (command)] = {0};
+		mares_puck_convert_ascii_to_binary (command + 1, i - 2, raw, (i - 2) / 2);
+
+		if (i == 8 && memcmp (command, req_dump, sizeof (req_dump) - 1) == 0) {
+			message ("Dump\n");
+			serial_write (device, resp_dump, sizeof (resp_dump) - 1);
+			unsigned int nbytes = 0;
+			while (nbytes < size) {
+				unsigned int len = size - nbytes;
+				if (len > 4096)
+					len = 4096;
+				serial_write (device, data + nbytes, len);
+#ifdef _WIN32
+				serial_sleep (device, 10); /* Cressi application needs this. */
+#endif
+				nbytes += len;
+			}
+			//serial_write (device, data, size);
+			serial_write (device, csum_ascii, sizeof (csum_ascii));
+		} else if (i == 14) {
+			unsigned int address = array_uint16_be (raw + 0);
+			unsigned int length  = array_uint16_be (raw + 2);
+			message ("Read(%04x,%u)\n", address, length);
+
+			dc_buffer_resize(buffer, 1 + length * 2 + 4 + 1);
+			unsigned char *packet = dc_buffer_get_data(buffer);
+			packet[0] = '{';
+			mares_puck_convert_binary_to_ascii(data + address - 0x1000, length, packet + 1, length * 2);
+			unsigned int csum = checksum_crc_ccitt_uint16 (packet + 1, length * 2);
+			csum_raw[0] = (csum	>> 8) & 0xFF;
+			csum_raw[1] = (csum	    ) & 0xFF;
+			mares_puck_convert_binary_to_ascii (csum_raw, sizeof (csum_raw), packet + 1 + length * 2, 4);
+			packet[1 + length * 2 + 4] = '}';
+			serial_write (device, packet, dc_buffer_get_size(buffer));
+		} else {
+			message ("Unknown\n");
+		}
+
+	}
+}
+
+
+static void
+simulation_nitekq (serial_t *device, unsigned char data[], unsigned int size, unsigned char version[])
+{
+
+	const unsigned char header[32] = {0};
+	const unsigned char csum[2] = {0};
+
+	const unsigned int blocksize = 256;
+	unsigned int address = 0;
+	unsigned int block = 0;
+
+	for (;;) {
+		unsigned char c = 0;
+		serial_read (device, &c, 1);
+		message ("Command: %02x \'%c\'\n", c, c);
+
+		switch (c) {
+		case 'H':
+			if (version)
+				serial_write (device, version, sizeof(header));
+			else
+				serial_write (device, header, sizeof(header));
+			break;
+		case 'D':
+			break;
+		case 'R':
+			address = 0;
+			block = 0;
+			break;
+		case 'U':
+			serial_write (device, data, blocksize);
+			serial_write (device, csum, sizeof(csum));
+			break;
+		case 'B':
+			message ("Block: %u %04x\n", block, address);
+			serial_write (device, data + blocksize + address, blocksize);
+			serial_write (device, csum, sizeof(csum));
+			address += blocksize;
+			block++;
+			break;
+		case '<':
+			serial_write (device, &c, 1);
+			break;
+		default:
+			message ("Unknown command (%02x).\n", c);
+		}
+
+
+	}
+
+}
+
+static void
+simulation_hyperaqualand (serial_t *device, unsigned char data[], unsigned int size)
+{
+	const unsigned int blocksize = 32;
+	unsigned int address = 0;
+	unsigned int block = 0;
+
+	//serial_set_timeout(device, 1000);
+
+	for (;;) {
+		unsigned char c = 0;
+		int n = serial_read (device, &c, 1);
+		if (n < 1) {
+			message ("timeout\n");
+			address = 0;
+			block = 0;
+			continue;
+		}
+		message ("Command: %02X\n", c);
+
+		if (c == 0x7F || address + blocksize > size) {
+			message ("Block: reset\n");
+			address = 0;
+			block = 0;
+		} else {
+			message ("Block: %u %04x\n", block, address);
+			serial_write (device, data + address, blocksize);
+			address += blocksize;
+			block++;
+		}
+	}
+
+}
+
+static void
+simulation_idive (serial_t *device, unsigned char data[], unsigned int size, unsigned char version[], unsigned int model)
+{
+	const unsigned char id_idive[] = {
+		0x02, 0x00, 0x6B, 0xFA, 0x32, 0x01, 0x65, 0x04, 0x00, 0x00
+	};
+	const unsigned char id_ix3m[] = {
+		0x24, 0x00, 0x10, 0xD7, 0xC9, 0x01, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x7F, 0x00, 0x00, 0x00, 0xE6, 0x59, 0x82, 0x0E, 0xE6, 0x59, 0x82, 0x0E
+	};
+	unsigned char id[0xFF];
+	unsigned char range[4];
+
+	if (version) {
+		model = array_uint16_le(version);
+	}
+
+	message ("model=0x%02x\n", model);
+
+	unsigned int idsize = 0x0A;
+	unsigned int headersize = 0x32;
+	unsigned int samplesize = 0x2A;
+	if (model >= 0x22 && model <= 0x25) {
+		idsize = 0x1A;
+		headersize = 0x36;
+		samplesize = 0x36;
+	}
+
+	if (version) {
+		memcpy(id, version, idsize);
+	}
+
+	unsigned char packet[0xFF + 4];
+	unsigned int len = 0;
+	int n = 0;
+
+	const unsigned char err[] = {0x60};
+	unsigned int number = 0;
+	unsigned int offset = 0;
+	unsigned int nsamples = 0;
+
+	unsigned int nbytes = 0;
+	unsigned int ndives = 0;
+	while (nbytes + headersize < size) {
+		unsigned int n = array_uint16_le (data + nbytes + 1);
+
+		nbytes += headersize + n * samplesize;
+		ndives++;
+	}
+
+	range[0] = 0x01;
+	range[1] = 0x00;
+	range[2] = (ndives     ) & 0xFF;
+	range[3] = (ndives >> 8) & 0xFF;
+
+	for (;;) {
+		n = serial_read (device, packet + 0, 1);
+		if (n != 1 || packet[0] != 0x55) {
+			continue;
+		}
+
+		serial_read (device, packet + 1, 1);
+		len = packet[1];
+		serial_read (device, packet + 2, len + 2);
+
+		//message ("CMD: %02x\n", packet[2]);
+		unsigned char ack = 0x06;
+
+		switch (packet[2]) {
+		case 0x10: // ID
+		case 0x11: // ID (iX3M)
+			message ("ID\n");
+			len = idsize;
+			memcpy(packet + 3, id, sizeof(id));
+			break;
+		case 0x98: // Range
+		case 0x78: // Range (iX3M)
+			message ("RANGE\n");
+			if (ndives) {
+				len = sizeof(range);
+				memcpy(packet + 3, range, sizeof(range));
+			} else {
+				len = 1;
+				packet[3] = 0x58;
+				ack = 0x15;
+			}
+			break;
+		case 0xA0: // Header
+		case 0x79: // Header (iX3M)
+			number = array_uint16_le(packet + 3);
+			message ("HEADER: %u\n", number);
+			if (number < 1 || number > ndives) {
+				len = 1;
+				packet[3] = 0x30;
+				ack = 0x15;
+			} else {
+				offset = 0;
+				for (unsigned int i = 0; i < ndives - number; ++i) {
+					unsigned int n = array_uint16_le (data + offset + 1);
+					offset += headersize + n * samplesize;
+				}
+				nsamples = array_uint16_le (data + offset + 1);
+				len = headersize;
+				memcpy(packet + 3, data + offset, len);
+			}
+			break;
+		case 0xA8: // Sample
+		case 0x7A: // Sample (iX3M)
+			number= array_uint16_le(packet + 3);
+			message ("SAMPLE: %u\n", number);
+			if (number < 1 || number > nsamples) {
+				len = 1;
+				packet[3] = 0x30;
+				ack = 0x15;
+			} else {
+				len = samplesize;
+				memcpy(packet + 3, data + offset + headersize + samplesize * (number - 1), len);
+			}
+			break;
+		default:
+			message ("UNKNOWN: %02x\n", packet[2]);
+			continue;
+		}
+
+#if 0
+		if (error(0.05)) {
+			message ("NAK\n");
+			len = 1;
+			packet[3] = 0x60;
+			ack = 0x15;
+		}
+#endif
+
+		packet[1] = len + 2;
+		packet[len + 3] = ack;
+		unsigned short crc = checksum_crc_ccitt_uint16 (packet, len + 4);
+		packet[len + 4] = (crc >> 8) & 0xFF;
+		packet[len + 5] = (crc     ) & 0xFF;
+		serial_write (device, packet, len + 6);
+	}
+}
+
+static void
+simulation_cochran (serial_t *device, unsigned char data[], unsigned int size, unsigned char header[])
+{
+	const unsigned int packetsize = 1024;
+	const unsigned char heartbeat[] = {0xAA};
+	unsigned char id[67];
+	unsigned char misc[1500];
+	unsigned char config[4][512];
+
+	unsigned int bits = 32;
+
+	unsigned char command[10];
+	unsigned int csize = 0;
+	unsigned int address = 0;
+	unsigned int length = 0;
+	int n = 0;
+
+	memset(id, 0xFF, sizeof(id));
+	memset(misc, 0xFF, sizeof(misc));
+	memset(config, 0xFF, sizeof(config));
+
+	if (header) {
+		unsigned int offset = 0;
+		memcpy(id, header + offset, sizeof(id));
+		offset += sizeof(id);
+#if 0
+		memcpy(misc, header + offset, sizeof(misc));
+		offset += sizeof(misc);
+		for (unsigned int i = 0; i < 4; ++i) {
+			memcpy(config[i], header + offset, sizeof(config[i]));
+			offset += sizeof(config[i]);
+		}
+#else
+		for (unsigned int i = 0; i < 2; ++i) {
+			memcpy(config[i], header + offset, sizeof(config[i]));
+			offset += sizeof(config[i]);
+		}
+#endif
+	}
+
+	if (memcmp(id + 0x3B, "AM\x11""2212\x02", 8) == 0) {
+		bits = 24;
+	}
+
+	for (;;) {
+		serial_set_timeout(device, 1000);
+		n = serial_read (device, command + 0, 1);
+		if (n != 1) {
+			message ("HEARTBEAT\n");
+			serial_write (device, heartbeat, sizeof(heartbeat));
+			continue;
+		}
+		serial_set_timeout(device, -1);
+
+		switch (command[0]) {
+		case 0x05: // ID
+			csize = 6;
+			serial_read (device, command + 1, csize - 1);
+			message ("ID\n");
+			serial_write (device, id, sizeof(id));
+			break;
+		case 0x96: // Config
+			csize = 2;
+			serial_read (device, command + 1, csize - 1);
+			address = command[1];
+			message ("CONFIG %u\n", address);
+			serial_write (device, config[address], sizeof(config[address]));
+			break;
+		case 0x89: // Misc
+			csize = 7;
+			serial_write (device, heartbeat, sizeof(heartbeat));
+			serial_read (device, command + 1, csize - 1);
+			message ("MISC\n");
+			serial_write (device, misc, sizeof(misc));
+			break;
+		case 0x15: // Read
+			csize = 2 + 2 * (bits / 8);
+			serial_read (device, command + 1, csize - 1);
+			if (bits == 24) {
+				address = array_uint24_le(command + 1);
+				length  = array_uint24_le(command + 4);
+			} else {
+				address = array_uint32_le(command + 1);
+				length  = array_uint32_le(command + 5);
+			}
+			message ("READ(%08x,%u)\n", address, length);
+
+			unsigned int nbytes = 0;
+			while (nbytes < length) {
+				unsigned int len = length - nbytes;
+				if (len > packetsize)
+					len = packetsize;
+				serial_write (device, data + address + nbytes, len);
+				nbytes += len;
+			}
+			break;
+		default:
+			message ("UNKNOWN: %02x\n", command[0]);
+			continue;
+		}
+
+
+	}
+}
+
+
+struct {
+	const char *name;
+	dc_family_t type;
+}
+static const 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},
+	{"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},
+	{"iconhd",		DC_FAMILY_MARES_ICONHD},
+	{"edy",         DC_FAMILY_CRESSI_EDY},
+	{"ostc",        DC_FAMILY_HW_OSTC},
+	{"ostc3",       DC_FAMILY_HW_OSTC3},
+	{"n2ition3",    DC_FAMILY_ZEAGLE_N2ITION3},
+	{"darwin",      DC_FAMILY_MARES_DARWIN},
+	{"predator",    DC_FAMILY_SHEARWATER_PREDATOR},
+	{"leonardo",    DC_FAMILY_CRESSI_LEONARDO},
+	{"nitekq",      DC_FAMILY_DIVERITE_NITEKQ},
+	{"aqualand",    DC_FAMILY_CITIZEN_AQUALAND},
+	{"idive",       DC_FAMILY_DIVESYSTEM_IDIVE},
+	{"cochran",     DC_FAMILY_COCHRAN_COMMANDER},
+};
+
+static dc_family_t
+lookup (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 void
+usage (const char *filename)
+{
+	fprintf (stderr, "Usage:\n\n");
+	fprintf (stderr, "   %s [options] filename\n\n", filename);
+	fprintf (stderr, "Options:\n\n");
+	fprintf (stderr, "   -b backend   Backend type (required)\n");
+	fprintf (stderr, "   -p devname   Serial port (required, except for infrared)\n");
+	fprintf (stderr, "   -d devtime   Device clock\n");
+	fprintf (stderr, "   -s systime   System clock\n");
+	fprintf (stderr, "   -m model     Model number\n");
+	fprintf (stderr, "   -l logfile   Set logfile.\n\n");
+
+	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");
+
+	}
+}
+
+
+static void
+get_default_parameters (dc_family_t backend, unsigned int model, const char **filename, long *systime, long *devtime)
+{
+	switch (backend) {
+	case DC_FAMILY_SUUNTO_SOLUTION:
+		*filename = "../../suunto/data/filip/SOLUTION.DMP";
+		break;
+	case DC_FAMILY_SUUNTO_EON:
+		*filename = "data/eon/nitrox.ebeaule.1.dives.bin";
+		break;
+	case DC_FAMILY_SUUNTO_VYPER:
+		switch (model) {
+		case 0x01: // Spyder
+			*filename = "data/vyper/spyder.roli.bin";
+			break;
+		default:
+			*filename = "data/vyper/vyper.stasia.bin";
+			break;
+		};
+		break;
+	case DC_FAMILY_SUUNTO_VYPER2:
+		*filename = "data/vyper2/cobra2.dusty.bin";
+		break;
+	case DC_FAMILY_SUUNTO_D9:
+		*filename = "data/d9/d9.eric.1.bin";
+		break;
+	case DC_FAMILY_UWATEC_ALADIN:
+		*filename = "data/aladin/aladin.harry.1.bin";
+		break;
+	case DC_FAMILY_UWATEC_MEMOMOUSE:
+		*filename = "data/memomouse/memomouse.svp.1.bin";
+		break;
+	case DC_FAMILY_UWATEC_SMART:
+		switch (model) {
+		case 0x11: // UWATEC Galileo Sol (peter)
+			*filename = "data/smart/galileo.peter.bin";
+			*systime = 1207211639;
+			*devtime = 521154354;
+			break;
+		case 0x18: // Aladin Smart Tec (rob)
+			*filename = "data/smart/smarttec.rob.bin";
+			*systime = 1209342465;
+			*devtime = 0x1f4fb2bf;
+			break;
+		case 0x1C: // Aladin Smart Z (sven)
+			*filename = "data/smart/smartz.sven.bin";
+			*systime = 1213630630;
+			*devtime = 0x1fd28c06;
+			break;
+		default:
+			break;
+		};
+		break;
+	case DC_FAMILY_REEFNET_SENSUS:
+		*filename = "data/sensus/sensus.steffenreith.bin";
+		break;
+	case DC_FAMILY_REEFNET_SENSUSPRO:
+		*filename = "data/sensuspro/sensuspro.reefnet.bin";
+		break;
+	case DC_FAMILY_REEFNET_SENSUSULTRA:
+		*filename = "data/sensusultra/sensusultra.reefnet.bin";
+		break;
+	case DC_FAMILY_OCEANIC_VTPRO:
+		*filename = "data/vtpro/proplus2.gerbrand.1.bin";
+		break;
+	case DC_FAMILY_OCEANIC_VEO250:
+		*filename = "data/veo250/veo250.jerry.1a.bin";
+		break;
+	case DC_FAMILY_OCEANIC_ATOM2:
+		*filename = "data/atom2/atom2.michael.1a.bin";
+		break;
+	case DC_FAMILY_MARES_NEMO:
+		*filename = "data/nemo/nemo.teemu.1.bin";
+		break;
+	case DC_FAMILY_MARES_PUCK:
+		*filename = "data/puck/puck.tom.1.bin";
+		break;
+	case DC_FAMILY_MARES_ICONHD:
+		*filename = "data/iconhd/iconhd.exposure.1.bin";
+		break;
+	case DC_FAMILY_HW_OSTC:
+		*filename = "data/ostc/ostc.artur.01.bin";
+		break;
+	case DC_FAMILY_CRESSI_EDY:
+		*filename = "data/edy/edy.gianpaolo.bin";
+		break;
+	default:
+		break;
+	}
+}
+
+static dc_buffer_t *
+readfile (const char *filename)
+{
+	// Open the file for reading.
+	FILE *fp = fopen (filename, "rb");
+	if (fp == NULL)
+		return NULL;
+
+	// Allocate an empty buffer.
+	dc_buffer_t *buffer = dc_buffer_new (0);
+
+	// Read the file contents.
+	while (!feof (fp)) {
+		unsigned char block[1024];
+		size_t n = fread (block, 1, sizeof (block), fp);
+
+		dc_buffer_append (buffer, block, n);
+	}
+
+	// Close the file.
+	fclose (fp);
+
+	return buffer;
+}
+
+static dc_ticks_t
+readtime (const char *filename)
+{
+	dc_ticks_t ticks = 0;
+
+	// Open the file for reading.
+	FILE *fp = fopen (filename, "rb");
+	if (fp == NULL)
+		return ticks;
+
+	// Allocate an empty buffer.
+	unsigned char buffer[8] = {0};
+
+	// Read the file contents.
+	size_t n = fread (buffer, 1, sizeof (buffer), fp);
+	if (n != sizeof (buffer))
+		return ticks;
+
+	// Close the file.
+	fclose (fp);
+
+	ticks += array_uint32_be (buffer);
+	ticks <<= 32;
+	ticks += array_uint32_be (buffer + 4);
+
+	return ticks;
+}
+
+int
+main (int argc, char *argv[])
+{
+	// Default values.
+	long systime = 0, devtime = 0;
+	unsigned int model = 0, serial = 0;
+	unsigned int realistic = 0;
+	const char *logfile = NULL;
+	const char *filename = NULL;
+	const char *devname = NULL;
+	dc_family_t backend = DC_FAMILY_NULL;
+	unsigned int delay = 0;
+	dc_loglevel_t loglevel = DC_LOGLEVEL_WARNING;
+
+	// Parse command-line options.
+	int opt = 0;
+	while ((opt = getopt (argc, argv, "b:m:s:d:p:l:w:rvh")) != -1) {
+		switch (opt) {
+		case 'b':
+			backend = lookup (optarg);
+			break;
+		case 'm':
+			model = strtol (optarg, NULL, 0);
+			break;
+		case 's':
+			systime = strtol (optarg, NULL, 0);
+			break;
+		case 'd':
+			devtime = strtol (optarg, NULL, 0);
+			break;
+		case 'p':
+			devname = optarg;
+			break;
+		case 'l':
+			logfile = optarg;
+			break;
+		case 'w':
+			delay = strtol (optarg, NULL, 0);
+			break;
+		case 'r':
+			realistic = 1;
+			break;
+		case 'v':
+			loglevel++;
+			break;
+		case 'h':
+			usage (argv[0]);
+			return EXIT_SUCCESS;
+		default:
+			usage (argv[0]);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (optind < argc)
+		filename = argv[optind];
+
+	if (backend == DC_FAMILY_NULL) {
+		usage (argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	if (filename == NULL)
+		get_default_parameters (backend, model, &filename, &systime, &devtime);
+
+	message_set_logfile (logfile);
+
+	message ("CONFIGURATION:\n");
+	message ("backend=%u\n", backend);
+	message ("devname=%s\n", devname);
+	message ("devtime=%u\n", devtime);
+	message ("systime=%u\n", systime);
+	message ("model=%u\n", model);
+	message ("filename=%s\n", filename);
+
+	// Read the input file.
+
+	unsigned int size = MAXSIZE;
+	unsigned char *data = malloc (size * sizeof (unsigned char));
+	memset (data, 0xFF, size);
+
+	FILE *fp = fopen (filename, "rb");
+	if (fp != NULL) {
+		size = fread (data, sizeof (unsigned char), size, fp);
+		fclose (fp);
+	} else {
+		message ("Failed to open the file.\n");
+		return EXIT_FAILURE;
+	}
+
+	message ("size=%u\n", size);
+
+if (backend == DC_FAMILY_REEFNET_SENSUSULTRA &&
+	size < REEFNET_SENSUSULTRA_MEMORY_DATA_SIZE)
+{
+	message ("padding=%u\n", REEFNET_SENSUSULTRA_MEMORY_DATA_SIZE - size);
+	memmove (data + REEFNET_SENSUSULTRA_MEMORY_DATA_SIZE - size, data, size);
+	memset (data, 0xFF, REEFNET_SENSUSULTRA_MEMORY_DATA_SIZE - size);
+	size = REEFNET_SENSUSULTRA_MEMORY_DATA_SIZE;
+}
+
+	// Read the header file.
+
+	char hdrfile[1024] = {0};
+	snprintf (hdrfile, sizeof (hdrfile), "%s.header", filename);
+	dc_buffer_t *header = readfile (hdrfile);
+	message ("header=%s, size=%u\n", hdrfile, dc_buffer_get_size (header));
+
+	// Read the logbook file.
+
+	char logbookfile[1024] = {0};
+	snprintf (logbookfile, sizeof (logbookfile), "%s.logbook", filename);
+	dc_buffer_t *logbook = readfile (logbookfile);
+	message ("logbook=%s, size=%u\n", logbookfile, dc_buffer_get_size (logbook));
+
+	// Read the systime file.
+
+	char systimefile[1024] = {0};
+	snprintf (systimefile, sizeof (systimefile), "%s.systime", filename);
+	dc_ticks_t ticks = readtime (systimefile);
+	message ("systime=%s, ticks=%llu\n", systimefile, ticks);
+	if (ticks) systime = ticks;
+
+	// Open the serial port.
+
+	dc_context_t *context = NULL;
+	serial_t *device = NULL;
+
+	dc_context_new(&context);
+	dc_context_set_loglevel(context, loglevel);
+
+if (backend != DC_FAMILY_UWATEC_SMART) {
+	int rc = serial_open (&device, context, devname);
+	if (rc == -1) {
+		//message("Error: %i (%s)\n", serial_errcode (), serial_errmsg ());
+		return 1;
+	}
+
+	serial_set_timeout (device, -1);
+	serial_set_halfduplex (device, realistic);
+	message ("lines=%u %u\n", serial_get_received (device), serial_get_transmitted (device));
+	serial_flush (device, SERIAL_QUEUE_BOTH);
+
+	switch (backend) {
+	case DC_FAMILY_CRESSI_EDY:
+		serial_configure (device, 1200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_SUUNTO_SOLUTION:
+	case DC_FAMILY_SUUNTO_EON:
+		serial_configure (device, 1200, 8, SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_SUUNTO_VYPER:
+	case DC_FAMILY_UWATEC_ALADIN:
+		serial_configure (device, 2400, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_ZEAGLE_N2ITION3:
+		serial_configure (device, 4800, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_SUUNTO_VYPER2:
+	case DC_FAMILY_SUUNTO_D9:
+	case DC_FAMILY_UWATEC_MEMOMOUSE:
+	case DC_FAMILY_OCEANIC_VTPRO:
+	case DC_FAMILY_OCEANIC_VEO250:
+	case DC_FAMILY_MARES_NEMO:
+	case DC_FAMILY_MARES_DARWIN:
+	case DC_FAMILY_DIVERITE_NITEKQ:
+		serial_configure (device, 9600, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_COCHRAN_COMMANDER:
+		serial_configure (device, 9600, 8, SERIAL_PARITY_NONE, 2, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_REEFNET_SENSUS:
+	case DC_FAMILY_REEFNET_SENSUSPRO:
+		serial_configure (device, 19200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_OCEANIC_ATOM2:
+	case DC_FAMILY_MARES_PUCK:
+		serial_configure (device, 38400, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_UWATEC_MERIDIAN:
+		serial_configure (device, 57600, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_REEFNET_SENSUSULTRA:
+	case DC_FAMILY_HW_OSTC:
+	case DC_FAMILY_HW_OSTC3:
+	case DC_FAMILY_SHEARWATER_PREDATOR:
+	case DC_FAMILY_CRESSI_LEONARDO:
+	case DC_FAMILY_DIVESYSTEM_IDIVE:
+		serial_configure (device, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	case DC_FAMILY_MARES_ICONHD:
+		serial_configure (device, 256000, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
+		break;
+	default:
+		break;
+	}
+}
+
+	if (delay) serial_sleep(device, delay);
+
+	unsigned char *version = NULL;
+	unsigned char version_d9[4] = {0x0E, 0x01, 0x02, 0x0F};
+	unsigned char version_d6[4] = {0x0F, 0x01, 0x00, 0x01};
+	unsigned char version_v2[4] = {0x10, 0x01, 0x00, 0x04};
+	unsigned char version_c2[4] = {0x11, 0x01, 0x00, 0x04};
+	unsigned char version_d4[4] = {0x12, 0x01, 0x00, 0x01};
+	unsigned char version_va[4] = {0x13, 0x01, 0x00, 0x01}; // Vyper Air
+	unsigned char version_c3[4] = {0x14, 0x01, 0x00, 0x01}; // Cobra 3
+	unsigned char version_he[4] = {0x15, 0x01, 0x00, 0x04}; // HelO2
+
+	unsigned char version_geo[16] = {'O', 'C', 'E', ' ', 'G', 'E', 'O', ' ', 'R', '1', 'D', ' ', '5', '1', '2', 'K'};
+	unsigned char version_vt3[16] = {'O', 'C', 'E', ' ', 'V', 'T', '3', ' ', 'R', '3', 'F', ' ', '5', '1', '2', 'K'};
+	unsigned char version_datamask[16] = {'D', 'A', 'T', 'A', 'M', 'A', 'S', 'K', ' ', '2', 'B', ' ', '5', '1', '2', 'K'};
+	unsigned char version_atom2[16] = {'2', 'M', ' ', 'A', 'T', 'O', 'M', ' ', 'r', '3', 'C', ' ', '5', '1', '2', 'K'};
+	unsigned char version_veo200[16] = {'V', 'E', 'O', ' ', '2', '0', '0', ' ', 'R', '2', 'A', ' ', '2', '5', '6', 'K'};
+	unsigned char version_veo250[16] = {'V', 'E', 'O', ' ', '2', '5', '0', ' ', 'R', '2', 'A', ' ', '2', '5', '6', 'K'};
+	unsigned char version_veo180nx[16] = {0x56, 0x45, 0x4F, 0x20, 0x32, 0x35, 0x30, 0x20, 0x52, 0x32, 0x42, 0x20, 0x32, 0x35, 0x36, 0x4B};
+	//unsigned char version_proplus2[16] = {0x50, 0x52, 0x4F, 0x50, 0x4C, 0x55, 0x53, 0x32, 0x20, 0x32, 0x41, 0x20, 0x32, 0x35, 0x36, 0x4B};
+	unsigned char version_vtpro[16] = {0x56, 0x54, 0x50, 0x52, 0x4F, 0x20, 0x20, 0x72, 0x31, 0x41, 0x20, 0x20, 0x32, 0x35, 0x36, 0x4B};
+	/*unsigned char version_atmosai[16] = {0x41, 0x54, 0x4D, 0x4F, 0x53, 0x41, 0x49, 0x52, 0x20, 0x32, 0x41, 0x20, 0x32, 0x35, 0x36, 0x4B};
+	unsigned char version_f10[16] = {'F', 'R', 'E', 'E', 'W', 'A', 'E', 'R', ' ', '1', 'A', ' ', '5', '1', '2', 'K'};
+	unsigned char version_oc1[16] = {'O', 'C', 'W', 'A', 'T', 'C', 'H', ' ', 'R', '1', 'A', ' ', '1', '0', '2', '4'};
+	unsigned char version_geo2[16] = "OCEGEO20 1A 512K";*/
+	unsigned char version_a300cs[16] = { 'A', 'E', 'R', '3', '0', '0', 'C', 'S', ' ', '1', 'F', ' ', '2', '0', '4', '8' };
+
+	// Run the simulation.
+	switch (backend) {
+	case DC_FAMILY_SUUNTO_SOLUTION:
+		simulation_solution (device, data, size);
+		break;
+	case DC_FAMILY_SUUNTO_EON:
+		simulation_eon (device, data, size);
+		break;
+	case DC_FAMILY_SUUNTO_VYPER:
+		simulation_vyper (device, data, size);
+		break;
+	case DC_FAMILY_SUUNTO_VYPER2:
+		switch (model) {
+		case 0x10: // Vyper 2
+			version = version_v2;
+			break;
+		case 0x11: // Cobra 2
+			version = version_c2;
+			break;
+		case 0x13: // Vyper Air
+			version = version_va;
+			break;
+		case 0x14: // Cobra 3
+			version = version_c3;
+			break;
+		case 0x15: // Hel02
+			version = version_he;
+			break;
+		default:
+			version = version_v2;
+			break;
+		}
+		if (dc_buffer_get_size (header) == 4)
+			version = dc_buffer_get_data (header);
+		simulation_vyper2_or_d9 (device, data, size, version, 1);
+		break;
+	case DC_FAMILY_SUUNTO_D9:
+		version = version_d9;
+		if (data[0x01] == 'D' && data[0x02] == '9')
+			version = version_d9;
+		else if (data[0x01] == 'D' && data[0x02] == '6')
+			version = version_d6;
+		else if (data[0x01] == 'D' && data[0x02] == '4')
+			version = version_d4;
+		if (dc_buffer_get_size (header) == 4)
+			version = dc_buffer_get_data (header);
+		simulation_vyper2_or_d9 (device, data, size, version, 0);
+		break;
+	case DC_FAMILY_UWATEC_ALADIN:
+		simulation_aladin (device, data, size, devtime, systime);
+		break;
+	case DC_FAMILY_UWATEC_MEMOMOUSE:
+		simulation_memomouse (device, data, size, devtime, systime);
+		break;
+	case DC_FAMILY_UWATEC_SMART:
+		if (dc_buffer_get_size (header) == 9) {
+			const unsigned char *hdr = dc_buffer_get_data (header);
+			model = hdr[0];
+			serial = array_uint32_le (hdr + 1);
+			if (systime)
+				devtime = array_uint32_le (hdr + 5);
+			message ("model=0x%02x, serial=0x%08x, devtime=0x%08x\n",
+				model, serial, devtime);
+		}
+		simulation_smart (data, size, model, serial, devtime, systime);
+		break;
+	case DC_FAMILY_UWATEC_MERIDIAN:
+		if (dc_buffer_get_size (header) == 9) {
+			const unsigned char *hdr = dc_buffer_get_data (header);
+			model = hdr[0];
+			serial = array_uint32_le (hdr + 1);
+			if (systime)
+				devtime = array_uint32_le (hdr + 5);
+			message ("model=0x%02x, serial=0x%08x, devtime=0x%08x\n",
+				model, serial, devtime);
+		}
+		simulation_meridian (device, data, size, model, serial, devtime, systime);
+		break;
+	case DC_FAMILY_REEFNET_SENSUS:
+		simulation_sensus (device, data, size, devtime, systime);
+		break;
+	case DC_FAMILY_REEFNET_SENSUSPRO:
+		version = NULL;
+		if (dc_buffer_get_size (header) == 10)
+			version = dc_buffer_get_data (header);
+		simulation_sensuspro (device, data, size, version, devtime, systime);
+		break;
+	case DC_FAMILY_REEFNET_SENSUSULTRA:
+		version = NULL;
+		if (dc_buffer_get_size (header) == 24)
+			version = dc_buffer_get_data (header);
+		simulation_sensusultra (device, data, size, version, devtime, systime);
+		break;
+	case DC_FAMILY_OCEANIC_VTPRO:
+		#if defined (PROPLUS2)
+			version = version_proplus2;
+		#else
+			version = version_vtpro;
+			//version = version_atmosai;
+		#endif
+		if (dc_buffer_get_size (header) == 16)
+			version = dc_buffer_get_data (header);
+		simulation_vtpro (device, data, size, version, logbook);
+		break;
+	case DC_FAMILY_OCEANIC_VEO250:
+		switch (model) {
+		case 250: // Veo 250
+			version = version_veo250;
+			break;
+		case 200: // Veo 200
+			version = version_veo200;
+			break;
+		case 180: // Veo 180
+			version = version_veo180nx;
+			break;
+		default:
+			version = version_veo250;
+			break;
+		}
+		if (dc_buffer_get_size (header) == 16)
+			version = dc_buffer_get_data (header);
+		simulation_veo250 (device, data, size, version, 16);
+		break;
+	case DC_FAMILY_OCEANIC_ATOM2:
+		switch (model) {
+		case 0x4342: // Atom 2
+			version = version_atom2;
+			break;
+		case 0x4256: // VT3
+		case 0x4258: // VT3
+			version = version_vt3;
+			break;
+		case 0x4344: // GEO
+			version = version_geo;
+			break;
+		case 0x4347: // Datamask
+			version = version_datamask;
+			break;
+		case 0x454C: // A300CS
+			version = version_a300cs;
+			break;
+		default:
+			version = version_atom2;
+			break;
+		}
+		if (dc_buffer_get_size (header) == 16)
+			version = dc_buffer_get_data (header);
+		simulation_atom2 (device, data, size, version);
+		break;
+	case DC_FAMILY_MARES_NEMO:
+		if (size == MARES_NEMO_MEMORY_SIZE)
+			simulation_nemo (device, data, size);
+		else
+			simulation_raw (device, data, size);
+		break;
+	case DC_FAMILY_MARES_PUCK:
+		simulation_puck (device, data, size, 0);
+		break;
+	case DC_FAMILY_MARES_DARWIN:
+		simulation_puck (device, data, size, 1);
+		break;
+	case DC_FAMILY_MARES_ICONHD:
+		if (dc_buffer_get_size (header) == 140)
+			version = dc_buffer_get_data (header);
+		simulation_iconhd (device, data, size, version);
+		break;
+	case DC_FAMILY_CRESSI_EDY:
+		if (dc_buffer_get_size (header) == 1)
+			version = dc_buffer_get_data (header);
+		simulation_seiko (device, data, size, version);
+		break;
+	case DC_FAMILY_HW_OSTC:
+		simulation_ostc (device, data, size);
+		break;
+	case DC_FAMILY_HW_OSTC3:
+		if (header == NULL) {
+			unsigned char hwid = model;
+			header = dc_buffer_new(1);
+			dc_buffer_append (header, &hwid, 1);
+		}
+		simulation_ostc3 (device, data, size, dc_buffer_get_data (header), dc_buffer_get_size (header));
+		break;
+	case DC_FAMILY_ZEAGLE_N2ITION3:
+		simulation_zeagle (device, data, size);
+		break;
+	case DC_FAMILY_SHEARWATER_PREDATOR:
+		simulation_predator (device, data, size);
+		break;
+	case DC_FAMILY_CRESSI_LEONARDO:
+		simulation_leonardo (device, data, size);
+		break;
+	case DC_FAMILY_DIVERITE_NITEKQ:
+		version = NULL;
+		if (dc_buffer_get_size (header) == 32)
+			version = dc_buffer_get_data (header);
+		simulation_nitekq (device, data, size, version);
+		break;
+	case DC_FAMILY_CITIZEN_AQUALAND:
+		simulation_hyperaqualand (device, data, size);
+		break;
+	case DC_FAMILY_DIVESYSTEM_IDIVE:
+		version = dc_buffer_get_data (header);
+		simulation_idive (device, data, size, version, model);
+		break;
+	case DC_FAMILY_COCHRAN_COMMANDER:
+		version = dc_buffer_get_data (header);
+		simulation_cochran (device, data, size, version);
+		break;
+	default:
+		break;
+	}
+
+	if (backend != DC_FAMILY_UWATEC_SMART)
+		serial_close (device);
+
+	free (data);
+	dc_buffer_free (header);
+	dc_buffer_free (logbook);
+
+	message_set_logfile (NULL);
+
+	return EXIT_SUCCESS;
+}
diff --git a/examples/utils.c b/examples/utils.c
index 9d0768f..d36a6d1 100644
--- a/examples/utils.c
+++ b/examples/utils.c
@@ -70,6 +70,7 @@ int message (const char* fmt, ...)
 		va_start (ap, fmt);
 		vfprintf (g_logfile, fmt, ap);
 		va_end (ap);
+		fflush (g_logfile);
 	}
 
 	va_start (ap, fmt);
diff --git a/src/Makefile.am b/src/Makefile.am
index 1f52841..4cb258d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,8 +6,7 @@ lib_LTLIBRARIES = libdivecomputer.la
 libdivecomputer_la_LIBADD = $(LIBUSB_LIBS)
 libdivecomputer_la_LDFLAGS = \
 	-version-info $(DC_VERSION_LIBTOOL) \
-	-no-undefined \
-	-export-symbols libdivecomputer.exp
+	-no-undefined
 
 libdivecomputer_la_SOURCES = \
 	version.c \
diff --git a/src/irda.c b/src/irda.c
index b15d09f..55c5b93 100644
--- a/src/irda.c
+++ b/src/irda.c
@@ -362,6 +362,108 @@ irda_socket_connect_lsap (irda_t *device, unsigned int address, unsigned int lsa
 
 
 int
+irda_socket_bind_name (irda_t *device, const char *name)
+{
+	if (device == NULL)
+		return -1;
+
+#ifdef _WIN32
+	SOCKADDR_IRDA peer = {AF_IRDA, 0, 0, 0, 0, 0};
+	peer.irdaAddressFamily = AF_IRDA;
+	if (name)
+		strncpy (peer.irdaServiceName, name, 25);
+	else
+		memset (peer.irdaServiceName, 0x00, 25);
+#else
+	struct sockaddr_irda peer = {0};
+	peer.sir_family = AF_IRDA;
+	peer.sir_lsap_sel = LSAP_ANY;
+	if (name)
+		strncpy (peer.sir_name, name, 25);
+	else
+		memset (peer.sir_name, 0x00, 25);
+#endif
+
+	if (bind (device->fd, (struct sockaddr *) &peer, sizeof (peer)) != 0) {
+		SYSERROR (device->context, ERRNO);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int
+irda_socket_bind_lsap (irda_t *device, unsigned int lsap)
+{
+	if (device == NULL)
+		return -1;
+
+#ifdef _WIN32
+	SOCKADDR_IRDA peer = {AF_IRDA, 0, 0, 0, 0, 0};
+	peer.irdaAddressFamily = AF_IRDA;
+	snprintf (peer.irdaServiceName, 25, "LSAP-SEL%u", lsap);
+#else
+	struct sockaddr_irda peer = {0};
+	peer.sir_family = AF_IRDA;
+	peer.sir_lsap_sel = lsap;
+	memset (peer.sir_name, 0x00, 25);
+#endif
+
+	if (bind (device->fd, (struct sockaddr *) &peer, sizeof (peer)) != 0) {
+		SYSERROR (device->context, ERRNO);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int
+irda_socket_listen (irda_t *device, int backlog)
+{
+	if (device == NULL)
+		return -1;
+
+	if (listen (device->fd, backlog) != 0) {
+		SYSERROR (device->context, ERRNO);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int
+irda_socket_accept (irda_t *device)
+{
+#ifdef _WIN32
+	SOCKADDR_IRDA peer = {0};
+	int size = sizeof (peer);
+#else
+	struct sockaddr_irda peer = {0};
+	socklen_t size = sizeof (peer);
+#endif
+
+	int rc = accept (device->fd, (struct sockaddr *) &peer, &size);
+	if (rc == -1) {
+		SYSERROR (device->context, ERRNO);
+		return -1;
+	}
+/*
+#ifdef _WIN32
+	closesocket (device->fd);
+#else
+	close (device->fd);
+#endif
+*/
+
+	device->fd = rc;
+	return 0;
+}
+
+
+int
 irda_socket_available (irda_t *device)
 {
 	if (device == NULL)
diff --git a/src/irda.h b/src/irda.h
index d83037a..5f9f10c 100644
--- a/src/irda.h
+++ b/src/irda.h
@@ -45,6 +45,13 @@ int irda_socket_connect_lsap (irda_t *device, unsigned int address, unsigned int
 
 int irda_socket_available (irda_t *device);
 
+int irda_socket_bind_name (irda_t *device, const char *name);
+int irda_socket_bind_lsap (irda_t *device, unsigned int lsap);
+
+int irda_socket_listen (irda_t *device, int backlog);
+
+int irda_socket_accept (irda_t *device);
+
 int irda_socket_read (irda_t *device, void *data, unsigned int size);
 
 int irda_socket_write (irda_t *device, const void *data, unsigned int size);
diff --git a/src/irda_dummy.c b/src/irda_dummy.c
index 504aee8..c69d125 100644
--- a/src/irda_dummy.c
+++ b/src/irda_dummy.c
@@ -67,6 +67,34 @@ irda_socket_connect_lsap (irda_t *device, unsigned int address, unsigned int lsa
 
 
 int
+irda_socket_bind_name (irda_t *device, const char *name)
+{
+	return -1;
+}
+
+
+int
+irda_socket_bind_lsap (irda_t *device, unsigned int lsap)
+{
+	return -1;
+}
+
+
+int
+irda_socket_listen (irda_t *device, int backlog)
+{
+	return -1;
+}
+
+
+int
+irda_socket_accept (irda_t *device)
+{
+	return -1;
+}
+
+
+int
 irda_socket_available (irda_t *device)
 {
 	return -1;
diff --git a/src/serial.h b/src/serial.h
index 5a9d8ea..4b0c1e2 100644
--- a/src/serial.h
+++ b/src/serial.h
@@ -111,6 +111,8 @@ int serial_get_transmitted (serial_t *device);
 
 int serial_get_line (serial_t *device, int line);
 
+int serial_wait_break (serial_t *device);
+
 int serial_sleep (serial_t *device, unsigned long timeout /* milliseconds */);
 
 #ifdef __cplusplus
diff --git a/src/serial_posix.c b/src/serial_posix.c
index 2b0c3fa..125c809 100644
--- a/src/serial_posix.c
+++ b/src/serial_posix.c
@@ -874,6 +874,15 @@ serial_get_line (serial_t *device, int line)
 	return 0;
 }
 
+int
+serial_wait_break (serial_t *device)
+{
+	if (device == NULL)
+		return -1;
+
+	return 0;
+}
+
 
 int
 serial_sleep (serial_t *device, unsigned long timeout)
diff --git a/src/serial_win32.c b/src/serial_win32.c
index 9d5572c..3424064 100644
--- a/src/serial_win32.c
+++ b/src/serial_win32.c
@@ -503,6 +503,34 @@ serial_flush (serial_t *device, int queue)
 
 
 int
+serial_wait_break (serial_t *device)
+{
+	if (device == NULL)
+		return -1;
+
+	if (!SetCommMask (device->hFile, EV_BREAK)) {
+		SYSERROR (device->context, GetLastError ());
+		return -1;
+	}
+
+	DWORD mask = 0;
+	while (WaitCommEvent (device->hFile, &mask, NULL)) {
+		message ("event=%i\n", mask);
+		if (mask & EV_BREAK) {
+			SetCommMask (device->hFile, 0);
+			return 0;
+		}
+	}
+
+	if (!SetCommMask (device->hFile, 0)) {
+		SYSERROR (device->context, GetLastError ());
+		return -1;
+	}
+
+	return -1;
+}
+
+int
 serial_send_break (serial_t *device)
 {
 	if (device == NULL)
