From 6abfa9792d345e3f27b7babfacad86c004077ecf Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 30 Mar 2009 20:30:32 +0000 Subject: [PATCH] Added the initial implementation for the Mares Puck. --- examples/Makefile.am | 5 +- examples/mares_puck_test.c | 123 +++++++++++++ src/Makefile.am | 4 +- src/device.h | 3 +- src/libdivecomputer.symbols | 1 + src/mares.h | 1 + src/mares_puck.c | 347 ++++++++++++++++++++++++++++++++++++ src/mares_puck.h | 41 +++++ 8 files changed, 522 insertions(+), 3 deletions(-) create mode 100644 examples/mares_puck_test.c create mode 100644 src/mares_puck.c create mode 100644 src/mares_puck.h diff --git a/examples/Makefile.am b/examples/Makefile.am index 7419b27..34c1bbd 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -15,7 +15,8 @@ bin_PROGRAMS = \ atom2 \ veo250 \ vtpro \ - nemo + nemo \ + puck if IRDA bin_PROGRAMS += smart @@ -49,6 +50,8 @@ vtpro_SOURCES = oceanic_vtpro_test.c nemo_SOURCES = mares_nemo_test.c +puck_SOURCES = mares_puck_test.c + if IRDA smart_SOURCES = uwatec_smart_test.c endif diff --git a/examples/mares_puck_test.c b/examples/mares_puck_test.c new file mode 100644 index 0000000..394ba22 --- /dev/null +++ b/examples/mares_puck_test.c @@ -0,0 +1,123 @@ +/* + * libdivecomputer + * + * Copyright (C) 2009 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include // fopen, fwrite, fclose + +#include "mares_puck.h" +#include "utils.h" + +#define WARNING(expr) \ +{ \ + message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \ +} + +device_status_t +test_dump_memory (const char* name, const char* filename) +{ + device_t *device = NULL; + unsigned char data[MARES_PUCK_MEMORY_SIZE] = {0}; + + message ("mares_puck_device_open\n"); + device_status_t rc = mares_puck_device_open (&device, name); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Error opening serial port."); + return rc; + } + + message ("device_dump\n"); + unsigned int nbytes = 0; + rc = device_dump (device, data, sizeof (data), &nbytes); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read memory."); + device_close (device); + return rc; + } + + message ("Dumping data\n"); + FILE* fp = fopen (filename, "wb"); + if (fp != NULL) { + fwrite (data, sizeof (unsigned char), nbytes, fp); + fclose (fp); + } + + message ("device_close\n"); + rc = device_close (device); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot close device."); + return rc; + } + + return DEVICE_STATUS_SUCCESS; +} + + +const char* +errmsg (device_status_t rc) +{ + switch (rc) { + case DEVICE_STATUS_SUCCESS: + return "Success"; + case DEVICE_STATUS_UNSUPPORTED: + return "Unsupported operation"; + case DEVICE_STATUS_TYPE_MISMATCH: + return "Device type mismatch"; + case DEVICE_STATUS_ERROR: + return "Generic error"; + case DEVICE_STATUS_IO: + return "Input/output error"; + case DEVICE_STATUS_MEMORY: + return "Memory error"; + case DEVICE_STATUS_PROTOCOL: + return "Protocol error"; + case DEVICE_STATUS_TIMEOUT: + return "Timeout"; + default: + return "Unknown error"; + } +} + + +int main(int argc, char *argv[]) +{ + message_set_logfile ("PUCK.LOG"); + +#ifdef _WIN32 + const char* name = "COM1"; +#else + const char* name = "/dev/ttyS0"; +#endif + + if (argc > 1) { + name = argv[1]; + } + + message ("DEVICE=%s\n", name); + + device_status_t a = test_dump_memory (name, "PUCK.DMP"); + + message ("SUMMARY\n"); + message ("-------\n"); + message ("test_dump_memory: %s\n", errmsg (a)); + + message_set_logfile (NULL); + + return 0; +} diff --git a/src/Makefile.am b/src/Makefile.am index 766850c..ca3cf0d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,7 +27,8 @@ libdivecomputer_HEADERS = \ oceanic_veo250.h \ oceanic_vtpro.h \ mares.h \ - mares_nemo.h + mares_nemo.h \ + mares_puck.h # # Source files. @@ -62,6 +63,7 @@ libdivecomputer_la_SOURCES = \ oceanic_vtpro.h oceanic_vtpro.c \ mares.h \ mares_nemo.h mares_nemo.c mares_nemo_parser.c \ + mares_puck.h mares_puck.c \ ringbuffer.h ringbuffer.c \ checksum.h checksum.c \ array.h array.c \ diff --git a/src/device.h b/src/device.h index 2e07721..58c690e 100644 --- a/src/device.h +++ b/src/device.h @@ -42,7 +42,8 @@ typedef enum device_type_t { DEVICE_TYPE_OCEANIC_ATOM2, DEVICE_TYPE_OCEANIC_VEO250, DEVICE_TYPE_OCEANIC_VTPRO, - DEVICE_TYPE_MARES_NEMO + DEVICE_TYPE_MARES_NEMO, + DEVICE_TYPE_MARES_PUCK } device_type_t; typedef enum device_status_t { diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 0437129..f3e766b 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -34,6 +34,7 @@ message_set_logfile #ifdef SERIAL mares_nemo_device_open mares_nemo_extract_dives +mares_puck_device_open oceanic_atom2_device_open oceanic_atom2_device_keepalive oceanic_veo250_device_open diff --git a/src/mares.h b/src/mares.h index a3fd4f5..a19bd84 100644 --- a/src/mares.h +++ b/src/mares.h @@ -23,5 +23,6 @@ #define MARES_H #include "mares_nemo.h" +#include "mares_puck.h" #endif /* MARES_H */ diff --git a/src/mares_puck.c b/src/mares_puck.c new file mode 100644 index 0000000..3f913f4 --- /dev/null +++ b/src/mares_puck.c @@ -0,0 +1,347 @@ +/* + * libdivecomputer + * + * Copyright (C) 2009 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include // memcpy, memcmp +#include // malloc, free +#include // assert + +#include "device-private.h" +#include "mares_puck.h" +#include "serial.h" +#include "utils.h" +#include "checksum.h" +#include "array.h" + +#define WARNING(expr) \ +{ \ + message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \ +} + +#define EXITCODE(rc) \ +( \ + rc == -1 ? DEVICE_STATUS_IO : DEVICE_STATUS_TIMEOUT \ +) + +#define MAXRETRIES 4 + +typedef struct mares_puck_device_t mares_puck_device_t; + +struct mares_puck_device_t { + device_t base; + struct serial *port; +}; + +static device_status_t mares_puck_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); +static device_status_t mares_puck_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result); +static device_status_t mares_puck_device_close (device_t *abstract); + +static const device_backend_t mares_puck_device_backend = { + DEVICE_TYPE_MARES_PUCK, + NULL, /* set_fingerprint */ + NULL, /* handshake */ + NULL, /* version */ + mares_puck_device_read, /* read */ + NULL, /* write */ + mares_puck_device_dump, /* dump */ + NULL, /* foreach */ + mares_puck_device_close /* close */ +}; + +static int +device_is_mares_puck (device_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &mares_puck_device_backend; +} + + +device_status_t +mares_puck_device_open (device_t **out, const char* name) +{ + if (out == NULL) + return DEVICE_STATUS_ERROR; + + // Allocate memory. + mares_puck_device_t *device = (mares_puck_device_t *) malloc (sizeof (mares_puck_device_t)); + if (device == NULL) { + WARNING ("Failed to allocate memory."); + return DEVICE_STATUS_MEMORY; + } + + // Initialize the base class. + device_init (&device->base, &mares_puck_device_backend); + + // Set the default values. + device->port = NULL; + + // Open the device. + int rc = serial_open (&device->port, name); + if (rc == -1) { + WARNING ("Failed to open the serial port."); + free (device); + return DEVICE_STATUS_IO; + } + + // Set the serial communication protocol (38400 8N1). + rc = serial_configure (device->port, 38400, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE); + if (rc == -1) { + WARNING ("Failed to set the terminal attributes."); + serial_close (device->port); + free (device); + return DEVICE_STATUS_IO; + } + + // Set the timeout for receiving data (1000 ms). + if (serial_set_timeout (device->port, 1000) == -1) { + WARNING ("Failed to set the timeout."); + serial_close (device->port); + free (device); + return DEVICE_STATUS_IO; + } + + // Clear the DTR/RTS lines. + if (serial_set_dtr (device->port, 0) == -1 || + serial_set_rts (device->port, 0) == -1) { + WARNING ("Failed to set the DTR/RTS line."); + serial_close (device->port); + free (device); + return DEVICE_STATUS_IO; + } + + *out = (device_t*) device; + + return DEVICE_STATUS_SUCCESS; +} + + +static device_status_t +mares_puck_device_close (device_t *abstract) +{ + mares_puck_device_t *device = (mares_puck_device_t*) abstract; + + if (! device_is_mares_puck (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + // Close the device. + if (serial_close (device->port) == -1) { + free (device); + return DEVICE_STATUS_IO; + } + + // Free memory. + free (device); + + return DEVICE_STATUS_SUCCESS; +} + + +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]; + } +} + + +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 + WARNING ("Invalid charachter."); + + value <<= 4; + value += number; + } + output[i] = value; + } +} + + +static void +mares_puck_make_ascii (const unsigned char raw[], unsigned int rsize, unsigned char ascii[], unsigned int asize) +{ + assert (asize == 2 * (rsize + 2)); + + // Header + ascii[0] = '<'; + + // Data + mares_puck_convert_binary_to_ascii (raw, rsize, ascii + 1, 2 * rsize); + + // Checksum + unsigned char checksum = checksum_add_uint8 (ascii + 1, 2 * rsize, 0x00); + mares_puck_convert_binary_to_ascii (&checksum, 1, ascii + 1 + 2 * rsize, 2); + + // Trailer + ascii[asize - 1] = '>'; +} + + +static device_status_t +mares_puck_packet (mares_puck_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +{ + // Send the command to the device. + int n = serial_write (device->port, command, csize); + if (n != csize) { + WARNING ("Failed to send the command."); + return EXITCODE (n); + } + + // Receive the answer of the device. + n = serial_read (device->port, answer, asize); + if (n != asize) { + WARNING ("Failed to receive the answer."); + return EXITCODE (n); + } + + // Verify the header and trailer of the packet. + if (answer[0] != '<' || answer[asize - 1] != '>') { + WARNING ("Unexpected answer header/trailer byte."); + return DEVICE_STATUS_PROTOCOL; + } + + // Verify the checksum of the packet. + unsigned char crc = 0; + unsigned char ccrc = checksum_add_uint8 (answer + 1, asize - 4, 0x00); + mares_puck_convert_ascii_to_binary (answer + asize - 3, 2, &crc, 1); + if (crc != ccrc) { + WARNING ("Unexpected answer CRC."); + return DEVICE_STATUS_PROTOCOL; + } + + return DEVICE_STATUS_SUCCESS; +} + + +static device_status_t +mares_puck_transfer (mares_puck_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +{ + unsigned int nretries = 0; + device_status_t rc = DEVICE_STATUS_SUCCESS; + while ((rc = mares_puck_packet (device, command, csize, answer, asize)) != DEVICE_STATUS_SUCCESS) { + // Automatically discard a corrupted packet, + // and request a new one. + if (rc != DEVICE_STATUS_PROTOCOL && rc != DEVICE_STATUS_TIMEOUT) + return rc; + + // Abort if the maximum number of retries is reached. + if (nretries++ >= MAXRETRIES) + return rc; + } + + return rc; +} + + +static device_status_t +mares_puck_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size) +{ + mares_puck_device_t *device = (mares_puck_device_t*) abstract; + + if (! device_is_mares_puck (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + // The data transmission is split in packages + // of maximum $MARES_PUCK_PACKET_SIZE bytes. + + unsigned int nbytes = 0; + while (nbytes < size) { + // Calculate the packet size. + unsigned int len = size - nbytes; + if (len > MARES_PUCK_PACKET_SIZE) + len = MARES_PUCK_PACKET_SIZE; + + // Build the raw command. + unsigned char raw[] = {0x51, + (address ) & 0xFF, // Low + (address >> 8) & 0xFF, // High + len}; // Count + + // Build the ascii command. + unsigned char command[2 * (sizeof (raw) + 2)] = {0}; + mares_puck_make_ascii (raw, sizeof (raw), command, sizeof (command)); + + // Send the command and receive the answer. + unsigned char answer[2 * (MARES_PUCK_PACKET_SIZE + 2)] = {0}; + device_status_t rc = mares_puck_transfer (device, command, sizeof (command), answer, 2 * (len + 2)); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + // Extract the raw data from the packet. + mares_puck_convert_ascii_to_binary (answer + 1, 2 * len, data, len); + + nbytes += len; + address += len; + data += len; + } + + return DEVICE_STATUS_SUCCESS; +} + + +static device_status_t +mares_puck_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result) +{ + if (! device_is_mares_puck (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + if (size < MARES_PUCK_MEMORY_SIZE) { + WARNING ("Insufficient buffer space available."); + return DEVICE_STATUS_MEMORY; + } + + device_status_t rc = mares_puck_device_read (abstract, 0x00, data, MARES_PUCK_MEMORY_SIZE); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + if (result) + *result = MARES_PUCK_MEMORY_SIZE; + + return DEVICE_STATUS_SUCCESS; +} diff --git a/src/mares_puck.h b/src/mares_puck.h new file mode 100644 index 0000000..01e1efc --- /dev/null +++ b/src/mares_puck.h @@ -0,0 +1,41 @@ +/* + * libdivecomputer + * + * Copyright (C) 2009 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef MARES_PUCK_H +#define MARES_PUCK_H + +#include "device.h" +#include "parser.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MARES_PUCK_MEMORY_SIZE 0x4000 +#define MARES_PUCK_PACKET_SIZE 0x20 + +device_status_t +mares_puck_device_open (device_t **device, const char* name); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* MARES_PUCK_H */