I just wrote my own library to access the DCD/DCT for firmware versions below v0.6.4. It should go without saying, but consider wrapping calls to these in a SINGLE_THREADED_BLOCK for safety.
No guarantees made on anything working properly, or for firmware v0.7.0 or higher (you should use the built in functions anyways then) so test in your use case.
dcd-reader.h
#ifndef DCD_READER_H
#define DCD_READER_H
#include "Particle.h"
#include "dct.h"
// Special Constants
#define DCT1_START 0x08004000
#define DCT2_START 0x08008000
#if HAL_PLATFORM_CLOUD_UDP
#define DCT_CONFIG_OFFSET 0x08
#define DCT_ACTIVE_OFFSET 0x04
#else
#define DCT_CONFIG_OFFSET 0x1d7c
#define DCT_ACTIVE_OFFSET 0x09
#endif
#define DCT_CONFIG_AREA_LENGTH 4258
// The size of the public and private keys depends on whether the device uses UDP (cellular devices, typically)
// which use the ALT key slot, or TCP (Wi-Fi devices) which use the main key slot
#if HAL_PLATFORM_CLOUD_UDP
#define DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE DCT_ALT_DEVICE_PRIVATE_KEY_SIZE
#define DEVICE_KEYS_HELPER_PUBLIC_KEY_SIZE DCT_ALT_DEVICE_PUBLIC_KEY_SIZE
#define DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET DCT_ALT_DEVICE_PRIVATE_KEY_OFFSET
#define DEVICE_KEYS_HELPER_PUBLIC_KEY_OFFSET DCT_ALT_DEVICE_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_SIZE DCT_ALT_SERVER_PUBLIC_KEY_SIZE
#define DEVICE_KEYS_HELPER_SERVER_KEY_OFFSET DCT_ALT_SERVER_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_ADDR_SIZE DCT_ALT_SERVER_ADDRESS_SIZE
#define DEVICE_KEYS_HELPER_SERVER_ADDR_OFFSET DCT_ALT_SERVER_ADDRESS_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_OFFSET DCT_ALT_SERVER_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE DCT_ALT_SERVER_PUBLIC_KEY_SIZE + DCT_ALT_SERVER_ADDRESS_SIZE
#else
#define DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE DCT_DEVICE_PRIVATE_KEY_SIZE
#define DEVICE_KEYS_HELPER_PUBLIC_KEY_SIZE DCT_DEVICE_PUBLIC_KEY_SIZE
#define DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET DCT_DEVICE_PRIVATE_KEY_OFFSET
#define DEVICE_KEYS_HELPER_PUBLIC_KEY_OFFSET DCT_DEVICE_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_SIZE DCT_SERVER_PUBLIC_KEY_SIZE
#define DEVICE_KEYS_HELPER_SERVER_KEY_OFFSET DCT_SERVER_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_ADDR_SIZE DCT_SERVER_ADDRESS_SIZE
#define DEVICE_KEYS_HELPER_SERVER_ADDR_OFFSET DCT_SERVER_ADDRESS_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_OFFSET DCT_SERVER_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE DCT_SERVER_PUBLIC_KEY_SIZE + DCT_SERVER_ADDRESS_SIZE
#endif
// errors
#define DCD_ERROR_CONFIG_NOT_FOUND 0x01
#define DCD_ERROR_SIZE_IS_ZERO 0x02
#define DCD_ERROR_ITEM_EXCEEDS_MEMORY 0x03
#define DCD_ERROR_BUFFER_TOO_SMALL 0x04
#define DCD_ERROR_BUFFER_TOO_LARGE 0x05
#define DCD_ERROR_CANT_ID_ACTIVE_DCT 0x05
enum DcdConfigType {
VERSION,
DEVICE_PRIVATE_KEY,
DEVICE_PUBLIC_KEY,
IP_CONFIG,
CLAIM_CODE,
CLAIMED,
SSID_PREFIX,
DEVICE_CODE,
VERSION_STRING,
SERVER_PUBLIC_KEY,
SERVER_ADDRESS,
SERVER_KEY_AND_ADDR,
DEVICE_ID,
};
class DcdReader {
public:
DcdReader() {};
~DcdReader() {};
int get_config(DcdConfigType config, void* read_ptr);
int copy_config(DcdConfigType config, void* buf, size_t buf_len);
int get_offset(uint32_t offset, void* read_ptr);
int copy_offset(uint32_t offset, size_t size, void* buf, size_t buf_len);
private:
enum DctAddr {
DCT_ERROR = 0,
DCT1 = DCT1_START,
DCT2 = DCT2_START
};
struct DcdConfigLocation {
uint32_t offset;
size_t size;
};
int check_offset(uint32_t offset, size_t size);
DctAddr get_active_dct();
DcdConfigLocation get_config_location(DcdConfigType config);
};
extern DcdReader Dcd;
#endif
dcd-reader.cpp
#include "dcd-reader.h"
int DcdReader::get_config(DcdConfigType config, void* read_ptr) {
DcdConfigLocation config_location = get_config_location(config);
if (config_location.size <= 0) return DCD_ERROR_CONFIG_NOT_FOUND;
return get_offset(config_location.offset, read_ptr);
}
int DcdReader::copy_config(DcdConfigType config, void* buf, size_t buf_len) {
DcdConfigLocation config_location = get_config_location(config);
if (config_location.size > 0) {
return copy_offset(config_location.offset, config_location.size, buf, buf_len);
}
return DCD_ERROR_CONFIG_NOT_FOUND;
}
int DcdReader::get_offset(uint32_t offset, void* read_ptr) {
int err = check_offset(offset, 1); // use 1 as minimum valid case
if (!err) {
read_ptr = nullptr;
return err;
}
DctAddr active_dct = get_active_dct();
if (active_dct == DCT_ERROR) {
read_ptr = nullptr;
return DCD_ERROR_CANT_ID_ACTIVE_DCT;
}
read_ptr = (void*)(active_dct + DCT_CONFIG_OFFSET + offset);
return 0;
}
int DcdReader::copy_offset(uint32_t offset, size_t size, void* buf, size_t buf_len) {
int err = check_offset(offset, size);
if (!err) return err;
if (buf_len < size) {
return DCD_ERROR_BUFFER_TOO_SMALL;
}
DctAddr active_dct = get_active_dct();
if (active_dct == DCT_ERROR) return DCD_ERROR_CANT_ID_ACTIVE_DCT;
memcpy(buf, (void*)(active_dct + DCT_CONFIG_OFFSET + offset), size);
return 0;
}
int DcdReader::check_offset(uint32_t offset, size_t size) {
if (size <= 0) {
return DCD_ERROR_SIZE_IS_ZERO;
}
if ((offset+size) > DCT_CONFIG_AREA_LENGTH) {
return DCD_ERROR_ITEM_EXCEEDS_MEMORY;
}
return 0;
}
DcdReader::DcdConfigLocation DcdReader::get_config_location(DcdConfigType config) {
DcdConfigLocation config_location;
switch(config) {
case VERSION:
config_location.offset = 32;
config_location.size = 2;
break;
case DEVICE_PRIVATE_KEY:
config_location.offset = DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET;
config_location.size = DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE;
break;
case DEVICE_PUBLIC_KEY:
config_location.offset = DEVICE_KEYS_HELPER_PUBLIC_KEY_OFFSET;
config_location.size = DEVICE_KEYS_HELPER_PUBLIC_KEY_SIZE;
break;
case IP_CONFIG:
config_location.offset = DCT_IP_CONFIG_OFFSET;
config_location.size = DCT_IP_CONFIG_SIZE;
break;
case CLAIM_CODE:// claim code. no terminating null.
config_location.offset = DCT_CLAIM_CODE_OFFSET;
config_location.size = DCT_CLAIM_CODE_SIZE;
break;
case CLAIMED: // 0,0xFF, not claimed. 1 claimed.
config_location.offset = 1825;
config_location.size = 1;
break;
case SSID_PREFIX:// SSID prefix (25 chars max). First byte is length.
config_location.offset = DCT_SSID_PREFIX_OFFSET;
config_location.size = DCT_SSID_PREFIX_SIZE;
break;
case DEVICE_CODE:// 6 suffix characters (not null terminated))
config_location.offset = DCT_DEVICE_CODE_OFFSET;
config_location.size = DCT_DEVICE_CODE_SIZE;
break;
case VERSION_STRING:
config_location.offset = 1858;
config_location.size = 32;
break;
case SERVER_PUBLIC_KEY:
config_location.offset = DCT_SERVER_PUBLIC_KEY_OFFSET;
config_location.size = DCT_SERVER_PUBLIC_KEY_SIZE;
break;
case SERVER_ADDRESS:
config_location.offset = DEVICE_KEYS_HELPER_SERVER_ADDR_OFFSET;
config_location.size = DEVICE_KEYS_HELPER_SERVER_ADDR_SIZE;
break;
case SERVER_KEY_AND_ADDR:
config_location.offset = DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_OFFSET;
config_location.size = DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE;
break;
case DEVICE_ID:
config_location.offset = DCT_DEVICE_ID_OFFSET;
config_location.size = DCT_DEVICE_ID_SIZE;
break;
default:
config_location.offset = 0;
config_location.size = 0;
}
return config_location;
}
DcdReader::DctAddr DcdReader::get_active_dct() {
#if HAL_PLATFORM_CLOUD_UDP
const uint32_t dct1_byte = *(uint32_t*)(DCT1_START+DCT_ACTIVE_OFFSET);
const uint32_t dct2_byte = *(uint32_t*)(DCT2_START+DCT_ACTIVE_OFFSET);
if (dct1_byte > 0) return DCT1;
else if (dct2_byte > 0) return DCT2;
#else
const uint8_t dct1_byte = *(uint8_t*)(DCT1_START+DCT_ACTIVE_OFFSET);
const uint8_t dct2_byte = *(uint8_t*)(DCT2_START+DCT_ACTIVE_OFFSET);
if (dct1_byte == 1) return DCT1;
else if (dct2_byte == 1) return DCT2;
#endif
return DCT_ERROR;
}
DcdReader Dcd;