diff --git a/src/descriptor.c b/src/descriptor.c index 863cb7c7..ad612019 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -392,6 +392,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Shearwater", "Tern", DC_FAMILY_SHEARWATER_PETREL, 12, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Tern TX", DC_FAMILY_SHEARWATER_PETREL, 12, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Peregrine TX", DC_FAMILY_SHEARWATER_PETREL, 13, DC_TRANSPORT_BLE, dc_filter_shearwater}, + {"Shearwater", "Perdix 3", DC_FAMILY_SHEARWATER_PETREL, 14, DC_TRANSPORT_BLE, dc_filter_shearwater}, /* Dive Rite NiTek Q */ {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL}, /* Citizen Hyper Aqualand */ @@ -764,6 +765,7 @@ dc_filter_shearwater (const dc_descriptor_t *descriptor, dc_transport_t transpor "NERD 2", "Perdix", "Perdix 2", + "Perdix 3", "Teric", "Peregrine", "Peregrine TX", diff --git a/src/device.c b/src/device.c index 5dbd57b3..e4378e5f 100644 --- a/src/device.c +++ b/src/device.c @@ -210,7 +210,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr rc = shearwater_predator_device_open (&device, context, iostream); break; case DC_FAMILY_SHEARWATER_PETREL: - rc = shearwater_petrel_device_open (&device, context, iostream); + rc = shearwater_petrel_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_DIVERITE_NITEKQ: rc = diverite_nitekq_device_open (&device, context, iostream); diff --git a/src/shearwater_common.c b/src/shearwater_common.c index dd5748ea..2d75fb6e 100644 --- a/src/shearwater_common.c +++ b/src/shearwater_common.c @@ -28,7 +28,22 @@ #include "platform.h" #include "array.h" -#define SZ_PACKET 254 +#define BLE_MTU_MIN 20 +#define BLE_MTU_MAX 514 + +// Protocol variants. +#define V1 1 +#define V2 2 + +// Maximum packet payload size. +// The V1 protocol uses an 8 bit length field, while the V2 protocol +// uses a 16 bit length field. Theoretically the V2 maximum size is +// 2^16-1 bytes, but the largest observed value is much smaller (0x0202 +// for the manifest and 0x0902 for the dive). Allow for an extra margin +// for future expansions. +#define SZ_PACKET_V1 254 +#define SZ_PACKET_V2 4096 +#define SZ_PACKET_MAX SZ_PACKET_V2 // SLIP special character codes #define END 0xC0 @@ -42,10 +57,19 @@ #define WDBI_REQUEST 0x2E #define WDBI_RESPONSE 0x6E +#define UPLOAD_INIT_REQUEST 0x35 +#define UPLOAD_INIT_RESPONSE 0x75 + +#define UPLOAD_DATA_REQUEST 0x36 +#define UPLOAD_DATA_RESPONSE 0x76 + +#define UPLOAD_EXIT_REQUEST 0x37 +#define UPLOAD_EXIT_RESPONSE 0x77 + #define NAK 0x7F dc_status_t -shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream) +shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model) { dc_status_t status = DC_STATUS_SUCCESS; @@ -69,6 +93,8 @@ shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *conte dc_iostream_sleep (device->iostream, 300); dc_iostream_purge (device->iostream, DC_DIRECTION_ALL); + device->protocol = (model == PERDIX3) ? V2 : V1; + return DC_STATUS_SUCCESS; } @@ -136,10 +162,11 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned { dc_status_t status = DC_STATUS_SUCCESS; dc_transport_t transport = dc_iostream_get_transport(device->iostream); - unsigned char buffer[32]; + const unsigned int header = transport == DC_TRANSPORT_BLE && device->protocol != V2; + unsigned char buffer[BLE_MTU_MIN]; unsigned int nbytes = 0; - if (transport == DC_TRANSPORT_BLE) { + if (header) { // Calculate the total number of bytes. unsigned int count = 1; for (unsigned int i = 0; i < size; ++i) { @@ -174,7 +201,7 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned return status; } - if (transport == DC_TRANSPORT_BLE) { + if (header) { buffer[1]++; nbytes = 2; } else { @@ -201,7 +228,7 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned return status; } - if (transport == DC_TRANSPORT_BLE) { + if (header) { buffer[1]++; nbytes = 2; } else { @@ -229,7 +256,8 @@ shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char d { dc_status_t status = DC_STATUS_SUCCESS; dc_transport_t transport = dc_iostream_get_transport(device->iostream); - unsigned char buffer[256]; + const unsigned int header = transport == DC_TRANSPORT_BLE && device->protocol != V2; + unsigned char buffer[BLE_MTU_MAX]; unsigned int escaped = 0; unsigned int nbytes = 0; @@ -249,7 +277,7 @@ shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char d } size_t offset = 0; - if (transport == DC_TRANSPORT_BLE) { + if (header) { if (transferred < 2) { ERROR (device->base.context, "Invalid packet length (" DC_PRINTF_SIZE ").", transferred); return DC_STATUS_PROTOCOL; @@ -330,10 +358,12 @@ shearwater_common_transfer (shearwater_common_device_t *device, const unsigned c { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; - unsigned char packet[SZ_PACKET + 4]; + const unsigned int maxpacket = (device->protocol == V2) ? SZ_PACKET_V2 : SZ_PACKET_V1; + const unsigned int headerlen = (device->protocol == V2) ? 5 : 4; + unsigned char packet[SZ_PACKET_MAX + 5]; unsigned int n = 0; - if (isize > SZ_PACKET || osize > SZ_PACKET) + if (isize > maxpacket || osize > maxpacket) return DC_STATUS_INVALIDARGS; if (device_is_cancelled (abstract)) @@ -342,12 +372,19 @@ shearwater_common_transfer (shearwater_common_device_t *device, const unsigned c // Setup the request packet. packet[0] = 0xFF; packet[1] = 0x01; - packet[2] = isize + 1; - packet[3] = 0x00; - memcpy (packet + 4, input, isize); + if (device->protocol == V2) { + // FF 01 00 [len_hi] [len_lo] + packet[2] = 0x00; + array_uint16_be_set (packet + 3, isize); + } else { + // FF 01 [len+1] 00 + packet[2] = isize + 1; + packet[3] = 0x00; + } + memcpy (packet + headerlen, input, isize); // Send the request packet. - status = shearwater_common_slip_write (device, packet, isize + 4); + status = shearwater_common_slip_write (device, packet, isize + headerlen); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the request packet."); return status; @@ -368,21 +405,33 @@ shearwater_common_transfer (shearwater_common_device_t *device, const unsigned c } // Validate the packet header. - if (n < 4 || packet[0] != 0x01 || packet[1] != 0xFF || packet[3] != 0x00) { + unsigned int idx = (device->protocol == V2) ? 2 : 3; + if (n < headerlen || packet[0] != 0x01 || packet[1] != 0xFF || packet[idx] != 0x00) { ERROR (abstract->context, "Invalid packet header."); return DC_STATUS_PROTOCOL; } // Validate the packet length. - unsigned int length = packet[2]; - if (length < 1 || length - 1 + 4 != n || length - 1 > osize) { + unsigned int length = 0; + if (device->protocol == V2) { + length = array_uint16_be (packet + 3); + } else { + length = packet[2]; + if (length < 1) { + ERROR (abstract->context, "Invalid packet header."); + return DC_STATUS_PROTOCOL; + } + + length -= 1; + } + if (length + headerlen != n || length > osize) { ERROR (abstract->context, "Invalid packet header."); return DC_STATUS_PROTOCOL; } - memcpy (output, packet + 4, length - 1); + memcpy (output, packet + headerlen, length); if (actual) - *actual = length - 1; + *actual = length; return DC_STATUS_SUCCESS; } @@ -393,10 +442,12 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf { dc_device_t *abstract = (dc_device_t *) device; dc_status_t rc = DC_STATUS_SUCCESS; + const unsigned int maxpacket = (device->protocol == V2) ? SZ_PACKET_V2 : SZ_PACKET_V1; + unsigned char response[SZ_PACKET_MAX]; unsigned int n = 0; unsigned char req_init[] = { - 0x35, + UPLOAD_INIT_REQUEST, (compression ? 0x10 : 0x00), 0x34, (address >> 24) & 0xFF, @@ -406,9 +457,10 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf (size >> 16) & 0xFF, (size >> 8) & 0xFF, (size ) & 0xFF}; - unsigned char req_block[] = {0x36, 0x00}; - unsigned char req_quit[] = {0x37}; - unsigned char response[SZ_PACKET]; + unsigned char req_block[] = {UPLOAD_DATA_REQUEST, 0x00, 0x00}; + unsigned char req_quit[] = {UPLOAD_EXIT_REQUEST}; + unsigned int req_block_len = (device->protocol == V2) ? + sizeof(req_block) : sizeof(req_block) - 1; // Erase the current contents of the buffer. if (!dc_buffer_clear (buffer)) { @@ -424,13 +476,27 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf } // Transfer the init request. - rc = shearwater_common_transfer (device, req_init, sizeof (req_init), response, 3, &n); + rc = shearwater_common_transfer (device, req_init, sizeof (req_init), response, maxpacket, &n); if (rc != DC_STATUS_SUCCESS) { return rc; } // Verify the init response. - if (n != 3 || response[0] != 0x75 || response[1] != 0x10 || response[2] > SZ_PACKET) { + if (n < 2 || response[0] != UPLOAD_INIT_RESPONSE) { + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + // Verify the init response length. + unsigned int len = (response[1] & 0xF0) >> 4; + if (n < len + 2 || len > 4) { + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + // Verify the maximum packet length. + unsigned int maxlength = array_uint_be (response + 2, len); + if (maxlength > maxpacket) { ERROR (abstract->context, "Unexpected response packet."); return DC_STATUS_PROTOCOL; } @@ -448,13 +514,13 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf while (nbytes < size && !done) { // Transfer the block request. req_block[1] = block; - rc = shearwater_common_transfer (device, req_block, sizeof (req_block), response, sizeof (response), &n); + rc = shearwater_common_transfer (device, req_block, req_block_len, response, maxpacket, &n); if (rc != DC_STATUS_SUCCESS) { return rc; } // Verify the block header. - if (n < 2 || response[0] != 0x76 || response[1] != block) { + if (n < 2 || response[0] != UPLOAD_DATA_RESPONSE || response[1] != block) { ERROR (abstract->context, "Unexpected response packet."); return DC_STATUS_PROTOCOL; } @@ -497,13 +563,13 @@ shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buf } // Transfer the quit request. - rc = shearwater_common_transfer (device, req_quit, sizeof (req_quit), response, 2, &n); + rc = shearwater_common_transfer (device, req_quit, sizeof (req_quit), response, maxpacket, &n); if (rc != DC_STATUS_SUCCESS) { return rc; } // Verify the quit response. - if (n != 2 || response[0] != 0x77 || response[1] != 0x00) { + if (n != 2 || response[0] != UPLOAD_EXIT_RESPONSE || response[1] != 0x00) { ERROR (abstract->context, "Unexpected response packet."); return DC_STATUS_PROTOCOL; } @@ -524,6 +590,8 @@ shearwater_common_rdbi (shearwater_common_device_t *device, unsigned int id, uns { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; + const unsigned int maxpacket = (device->protocol == V2) ? SZ_PACKET_V2 : SZ_PACKET_V1; + unsigned char response[SZ_PACKET_MAX]; // Transfer the request. unsigned int n = 0; @@ -531,8 +599,7 @@ shearwater_common_rdbi (shearwater_common_device_t *device, unsigned int id, uns RDBI_REQUEST, (id >> 8) & 0xFF, (id ) & 0xFF}; - unsigned char response[SZ_PACKET]; - status = shearwater_common_transfer (device, request, sizeof (request), response, sizeof (response), &n); + status = shearwater_common_transfer (device, request, sizeof (request), response, maxpacket, &n); if (status != DC_STATUS_SUCCESS) { return status; } @@ -577,22 +644,23 @@ shearwater_common_wdbi (shearwater_common_device_t *device, unsigned int id, con { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; + const unsigned int maxpacket = (device->protocol == V2) ? SZ_PACKET_V2 : SZ_PACKET_V1; + unsigned char response[SZ_PACKET_MAX]; - if (size + 3 > SZ_PACKET) { + if (size + 3 > maxpacket) { return DC_STATUS_INVALIDARGS; } // Transfer the request. unsigned int n = 0; - unsigned char request[SZ_PACKET] = { + unsigned char request[SZ_PACKET_MAX] = { WDBI_REQUEST, (id >> 8) & 0xFF, (id ) & 0xFF}; if (size) { memcpy (request + 3, data, size); } - unsigned char response[SZ_PACKET]; - status = shearwater_common_transfer (device, request, size + 3, response, sizeof (response), &n); + status = shearwater_common_transfer (device, request, size + 3, response, maxpacket, &n); if (status != DC_STATUS_SUCCESS) { return status; } diff --git a/src/shearwater_common.h b/src/shearwater_common.h index 22143f6f..d9da0082 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -59,6 +59,7 @@ extern "C" { #define PERDIX2 11 #define TERN 12 #define PEREGRINE_TX 13 +#define PERDIX3 14 #define NSTEPS 10000 #define STEP(i,n) ((NSTEPS * (i) + (n) / 2) / (n)) @@ -66,10 +67,11 @@ extern "C" { typedef struct shearwater_common_device_t { dc_device_t base; dc_iostream_t *iostream; + unsigned int protocol; } shearwater_common_device_t; dc_status_t -shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream); +shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); dc_status_t shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual); diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index a91b6f62..1079bb13 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -79,7 +79,7 @@ str2num (unsigned char data[], unsigned int size, unsigned int offset) dc_status_t -shearwater_petrel_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) +shearwater_petrel_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model) { dc_status_t status = DC_STATUS_SUCCESS; shearwater_petrel_device_t *device = NULL; @@ -98,7 +98,7 @@ shearwater_petrel_device_open (dc_device_t **out, dc_context_t *context, dc_iost memset (device->fingerprint, 0, sizeof (device->fingerprint)); // Setup the device. - status = shearwater_common_setup (&device->base, context, iostream); + status = shearwater_common_setup (&device->base, context, iostream, model); if (status != DC_STATUS_SUCCESS) { goto error_free; } diff --git a/src/shearwater_petrel.h b/src/shearwater_petrel.h index ea52ee96..61e6289f 100644 --- a/src/shearwater_petrel.h +++ b/src/shearwater_petrel.h @@ -32,7 +32,7 @@ extern "C" { #endif /* __cplusplus */ dc_status_t -shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); +shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); dc_status_t shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model, unsigned int serial); diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c index d4c97b4a..2234a5d4 100644 --- a/src/shearwater_predator.c +++ b/src/shearwater_predator.c @@ -80,7 +80,7 @@ shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, dc_io memset (device->fingerprint, 0, sizeof (device->fingerprint)); // Setup the device. - status = shearwater_common_setup (&device->base, context, iostream); + status = shearwater_common_setup (&device->base, context, iostream, PREDATOR); if (status != DC_STATUS_SUCCESS) { goto error_free; }