From 6f3de69f0d557d13abf39cfc141c1ed357dee741 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 9 Apr 2013 22:57:08 +0200 Subject: [PATCH] Move the low-level communication to a common file. This is done in preparation for the implementation of the new Petrel protocol, which shares the low level communication with the existing Predator protocol. --- src/Makefile.am | 1 + src/shearwater_common.c | 355 ++++++++++++++++++++++++++++++++++++++ src/shearwater_common.h | 49 ++++++ src/shearwater_predator.c | 336 ++---------------------------------- 4 files changed, 421 insertions(+), 320 deletions(-) create mode 100644 src/shearwater_common.c create mode 100644 src/shearwater_common.h diff --git a/src/Makefile.am b/src/Makefile.am index 39b5d5c..51cf70e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,6 +45,7 @@ libdivecomputer_la_SOURCES = \ cressi_leonardo.c cressi_leonardo_parser.c \ zeagle_n2ition3.c \ atomics_cobalt.c atomics_cobalt_parser.c \ + shearwater_common.h shearwater_common.c \ shearwater_predator.c shearwater_predator_parser.c \ ringbuffer.h ringbuffer.c \ checksum.h checksum.c \ diff --git a/src/shearwater_common.c b/src/shearwater_common.c new file mode 100644 index 0000000..f411785 --- /dev/null +++ b/src/shearwater_common.c @@ -0,0 +1,355 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 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 // memcmp, memcpy +#include // malloc, free + +#include "shearwater_common.h" + +#include "context-private.h" + +#define SZ_PACKET 254 + +// SLIP special character codes +#define END 0xC0 +#define ESC 0xDB +#define ESC_END 0xDC +#define ESC_ESC 0xDD + +#define EXITCODE(n) ((n) < 0 ? (n) : 0) + +dc_status_t +shearwater_common_open (shearwater_common_device_t *device, dc_context_t *context, const char *name) +{ + // Open the device. + int rc = serial_open (&device->port, context, name); + if (rc == -1) { + ERROR (context, "Failed to open the serial port."); + return DC_STATUS_IO; + } + + // Set the serial communication protocol (115200 8N1). + rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE); + if (rc == -1) { + ERROR (context, "Failed to set the terminal attributes."); + serial_close (device->port); + return DC_STATUS_IO; + } + + // Set the timeout for receiving data (3000ms). + if (serial_set_timeout (device->port, 3000) == -1) { + ERROR (context, "Failed to set the timeout."); + serial_close (device->port); + return DC_STATUS_IO; + } + + // Make sure everything is in a sane state. + serial_sleep (device->port, 300); + serial_flush (device->port, SERIAL_QUEUE_BOTH); + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +shearwater_common_close (shearwater_common_device_t *device) +{ + // Close the device. + if (serial_close (device->port) == -1) { + return DC_STATUS_IO; + } + + return DC_STATUS_SUCCESS; +} + + +static int +shearwater_common_slip_write (shearwater_common_device_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}; + +#if 0 + // Send an initial END character to flush out any data that may have + // accumulated in the receiver due to line noise. + n = serial_write (device->port, end, sizeof (end)); + if (n != sizeof (end)) { + return EXITCODE(n); + } +#endif + + for (unsigned int i = 0; i < size; ++i) { + const unsigned char *seq = NULL; + unsigned int len = 0; + switch (data[i]) { + case END: + // Escape the END character. + seq = esc_end; + len = sizeof (esc_end); + break; + case ESC: + // Escape the ESC character. + seq = esc_esc; + len = sizeof (esc_esc); + break; + default: + // Normal character. + seq = data + i; + len = 1; + break; + } + + n = serial_write (device->port, seq, len); + if (n != len) { + return EXITCODE(n); + } + } + + // Send the END character to indicate the end of the packet. + n = serial_write (device->port, end, sizeof (end)); + if (n != sizeof (end)) { + return EXITCODE(n); + } + + return size; +} + + +static int +shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char data[], unsigned int size) +{ + unsigned int received = 0; + + // Read bytes until a complete packet has been received. If the + // buffer runs out of space, bytes are dropped. The caller can + // detect this condition because the return value will be larger + // than the supplied buffer size. + while (1) { + unsigned char c = 0; + int n = 0; + + // Get a single character to process. + n = serial_read (device->port, &c, 1); + if (n != 1) { + return EXITCODE(n); + } + + switch (c) { + case END: + // If it's an END character then we're done. + // As a minor optimization, empty packets are ignored. This + // is to avoid bothering the upper layers with all the empty + // packets generated by the duplicate END characters which + // are sent to try to detect line noise. + if (received) + return received; + else + break; + case ESC: + // If it's an ESC character, get another character and then + // figure out what to store in the packet based on that. + n = serial_read (device->port, &c, 1); + if (n != 1) { + return EXITCODE(n); + } + + // If it's not one of the two escaped characters, 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 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) +{ + dc_device_t *abstract = (dc_device_t *) device; + unsigned char packet[SZ_PACKET + 4]; + int n = 0; + + if (isize > SZ_PACKET || osize > SZ_PACKET) + return DC_STATUS_INVALIDARGS; + + // Setup the request packet. + packet[0] = 0xFF; + packet[1] = 0x01; + packet[2] = isize + 1; + packet[3] = 0x00; + memcpy (packet + 4, input, isize); + + // Send the request packet. + n = shearwater_common_slip_write (device, packet, isize + 4); + if (n != isize + 4) { + ERROR (abstract->context, "Failed to send the request packet."); + if (n < 0) + return DC_STATUS_IO; + else + return DC_STATUS_TIMEOUT; + } + + // Receive the response packet. + n = shearwater_common_slip_read (device, packet, sizeof (packet)); + if (n <= 0 || n > sizeof (packet)) { + ERROR (abstract->context, "Failed to receive the response packet."); + if (n < 0) + return DC_STATUS_IO; + else if (n > sizeof (packet)) + return DC_STATUS_PROTOCOL; + else + return DC_STATUS_TIMEOUT; + } + + // Validate the packet header. + if (n < 4 || packet[0] != 0x01 || packet[1] != 0xFF || packet[3] != 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) { + ERROR (abstract->context, "Invalid packet header."); + return DC_STATUS_PROTOCOL; + } + + memcpy (output, packet + 4, length - 1); + *actual = length - 1; + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int address, unsigned int size) +{ + dc_device_t *abstract = (dc_device_t *) device; + dc_status_t rc = DC_STATUS_SUCCESS; + unsigned int n = 0; + + unsigned char req_init[] = { + 0x35, 0x00, 0x34, + (address >> 24) & 0xFF, + (address >> 16) & 0xFF, + (address >> 8) & 0xFF, + (address ) & 0xFF, + (size >> 16) & 0xFF, + (size >> 8) & 0xFF, + (size ) & 0xFF}; + unsigned char req_block[] = {0x36, 0x00}; + unsigned char req_quit[] = {0x37}; + unsigned char response[SZ_PACKET]; + + // Erase the current contents of the buffer. + if (!dc_buffer_clear (buffer) || !dc_buffer_reserve (buffer, size)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = 3 + size + 1; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Transfer the init request. + rc = shearwater_common_transfer (device, req_init, sizeof (req_init), response, 3, &n); + if (rc != DC_STATUS_SUCCESS) { + return rc; + } + + // Verify the init response. + if (n != 3 || response[0] != 0x75 || response[1] != 0x10 || response[2] != 0x82) { + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + progress.current += 3; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + unsigned char block = 1; + unsigned int nbytes = 0; + while (nbytes < size) { + // Transfer the block request. + req_block[1] = block; + rc = shearwater_common_transfer (device, req_block, sizeof (req_block), response, sizeof (response), &n); + if (rc != DC_STATUS_SUCCESS) { + return rc; + } + + // Verify the block header. + if (n < 2 || response[0] != 0x76 || response[1] != block) { + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + // Verify the block length. + unsigned int length = n - 2; + if (nbytes + length > size) { + ERROR (abstract->context, "Unexpected packet size."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + progress.current += length; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + dc_buffer_append (buffer, response + 2, length); + + nbytes += length; + block++; + } + + // Transfer the quit request. + rc = shearwater_common_transfer (device, req_quit, sizeof (req_quit), response, 2, &n); + if (rc != DC_STATUS_SUCCESS) { + return rc; + } + + // Verify the quit response. + if (n != 2 || response[0] != 0x77 || response[1] != 0x00) { + ERROR (abstract->context, "Unexpected response packet."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + progress.current += 1; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + return DC_STATUS_SUCCESS; +} diff --git a/src/shearwater_common.h b/src/shearwater_common.h new file mode 100644 index 0000000..a9e0661 --- /dev/null +++ b/src/shearwater_common.h @@ -0,0 +1,49 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 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 SHEARWATER_COMMON_H +#define SHEARWATER_COMMON_H + +#include "device-private.h" +#include "serial.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct shearwater_common_device_t { + dc_device_t base; + serial_t *port; +} shearwater_common_device_t; + +dc_status_t +shearwater_common_open (shearwater_common_device_t *device, dc_context_t *context, const char *name); + +dc_status_t +shearwater_common_close (shearwater_common_device_t *device); + +dc_status_t +shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int address, unsigned int size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* SHEARWATER_COMMON_H */ diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c index 3250f18..c7e8dec 100644 --- a/src/shearwater_predator.c +++ b/src/shearwater_predator.c @@ -24,9 +24,10 @@ #include +#include "shearwater_common.h" + #include "context-private.h" #include "device-private.h" -#include "serial.h" #include "array.h" #define ISINSTANCE(device) dc_device_isinstance((device), &shearwater_predator_device_vtable) @@ -34,24 +35,14 @@ #define PREDATOR 2 #define PETREL 3 -#define SZ_PACKET 254 #define SZ_BLOCK 0x80 #define SZ_MEMORY 0x20080 #define RB_PROFILE_BEGIN 0 #define RB_PROFILE_END 0x1F600 -// SLIP special character codes -#define END 0xC0 -#define ESC 0xDB -#define ESC_END 0xDC -#define ESC_ESC 0xDD - -#define EXITCODE(n) ((n) < 0 ? (n) : 0) - typedef struct shearwater_predator_device_t { - dc_device_t base; - serial_t *port; + shearwater_common_device_t base; unsigned char fingerprint[4]; } shearwater_predator_device_t; @@ -71,183 +62,11 @@ static const dc_device_vtable_t shearwater_predator_device_vtable = { }; -static int -shearwater_predator_slip_write (shearwater_predator_device_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}; - -#if 0 - // Send an initial END character to flush out any data that may have - // accumulated in the receiver due to line noise. - n = serial_write (device->port, end, sizeof (end)); - if (n != sizeof (end)) { - return EXITCODE(n); - } -#endif - - for (unsigned int i = 0; i < size; ++i) { - const unsigned char *seq = NULL; - unsigned int len = 0; - switch (data[i]) { - case END: - // Escape the END character. - seq = esc_end; - len = sizeof (esc_end); - break; - case ESC: - // Escape the ESC character. - seq = esc_esc; - len = sizeof (esc_esc); - break; - default: - // Normal character. - seq = data + i; - len = 1; - break; - } - - n = serial_write (device->port, seq, len); - if (n != len) { - return EXITCODE(n); - } - } - - // Send the END character to indicate the end of the packet. - n = serial_write (device->port, end, sizeof (end)); - if (n != sizeof (end)) { - return EXITCODE(n); - } - - return size; -} - - -static int -shearwater_predator_slip_read (shearwater_predator_device_t *device, unsigned char data[], unsigned int size) -{ - unsigned int received = 0; - - // Read bytes until a complete packet has been received. If the - // buffer runs out of space, bytes are dropped. The caller can - // detect this condition because the return value will be larger - // than the supplied buffer size. - while (1) { - unsigned char c = 0; - int n = 0; - - // Get a single character to process. - n = serial_read (device->port, &c, 1); - if (n != 1) { - return EXITCODE(n); - } - - switch (c) { - case END: - // If it's an END character then we're done. - // As a minor optimization, empty packets are ignored. This - // is to avoid bothering the upper layers with all the empty - // packets generated by the duplicate END characters which - // are sent to try to detect line noise. - if (received) - return received; - else - break; - case ESC: - // If it's an ESC character, get another character and then - // figure out what to store in the packet based on that. - n = serial_read (device->port, &c, 1); - if (n != 1) { - return EXITCODE(n); - } - - // If it's not one of the two escaped characters, 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 dc_status_t -shearwater_predator_transfer (shearwater_predator_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual) -{ - dc_device_t *abstract = (dc_device_t *) device; - unsigned char packet[SZ_PACKET + 4]; - int n = 0; - - if (isize > SZ_PACKET || osize > SZ_PACKET) - return DC_STATUS_INVALIDARGS; - - // Setup the request packet. - packet[0] = 0xFF; - packet[1] = 0x01; - packet[2] = isize + 1; - packet[3] = 0x00; - memcpy (packet + 4, input, isize); - - // Send the request packet. - n = shearwater_predator_slip_write (device, packet, isize + 4); - if (n != isize + 4) { - ERROR (abstract->context, "Failed to send the request packet."); - if (n < 0) - return DC_STATUS_IO; - else - return DC_STATUS_TIMEOUT; - } - - // Receive the response packet. - n = shearwater_predator_slip_read (device, packet, sizeof (packet)); - if (n <= 0 || n > sizeof (packet)) { - ERROR (abstract->context, "Failed to receive the response packet."); - if (n < 0) - return DC_STATUS_IO; - else if (n > sizeof (packet)) - return DC_STATUS_PROTOCOL; - else - return DC_STATUS_TIMEOUT; - } - - // Validate the packet header. - if (n < 4 || packet[0] != 0x01 || packet[1] != 0xFF || packet[3] != 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) { - ERROR (abstract->context, "Invalid packet header."); - return DC_STATUS_PROTOCOL; - } - - memcpy (output, packet + 4, length - 1); - *actual = length - 1; - - return DC_STATUS_SUCCESS; -} - - dc_status_t shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, const char *name) { + dc_status_t rc = DC_STATUS_SUCCESS; + if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -259,41 +78,18 @@ shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, const } // Initialize the base class. - device_init (&device->base, context, &shearwater_predator_device_vtable); + device_init (&device->base.base, context, &shearwater_predator_device_vtable); // Set the default values. - device->port = NULL; memset (device->fingerprint, 0, sizeof (device->fingerprint)); // Open the device. - int rc = serial_open (&device->port, context, name); - if (rc == -1) { - ERROR (context, "Failed to open the serial port."); + rc = shearwater_common_open (&device->base, context, name); + if (rc != DC_STATUS_SUCCESS) { free (device); - return DC_STATUS_IO; + return rc; } - // Set the serial communication protocol (115200 8N1). - rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE); - if (rc == -1) { - ERROR (context, "Failed to set the terminal attributes."); - serial_close (device->port); - free (device); - return DC_STATUS_IO; - } - - // Set the timeout for receiving data (3000ms). - if (serial_set_timeout (device->port, 3000) == -1) { - ERROR (context, "Failed to set the timeout."); - serial_close (device->port); - free (device); - return DC_STATUS_IO; - } - - // Make sure everything is in a sane state. - serial_sleep (device->port, 300); - serial_flush (device->port, SERIAL_QUEUE_BOTH); - *out = (dc_device_t *) device; return DC_STATUS_SUCCESS; @@ -303,18 +99,16 @@ shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, const static dc_status_t shearwater_predator_device_close (dc_device_t *abstract) { - shearwater_predator_device_t *device = (shearwater_predator_device_t*) abstract; + dc_status_t rc = DC_STATUS_SUCCESS; + shearwater_common_device_t *device = (shearwater_common_device_t *) abstract; // Close the device. - if (serial_close (device->port) == -1) { - free (device); - return DC_STATUS_IO; - } + rc = shearwater_common_close (device); // Free memory. free (device); - return DC_STATUS_SUCCESS; + return rc; } @@ -335,110 +129,12 @@ shearwater_predator_device_set_fingerprint (dc_device_t *abstract, const unsigne } -static dc_status_t -shearwater_predator_download (dc_device_t *abstract, dc_buffer_t *buffer, unsigned int address, unsigned int size) -{ - shearwater_predator_device_t *device = (shearwater_predator_device_t *) abstract; - dc_status_t rc = DC_STATUS_SUCCESS; - unsigned int n = 0; - - unsigned char req_init[] = { - 0x35, 0x00, 0x34, - (address >> 24) & 0xFF, - (address >> 16) & 0xFF, - (address >> 8) & 0xFF, - (address ) & 0xFF, - (size >> 16) & 0xFF, - (size >> 8) & 0xFF, - (size ) & 0xFF}; - unsigned char req_block[] = {0x36, 0x00}; - unsigned char req_quit[] = {0x37}; - unsigned char response[SZ_PACKET]; - - // Erase the current contents of the buffer. - if (!dc_buffer_clear (buffer) || !dc_buffer_reserve (buffer, size)) { - ERROR (abstract->context, "Insufficient buffer space available."); - return DC_STATUS_NOMEMORY; - } - - // Enable progress notifications. - dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - progress.maximum = 3 + size + 1; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - // Transfer the init request. - rc = shearwater_predator_transfer (device, req_init, sizeof (req_init), response, 3, &n); - if (rc != DC_STATUS_SUCCESS) { - return rc; - } - - // Verify the init response. - if (n != 3 || response[0] != 0x75 || response[1] != 0x10 || response[2] != 0x82) { - ERROR (abstract->context, "Unexpected response packet."); - return DC_STATUS_PROTOCOL; - } - - // Update and emit a progress event. - progress.current += 3; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - unsigned char block = 1; - unsigned int nbytes = 0; - while (nbytes < size) { - // Transfer the block request. - req_block[1] = block; - rc = shearwater_predator_transfer (device, req_block, sizeof (req_block), response, sizeof (response), &n); - if (rc != DC_STATUS_SUCCESS) { - return rc; - } - - // Verify the block header. - if (n < 2 || response[0] != 0x76 || response[1] != block) { - ERROR (abstract->context, "Unexpected response packet."); - return DC_STATUS_PROTOCOL; - } - - // Verify the block length. - unsigned int length = n - 2; - if (nbytes + length > size) { - ERROR (abstract->context, "Unexpected packet size."); - return DC_STATUS_PROTOCOL; - } - - // Update and emit a progress event. - progress.current += length; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - dc_buffer_append (buffer, response + 2, length); - - nbytes += length; - block++; - } - - // Transfer the quit request. - rc = shearwater_predator_transfer (device, req_quit, sizeof (req_quit), response, 2, &n); - if (rc != DC_STATUS_SUCCESS) { - return rc; - } - - // Verify the quit response. - if (n != 2 || response[0] != 0x77 || response[1] != 0x00) { - ERROR (abstract->context, "Unexpected response packet."); - return DC_STATUS_PROTOCOL; - } - - // Update and emit a progress event. - progress.current += 1; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - return DC_STATUS_SUCCESS; -} - - static dc_status_t shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) { - return shearwater_predator_download (abstract, buffer, 0xDD000000, SZ_MEMORY); + shearwater_common_device_t *device = (shearwater_common_device_t *) abstract; + + return shearwater_common_download (device, buffer, 0xDD000000, SZ_MEMORY); }