Drop support for older firmware versions.

Some of the older firmware versions contain a critical bug that can
possibly crash the device when trying to download dives, and also a bug
in the checksum calculation. To avoid these problems we simply refuse to
download when an old firmware is detected and require the user to
upgrade the firmware first.

The detection is performed by means of a new version command which
allows to retrieve the device information prior to downloading the
dives, but is only supported by recent firmware versions.

As a side effect, we gain access to the current firmware version, which
may be different from the one recorded during the last dive. And we can
get the info even if there are no dives present.
This commit is contained in:
Jef Driesen 2011-04-28 20:42:19 +02:00
parent 2b77a52e89
commit 36e5a75f8e

View File

@ -43,7 +43,7 @@
#define FP_OFFSET 20
#define SZ_MEMORY (29 * 64 * 1024)
#define SZ_HEADER 228
#define SZ_VERSION 14
typedef struct atomics_cobalt_device_t {
device_t base;
@ -53,16 +53,18 @@ typedef struct atomics_cobalt_device_t {
#endif
unsigned int simulation;
unsigned char fingerprint[6];
unsigned char version[SZ_VERSION];
} atomics_cobalt_device_t;
static device_status_t atomics_cobalt_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size);
static device_status_t atomics_cobalt_device_version (device_t *abstract, unsigned char data[], unsigned int size);
static device_status_t atomics_cobalt_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata);
static device_status_t atomics_cobalt_device_close (device_t *abstract);
static const device_backend_t atomics_cobalt_device_backend = {
DEVICE_TYPE_ATOMICS_COBALT,
atomics_cobalt_device_set_fingerprint, /* set_fingerprint */
NULL, /* version */
atomics_cobalt_device_version, /* version */
NULL, /* read */
NULL, /* write */
NULL, /* dump */
@ -124,6 +126,14 @@ atomics_cobalt_device_open (device_t **out)
return DEVICE_STATUS_IO;
}
device_status_t status = atomics_cobalt_device_version ((device_t *) device, device->version, sizeof (device->version));
if (status != DEVICE_STATUS_SUCCESS) {
libusb_close (device->handle);
libusb_exit (device->context);
free (device);
return status;
}
*out = (device_t*) device;
return DEVICE_STATUS_SUCCESS;
@ -188,6 +198,48 @@ atomics_cobalt_device_set_simulation (device_t *abstract, unsigned int simulatio
}
static device_status_t
atomics_cobalt_device_version (device_t *abstract, unsigned char data[], unsigned int size)
{
atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract;
if (size < SZ_VERSION)
return DEVICE_STATUS_MEMORY;
#ifdef HAVE_LIBUSB
// Send the command to the dive computer.
uint8_t bRequest = 0x01;
int rc = libusb_control_transfer (device->handle,
LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT,
bRequest, 0, 0, NULL, 0, TIMEOUT);
if (rc != LIBUSB_SUCCESS)
return DEVICE_STATUS_IO;
// Receive the answer from the dive computer.
int length = 0;
unsigned char packet[SZ_VERSION + 2] = {0};
rc = libusb_bulk_transfer (device->handle, 0x82,
packet, sizeof (packet), &length, TIMEOUT);
if (rc != LIBUSB_SUCCESS || length != sizeof (packet))
return DEVICE_STATUS_IO;
// Verify the checksum of the packet.
unsigned short crc = array_uint16_le (packet + SZ_VERSION);
unsigned short ccrc = checksum_add_uint16 (packet, SZ_VERSION, 0x0);
if (crc != ccrc) {
WARNING ("Unexpected answer CRC.");
return DEVICE_STATUS_PROTOCOL;
}
memcpy (data, packet, SZ_VERSION);
return DEVICE_STATUS_SUCCESS;
#else
return DEVICE_STATUS_UNSUPPORTED;
#endif
}
static device_status_t
atomics_cobalt_read_dive (device_t *abstract, dc_buffer_t *buffer, int init, device_progress_t *progress)
{
@ -252,16 +304,20 @@ atomics_cobalt_read_dive (device_t *abstract, dc_buffer_t *buffer, int init, dev
return DEVICE_STATUS_ERROR;
}
#if 0
// Verify the checksum of the packet.
// When only two 0xFF bytes are received, there are no more dives.
unsigned char *data = dc_buffer_get_data (buffer);
if (nbytes == 2 && data[0] == 0xFF && data[1] == 0xFF) {
dc_buffer_clear (buffer);
return DEVICE_STATUS_SUCCESS;
}
// Verify the checksum of the packet.
unsigned short crc = array_uint16_le (data + nbytes - 2);
unsigned short ccrc = checksum_add_uint16 (data, nbytes - 2, 0x0);
if (crc != ccrc) {
WARNING ("Unexpected answer CRC.");
return DEVICE_STATUS_PROTOCOL;
}
#endif
// Remove the checksum bytes.
dc_buffer_slice (buffer, 0, nbytes - 2);
@ -286,6 +342,18 @@ atomics_cobalt_device_foreach (device_t *abstract, dive_callback_t callback, voi
progress.maximum = SZ_MEMORY + 2;
device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress);
// Emit a device info event.
device_devinfo_t devinfo;
devinfo.model = array_uint16_le (device->version + 12);
devinfo.firmware = (array_uint16_le (device->version + 8) << 16)
+ array_uint16_le (device->version + 10);
devinfo.serial = 0;
for (unsigned int i = 0; i < 8; ++i) {
devinfo.serial *= 10;
devinfo.serial += device->version[i] - '0';
}
device_event_emit (abstract, DEVICE_EVENT_DEVINFO, &devinfo);
// Allocate a memory buffer.
dc_buffer_t *buffer = dc_buffer_new (0);
if (buffer == NULL)
@ -302,20 +370,6 @@ atomics_cobalt_device_foreach (device_t *abstract, dive_callback_t callback, voi
return DEVICE_STATUS_SUCCESS;
}
if (ndives == 0 && size >= SZ_HEADER) {
// Emit a device info event.
device_devinfo_t devinfo;
devinfo.model = 0;
devinfo.firmware = (array_uint16_le (data + 0x1E) << 16)
+ array_uint16_le (data + 0x20);
devinfo.serial = 0;
for (unsigned int i = 0; i < 8; ++i) {
devinfo.serial *= 10;
devinfo.serial += data[4 + i] - '0';
}
device_event_emit (abstract, DEVICE_EVENT_DEVINFO, &devinfo);
}
if (memcmp (data + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0) {
dc_buffer_free (buffer);
return DEVICE_STATUS_SUCCESS;