diff --git a/uwatec.h b/uwatec.h index 11c0b23..252ec37 100644 --- a/uwatec.h +++ b/uwatec.h @@ -12,5 +12,6 @@ typedef void (*dive_callback_t) (const unsigned char *data, unsigned int size, v #include "uwatec_aladin.h" #include "uwatec_memomouse.h" +#include "uwatec_smart.h" #endif /* UWATEC_H */ diff --git a/uwatec_smart.c b/uwatec_smart.c new file mode 100644 index 0000000..b0ed1e8 --- /dev/null +++ b/uwatec_smart.c @@ -0,0 +1,326 @@ +#include // malloc, free +#include // strncmp, strstr +#include // time, strftime + +#include "uwatec.h" +#include "irda.h" +#include "utils.h" + +#define WARNING(expr) \ +{ \ + message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \ +} + +#define EXITCODE(rc) \ +( \ + rc == -1 ? UWATEC_ERROR_IO : UWATEC_ERROR_TIMEOUT \ +) + +struct smart { + struct irda *socket; + unsigned int address; +}; + + +void discovery (unsigned int address, const char *name, unsigned int charset, unsigned int hints, void *userdata) +{ + message ("device: address=%08x, name=%s, charset=%02x, hints=%04x\n", address, name, charset, hints); + + smart *device = (smart *) userdata; + if (device == NULL) + return; + + if (strncmp (name, "UWATEC Galileo Sol", 18) == 0 || + strncmp (name, "Uwatec Smart", 12) == 0 || + strstr (name, "Uwatec") != NULL || + strstr (name, "UWATEC") != NULL || + strstr (name, "Aladin") != NULL || + strstr (name, "ALADIN") != NULL || + strstr (name, "Smart") != NULL || + strstr (name, "SMART") != NULL || + strstr (name, "Galileo") != NULL || + strstr (name, "GALILEO") != NULL) { + message ("Found an Uwatec dive computer.\n"); + device->address = address; + } +} + + +int +uwatec_smart_open (smart **out) +{ + if (out == NULL) + return UWATEC_ERROR; + + // Allocate memory. + struct smart *device = malloc (sizeof (struct smart)); + if (device == NULL) { + WARNING ("Failed to allocate memory."); + return UWATEC_ERROR_MEMORY; + } + + // Set the default values. + device->socket = NULL; + device->address = 0; + + irda_init (); + + // Open the irda socket. + int rc = irda_socket_open (&device->socket); + if (rc == -1) { + WARNING ("Failed to open the irda socket."); + irda_cleanup (); + free (device); + return UWATEC_ERROR_IO; + } + + // Discover the device. + rc = irda_socket_discover (device->socket, discovery, device); + if (rc == -1) { + WARNING ("Failed to discover the device."); + irda_socket_close (device->socket); + irda_cleanup (); + free (device); + return UWATEC_ERROR_IO; + } + + if (device->address == 0) { + WARNING ("No dive computer found."); + irda_socket_close (device->socket); + irda_cleanup (); + free (device); + return UWATEC_ERROR; + } + + // Connect the device. + rc = irda_socket_connect_lsap (device->socket, device->address, 1); + if (rc == -1) { + WARNING ("Failed to connect the device."); + irda_socket_close (device->socket); + irda_cleanup (); + free (device); + return UWATEC_ERROR_IO; + } + + *out = device; + + return UWATEC_SUCCESS; +} + + +int +uwatec_smart_close (smart *device) +{ + if (device == NULL) + return UWATEC_SUCCESS; + + // Close the device. + if (irda_socket_close (device->socket) == -1) { + irda_cleanup (); + free (device); + return UWATEC_ERROR_IO; + } + + irda_cleanup (); + + // Free memory. + free (device); + + return UWATEC_SUCCESS; +} + + +int +uwatec_smart_read (smart *device, unsigned char data[], unsigned int msize) +{ + if (device == NULL) + return UWATEC_ERROR; + + unsigned int timestamp = 0; + unsigned char command[9] = {0}; + unsigned char answer[4] = {0}; + + // Handshake (stage 1). + + command[0] = 0x1B; + int rc = irda_socket_write (device->socket, command, 1); + if (rc != 1) { + WARNING ("Failed to send the command."); + return EXITCODE (rc); + } + rc = irda_socket_read (device->socket, answer, 1); + if (rc != 1) { + WARNING ("Failed to receive the answer."); + return EXITCODE (rc); + } + + message ("handshake: header=%02x\n", answer[0]); + + if (answer[0] != 0x01) { + WARNING ("Unexpected answer byte(s)."); + return UWATEC_ERROR_PROTOCOL; + } + + // Handshake (stage 2). + + command[0] = 0x1C; + command[1] = 0x10; + command[2] = 0x27; + command[3] = 0; + command[4] = 0; + rc = irda_socket_write (device->socket, command, 5); + if (rc != 5) { + WARNING ("Failed to send the command."); + return EXITCODE (rc); + } + rc = irda_socket_read (device->socket, answer, 1); + if (rc != 1) { + WARNING ("Failed to receive the answer."); + return EXITCODE (rc); + } + + message ("handshake: header=%02x\n", answer[0]); + + if (answer[0] != 0x01) { + WARNING ("Unexpected answer byte(s)."); + return UWATEC_ERROR_PROTOCOL; + } + + // Dive Computer Time. + + command[0] = 0x1A; + rc = irda_socket_write (device->socket, command, 1); + if (rc != 1) { + WARNING ("Failed to send the command."); + return EXITCODE (rc); + } + rc = irda_socket_read (device->socket, answer, 4); + if (rc != 4) { + WARNING ("Failed to receive the answer."); + return EXITCODE (rc); + } + + time_t device_time = answer[0] + (answer[1] << 8) + + (answer[2] << 16) + (answer[3] << 24); + message ("handshake: timestamp=0x%08x\n", device_time); + + // PC Time and Time Correction. + + time_t now = time (NULL); + unsigned char datetime[21] = {0}; + strftime (datetime, sizeof (datetime), "%Y-%m-%dT%H:%M:%SZ", gmtime (&now)); + message ("handshake: now=%lu (%s)\n", (unsigned long)now, datetime); + + // Serial Number + + command[0] = 0x14; + rc = irda_socket_write (device->socket, command, 1); + if (rc != 1) { + WARNING ("Failed to send the command."); + return EXITCODE (rc); + } + rc = irda_socket_read (device->socket, answer, 4); + if (rc != 4) { + WARNING ("Failed to receive the answer."); + return EXITCODE (rc); + } + + unsigned int serial = answer[0] + (answer[1] << 8) + + (answer[2] << 16) + (answer[3] << 24); + message ("handshake: serial=0x%08x\n", serial); + + // Dive Computer Model. + + command[0] = 0x10; + rc = irda_socket_write (device->socket, command, 1); + if (rc != 1) { + WARNING ("Failed to send the command."); + return EXITCODE (rc); + } + rc = irda_socket_read (device->socket, answer, 1); + if (rc != 1) { + WARNING ("Failed to receive the answer."); + return EXITCODE (rc); + } + + message ("handshake: model=0x%02x\n", answer[0]); + + // Data Length. + + command[0] = 0xC6; + command[1] = (timestamp ) & 0xFF; + command[2] = (timestamp >> 8 ) & 0xFF; + command[3] = (timestamp >> 16) & 0xFF; + command[4] = (timestamp >> 24) & 0xFF; + command[5] = 0x10; + command[6] = 0x27; + command[7] = 0; + command[8] = 0; + rc = irda_socket_write (device->socket, command, 9); + if (rc != 9) { + WARNING ("Failed to send the command."); + return EXITCODE (rc); + } + rc = irda_socket_read (device->socket, answer, 4); + if (rc != 4) { + WARNING ("Failed to receive the answer."); + return EXITCODE (rc); + } + + unsigned int size = answer[0] + (answer[1] << 8) + + (answer[2] << 16) + (answer[3] << 24); + message ("handshake: size=%u\n", size); + + if (size == UWATEC_SUCCESS) + return 0; + + unsigned char *package = malloc (size * sizeof (unsigned char)); + if (package == NULL) { + WARNING ("Memory allocation error."); + return UWATEC_ERROR_MEMORY; + } + + // Data. + + command[0] = 0xC4; + command[1] = (timestamp ) & 0xFF; + command[2] = (timestamp >> 8 ) & 0xFF; + command[3] = (timestamp >> 16) & 0xFF; + command[4] = (timestamp >> 24) & 0xFF; + command[5] = 0x10; + command[6] = 0x27; + command[7] = 0; + command[8] = 0; + rc = irda_socket_write (device->socket, command, 9); + if (rc != 9) { + WARNING ("Failed to send the command."); + free (package); + return EXITCODE (rc); + } + + unsigned int nbytes = 0; + while (nbytes < size) { + unsigned int len = size - nbytes; + if (len > 32) + len = 32; + rc = irda_socket_read (device->socket, package + nbytes, len); + if (rc < 0) { + WARNING ("Failed to receive the answer."); + free (package); + return EXITCODE (rc); + } + nbytes += rc; + message ("len=%u, rc=%i, nbytes=%u\n", len, rc, nbytes); + } + + if (size <= msize) { + memcpy (data, package, size); + } else { + message ("Insufficient buffer space available.\n"); + memcpy (data, package, msize); + } + + free (package); + + return UWATEC_SUCCESS; +} diff --git a/uwatec_smart.h b/uwatec_smart.h new file mode 100644 index 0000000..32f01e6 --- /dev/null +++ b/uwatec_smart.h @@ -0,0 +1,19 @@ +#ifndef UWATEC_SMART_H +#define UWATEC_SMART_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct smart smart; + +int uwatec_smart_open (smart **device); + +int uwatec_smart_close (smart *device); + +int uwatec_smart_read (smart *device, unsigned char data[], unsigned int size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* UWATEC_SMART_H */ diff --git a/uwatec_smart_test.c b/uwatec_smart_test.c new file mode 100644 index 0000000..c3ba9a3 --- /dev/null +++ b/uwatec_smart_test.c @@ -0,0 +1,96 @@ +#include // fopen, fwrite, fclose +#include // malloc, free +#include // memset + +#include "uwatec.h" +#include "utils.h" + +#define WARNING(expr) \ +{ \ + message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \ +} + + +int test_dump_memory (const char* filename) +{ + smart *device = NULL; + + const unsigned int size = 2 * 1024 * 1024; + unsigned char *data = malloc (size * sizeof (unsigned char)); + if (data == NULL) + return UWATEC_ERROR_MEMORY; + memset (data, 0, size * sizeof (unsigned char)); + + message ("uwatec_smart_open\n"); + int rc = uwatec_smart_open (&device); + if (rc != UWATEC_SUCCESS) { + WARNING ("Cannot open device."); + free (data); + return rc; + } + + message ("uwatec_smart_read\n"); + rc = uwatec_smart_read (device, data, size); + if (rc != UWATEC_SUCCESS) { + WARNING ("Cannot read data."); + uwatec_smart_close (device); + free (data); + return rc; + } + + message ("Dumping data\n"); + FILE* fp = fopen (filename, "wb"); + if (fp != NULL) { + fwrite (data, sizeof (unsigned char), size, fp); + fclose (fp); + } + + message ("uwatec_smart_close\n"); + rc = uwatec_smart_close (device); + if (rc != UWATEC_SUCCESS) { + WARNING ("Cannot close device."); + free (data); + return rc; + } + + free (data); + + return UWATEC_SUCCESS; +} + + +const char* errmsg (int rc) +{ + switch (rc) { + case UWATEC_SUCCESS: + return "Success"; + case UWATEC_ERROR: + return "Generic error"; + case UWATEC_ERROR_IO: + return "Input/output error"; + case UWATEC_ERROR_MEMORY: + return "Memory error"; + case UWATEC_ERROR_PROTOCOL: + return "Protocol error"; + case UWATEC_ERROR_TIMEOUT: + return "Timeout"; + default: + return "Unknown error"; + } +} + + +int main(int argc, char *argv[]) +{ + message_set_logfile ("SMART.LOG"); + + int a = test_dump_memory ("SMART.DMP"); + + message ("\nSUMMARY\n"); + message ("-------\n"); + message ("test_dump_memory: %s\n", errmsg (a)); + + message_set_logfile (NULL); + + return 0; +}