Commit e5cd9e56 authored by 尹佳钦's avatar 尹佳钦

2020 0930

parent e5950adc
......@@ -156,6 +156,9 @@ void emberAfPluginDeviceTableDeviceLeftCallback(EmberEUI64 nodeEui64)
rpc_printfJSON("left",nodeJson);
kk_msg_report_dev_leave(deviceTable[deviceTableIndex].eui64);
kk_device_table_delete(deviceTable[deviceTableIndex].eui64);
kk_device_db_delete(deviceTable[deviceTableIndex].eui64);
}
......
......@@ -266,7 +266,7 @@ int _init_param(struct jrpc_server *server) {
//kk_zlog_init("paltform");
printf("getenv\r\n");
char * debug_level_env = getenv("HOME");
char * debug_level_env = getenv("JRPC_DEBUG");
printf("getenv(JRPC_DEBUG):%s\n", server->debug_level);
if (debug_level_env == NULL)
server->debug_level = 0;
......
......@@ -47,13 +47,13 @@ void halReboot(void)
uint8_t halGetResetInfo(void)
{
return RESET_SOFTWARE;
return RESET_SOFTWARE;
}
PGM_P halGetResetString(void)
{
static PGM_P resetString = "SOFTWARE";
return (resetString);
static PGM_P resetString = "SOFTWARE";
return (resetString);
}
// Ideally this should not be necessary, but the serial code references
......
......@@ -36,9 +36,10 @@
static const char options[] = "b:f:hv::i:n:o:p:r:s:t:x:";
bool checkSerialPort(const char* portString);
bool ezspInternalProcessCommandOptions(int argc, char *argv[], char *errStr)
{
int c;
int c;
char port[ASH_PORT_LEN];
char devport[ASH_PORT_LEN];
uint32_t baud;
......@@ -48,181 +49,181 @@ bool ezspInternalProcessCommandOptions(int argc, char *argv[], char *errStr)
uint8_t portnum;
uint8_t cfg;
int blksize;
int optionCount = 0;
int optionCount = 0;
if (!argv || !errStr) {
snprintf(errStr, ERR_LEN, "Error: argv[] is %s and errStr is %s.\n",
argv ? "valid" : "NULL",
errStr ? "valid" : "NULL");
return false;
}
if (!argv || !errStr) {
snprintf(errStr, ERR_LEN, "Error: argv[] is %s and errStr is %s.\n",
argv ? "valid" : "NULL",
errStr ? "valid" : "NULL");
return false;
}
while (true) {
c = getopt(argc, argv, options);
if (c == -1) {
if (optind != argc ) {
snprintf(errStr, ERR_LEN, "Invalid option %s.\n", argv[optind]);
}
break;
}
while (true) {
c = getopt(argc, argv, options);
if (c == -1) {
if (optind != argc ) {
snprintf(errStr, ERR_LEN, "Invalid option %s.\n", argv[optind]);
}
break;
}
optionCount++;
optionCount++;
switch (c) {
case 'b':
if (!optarg || (sscanf(optarg, "%u", &baud) != 1)) {
snprintf(errStr, ERR_LEN, "Invalid baud rate %s.\n",
optarg ? optarg : "NULL");
} else {
ashWriteConfig(baudRate, baud);
}
break;
case 'f':
if (!optarg) {
snprintf(errStr, ERR_LEN, "Invalid flow control choice NULL.\n");
break;
}
switch (*optarg) {
case 'r':
ashWriteConfig(rtsCts, true);
break;
case 'x':
ashWriteConfig(rtsCts, false);
break;
default:
snprintf(errStr, ERR_LEN, "Invalid flow control choice %s.\n", optarg);
}
break;
case 'h':
case '?':
snprintf(errStr, ERR_LEN, "\n");
break;
case 'i':
if (!optarg || (sscanf(optarg, "%hhu", &enable) != 1) || (enable > 1)) {
snprintf(errStr, ERR_LEN, "Invalid input buffer choice %s.\n",
optarg ? optarg : "NULL");
} else {
blksize = enable ? 256 : 1;
ashWriteConfig(inBlockLen, blksize);
}
break;
case 'n':
if (optionCount != 1) {
snprintf(errStr, ERR_LEN, "NCP option, if present, must be first.\n");
} else if (!optarg || (sscanf(optarg, "%hhu", &cfg) != 1) || (cfg > 1)) {
snprintf(errStr, ERR_LEN, "Invalid NCP config choice %s.\n",
optarg ? optarg : "NULL");
} else {
ashSelectHostConfig(cfg);
}
break;
case 'o':
if (!optarg || (sscanf(optarg, "%hhu", &enable) != 1) || (enable > 1)) {
snprintf(errStr, ERR_LEN, "Invalid output buffer choice %s.\n",
optarg ? optarg : "NULL");
} else {
blksize = enable ? 256 : 1;
ashWriteConfig(outBlockLen, blksize);
}
break;
case 'p':
if (!optarg || (sscanf(optarg, "%39s", port) <= 0)) {
snprintf(errStr, ERR_LEN, "Invalid serial port name %s.\n",
optarg ? optarg : "NULL");
} else if (strlen(port) >= ASH_PORT_LEN - 1) {
snprintf(errStr, ERR_LEN, "Serial port name %s too long.\n", port);
} else {
// Handle some common variations specifying a serial port
strncpy(devport, "/dev/", 5);
devport[5] = '\0';
if ( strncmp(devport, port, 5) == 0) {
strncat(devport, port + 5, ASH_PORT_LEN - 5 - 1);
#ifdef __CYGWIN__
} else if ( ((strncmp("COM", port, 3) == 0)
|| (strncmp("com", port, 3) == 0) )
&& (sscanf(port + 3, "%hhu", &portnum) == 1)
&& (portnum > 0) ) {
snprintf(devport, ASH_PORT_LEN, "/dev/ttyS%hhu", portnum - 1);
} else if ( (sscanf(port, "%hhu", &portnum) == 1) && portnum ) {
snprintf(devport, ASH_PORT_LEN, "/dev/ttyS%hhu", portnum - 1);
#else
} else if (sscanf(port, "%hhu", &portnum) == 1) {
snprintf(devport, ASH_PORT_LEN, "/dev/ttyS%hhu", portnum);
#endif
} else {
strncat(devport, port, ASH_PORT_LEN - 1);
}
strncpy(ashHostConfig.serialPort, devport, ASH_PORT_LEN - 1);
ashHostConfig.serialPort[ASH_PORT_LEN - 1] = '\0';
if (!checkSerialPort(ashHostConfig.serialPort)) {
return false;
}
}
break;
case 'r':
if (!optarg) {
snprintf(errStr, ERR_LEN, "Invalid reset method NULL.\n");
break;
}
switch (*optarg) {
case 'r':
ashWriteConfig(resetMethod, ASH_RESET_METHOD_RST);
break;
case 'd':
ashWriteConfig(resetMethod, ASH_RESET_METHOD_DTR);
break;
case 'c':
ashWriteConfig(resetMethod, ASH_RESET_METHOD_CUSTOM);
break;
default:
snprintf(errStr, ERR_LEN, "Invalid reset method %s.\n", optarg);
}
break;
case 's':
if (!optarg || (sscanf(optarg, "%hhu", &stops) != 1) || (stops < 1)
|| (stops > 2)) {
snprintf(errStr, ERR_LEN, "Invalid number of stop bits %s.\n",
optarg ? optarg : "NULL");
} else {
ashWriteConfig(stopBits, stops);
}
break;
case 't':
if (!optarg || (sscanf(optarg, "%hhu", &trace) != 1)) {
snprintf(errStr, ERR_LEN, "Invalid trace flag value %s.\n",
optarg ? optarg : "NULL");
} else {
ashWriteConfig(traceFlags, trace);
}
break;
case 'v':
if (!backchannelSupported) {
fprintf(stderr, "Error: Backchannel support not compiled into this application.\n");
exit(1);
}
backchannelEnable = true;
if (optarg) {
int port = atoi(optarg);
if (port == 0 || port > 65535) {
snprintf(errStr, ERR_LEN, "Invalid virtual ISA port number '%d'.\n", port);
}
backchannelSerialPortOffset = port;
}
break;
case 'x':
if (!optarg || (sscanf(optarg, "%hhu", &enable) != 1) || (enable > 1)) {
snprintf(errStr, ERR_LEN, "Invalid randomization choice %s.\n",
optarg ? optarg : "NULL");
} else {
ashWriteConfig(randomize, enable);
}
break;
default:
assert(1);
break;
} // end of switch (c)
} //end while
return true;
switch (c) {
case 'b':
if (!optarg || (sscanf(optarg, "%u", &baud) != 1)) {
snprintf(errStr, ERR_LEN, "Invalid baud rate %s.\n",
optarg ? optarg : "NULL");
} else {
ashWriteConfig(baudRate, baud);
}
break;
case 'f':
if (!optarg) {
snprintf(errStr, ERR_LEN, "Invalid flow control choice NULL.\n");
break;
}
switch (*optarg) {
case 'r':
ashWriteConfig(rtsCts, true);
break;
case 'x':
ashWriteConfig(rtsCts, false);
break;
default:
snprintf(errStr, ERR_LEN, "Invalid flow control choice %s.\n", optarg);
}
break;
case 'h':
case '?':
snprintf(errStr, ERR_LEN, "\n");
break;
case 'i':
if (!optarg || (sscanf(optarg, "%hhu", &enable) != 1) || (enable > 1)) {
snprintf(errStr, ERR_LEN, "Invalid input buffer choice %s.\n",
optarg ? optarg : "NULL");
} else {
blksize = enable ? 256 : 1;
ashWriteConfig(inBlockLen, blksize);
}
break;
case 'n':
if (optionCount != 1) {
snprintf(errStr, ERR_LEN, "NCP option, if present, must be first.\n");
} else if (!optarg || (sscanf(optarg, "%hhu", &cfg) != 1) || (cfg > 1)) {
snprintf(errStr, ERR_LEN, "Invalid NCP config choice %s.\n",
optarg ? optarg : "NULL");
} else {
ashSelectHostConfig(cfg);
}
break;
case 'o':
if (!optarg || (sscanf(optarg, "%hhu", &enable) != 1) || (enable > 1)) {
snprintf(errStr, ERR_LEN, "Invalid output buffer choice %s.\n",
optarg ? optarg : "NULL");
} else {
blksize = enable ? 256 : 1;
ashWriteConfig(outBlockLen, blksize);
}
break;
case 'p':
if (!optarg || (sscanf(optarg, "%39s", port) <= 0)) {
snprintf(errStr, ERR_LEN, "Invalid serial port name %s.\n",
optarg ? optarg : "NULL");
} else if (strlen(port) >= ASH_PORT_LEN - 1) {
snprintf(errStr, ERR_LEN, "Serial port name %s too long.\n", port);
} else {
// Handle some common variations specifying a serial port
strncpy(devport, "/dev/", 5);
devport[5] = '\0';
if ( strncmp(devport, port, 5) == 0) {
strncat(devport, port + 5, ASH_PORT_LEN - 5 - 1);
#ifdef __CYGWIN__
} else if ( ((strncmp("COM", port, 3) == 0)
|| (strncmp("com", port, 3) == 0) )
&& (sscanf(port + 3, "%hhu", &portnum) == 1)
&& (portnum > 0) ) {
snprintf(devport, ASH_PORT_LEN, "/dev/ttyS%hhu", portnum - 1);
} else if ( (sscanf(port, "%hhu", &portnum) == 1) && portnum ) {
snprintf(devport, ASH_PORT_LEN, "/dev/ttyS%hhu", portnum - 1);
#else
} else if (sscanf(port, "%hhu", &portnum) == 1) {
snprintf(devport, ASH_PORT_LEN, "/dev/ttyS%hhu", portnum);
#endif
} else {
strncat(devport, port, ASH_PORT_LEN - 1);
}
strncpy(ashHostConfig.serialPort, devport, ASH_PORT_LEN - 1);
ashHostConfig.serialPort[ASH_PORT_LEN - 1] = '\0';
if (!checkSerialPort(ashHostConfig.serialPort)) {
return false;
}
}
break;
case 'r':
if (!optarg) {
snprintf(errStr, ERR_LEN, "Invalid reset method NULL.\n");
break;
}
switch (*optarg) {
case 'r':
ashWriteConfig(resetMethod, ASH_RESET_METHOD_RST);
break;
case 'd':
ashWriteConfig(resetMethod, ASH_RESET_METHOD_DTR);
break;
case 'c':
ashWriteConfig(resetMethod, ASH_RESET_METHOD_CUSTOM);
break;
default:
snprintf(errStr, ERR_LEN, "Invalid reset method %s.\n", optarg);
}
break;
case 's':
if (!optarg || (sscanf(optarg, "%hhu", &stops) != 1) || (stops < 1)
|| (stops > 2)) {
snprintf(errStr, ERR_LEN, "Invalid number of stop bits %s.\n",
optarg ? optarg : "NULL");
} else {
ashWriteConfig(stopBits, stops);
}
break;
case 't':
if (!optarg || (sscanf(optarg, "%hhu", &trace) != 1)) {
snprintf(errStr, ERR_LEN, "Invalid trace flag value %s.\n",
optarg ? optarg : "NULL");
} else {
ashWriteConfig(traceFlags, trace);
}
break;
case 'v':
if (!backchannelSupported) {
fprintf(stderr, "Error: Backchannel support not compiled into this application.\n");
exit(1);
}
backchannelEnable = true;
if (optarg) {
int port = atoi(optarg);
if (port == 0 || port > 65535) {
snprintf(errStr, ERR_LEN, "Invalid virtual ISA port number '%d'.\n", port);
}
backchannelSerialPortOffset = port;
}
break;
case 'x':
if (!optarg || (sscanf(optarg, "%hhu", &enable) != 1) || (enable > 1)) {
snprintf(errStr, ERR_LEN, "Invalid randomization choice %s.\n",
optarg ? optarg : "NULL");
} else {
ashWriteConfig(randomize, enable);
}
break;
default:
assert(1);
break;
} // end of switch (c)
} //end while
return true;
}
//------------------------------------------------------------------------------
......
......@@ -31,31 +31,31 @@ void ashPrintCounters(AshCount *counters, bool clear);
void ashClearCounters(AshCount *counters);
static const char usage[] =
" {ncp type} {options}\n"
" ncp type:\n"
" -n 0,1 0=EM2xx/EM3xx @ 115200 bps, RTS/CTS\n"
" 1=EM2xx/EM3xx @ 57600 bps, XON/XOFF\n"
" (if present must be the first option)\n"
" options:\n"
" -b <baud rate> 9600, 19200, 38400, 57600, 115200, etc.\n"
" -f r,x flow control: r=RST/CTS, x=XON/XOFF\n"
" -h display usage information\n"
" -i 0,1 enable/disable input buffering\n"
" -o 0,1 enable/disable output buffering\n"
" -p <port> serial port name or number (eg, COM1, ttyS0, or 1)\n"
" -r d,r,c ncp reset method: d=DTR, r=RST frame, c=custom\n"
" -s 1,2 stop bits\n"
" -t <trace flags> trace B0=frames, B1=verbose frames, B2=events, B3=EZSP\n"
" -v[base-port] enables virtual ISA support. The [base-port] argument\n"
" is optional. Both serial ports are available via telnet\n"
" instead of local console. RAW serial port is available\n"
" on the first port (offset 0 from base port), and CLI is\n"
" available on the second port (offset 1 from base port).\n"
" By default, 4900 is the base-port, therefore RAW access\n"
" is available from port 4900, and CLI access is available\n"
" on port 4901.\n"
" NOTE: No space is allowed between '-v' and [base-port].\n"
" -x 0,1 enable/disable data randomization\n";
" {ncp type} {options}\n"
" ncp type:\n"
" -n 0,1 0=EM2xx/EM3xx @ 115200 bps, RTS/CTS\n"
" 1=EM2xx/EM3xx @ 57600 bps, XON/XOFF\n"
" (if present must be the first option)\n"
" options:\n"
" -b <baud rate> 9600, 19200, 38400, 57600, 115200, etc.\n"
" -f r,x flow control: r=RST/CTS, x=XON/XOFF\n"
" -h display usage information\n"
" -i 0,1 enable/disable input buffering\n"
" -o 0,1 enable/disable output buffering\n"
" -p <port> serial port name or number (eg, COM1, ttyS0, or 1)\n"
" -r d,r,c ncp reset method: d=DTR, r=RST frame, c=custom\n"
" -s 1,2 stop bits\n"
" -t <trace flags> trace B0=frames, B1=verbose frames, B2=events, B3=EZSP\n"
" -v[base-port] enables virtual ISA support. The [base-port] argument\n"
" is optional. Both serial ports are available via telnet\n"
" instead of local console. RAW serial port is available\n"
" on the first port (offset 0 from base port), and CLI is\n"
" available on the second port (offset 1 from base port).\n"
" By default, 4900 is the base-port, therefore RAW access\n"
" is available from port 4900, and CLI access is available\n"
" on port 4901.\n"
" NOTE: No space is allowed between '-v' and [base-port].\n"
" -x 0,1 enable/disable data randomization\n";
static const AshCount zeroAshCount = { 0 };
......
......@@ -559,6 +559,7 @@ bool ezspSerialPortRegisterCallback(EzspSerialPortCallbackFunction callback)
void ezspSerialWriteByte(uint8_t byte)
{
BUMP_HOST_COUNTER(txBytes);
#ifdef IO_LOG
{
......
......@@ -66,10 +66,10 @@
void ezspPrintUsage(char *name)
{
char *shortName = strrchr(name, '/');
shortName = shortName ? shortName + 1 : name;
fprintf(stderr, "Usage: %s", shortName);
fprintf(stderr, usage);
char *shortName = strrchr(name, '/');
shortName = shortName ? shortName + 1 : name;
fprintf(stderr, "Usage: %s", shortName);
fprintf(stderr, usage);
}
#ifdef __CYGWIN__
......@@ -103,16 +103,16 @@ bool checkSerialPort(const char* portString, bool silent)
bool ezspProcessCommandOptions(int argc, char *argv[])
{
char errStr[ERR_LEN] = "";
if (!ezspInternalProcessCommandOptions(argc, argv, errStr)) {
return false;
}
if (*errStr != '\0') {
fprintf(stderr, "%s", errStr);
ezspPrintUsage(argv[0]);
}
return (*errStr == '\0');
char errStr[ERR_LEN] = "";
if (!ezspInternalProcessCommandOptions(argc, argv, errStr)) {
return false;
}
if (*errStr != '\0') {
fprintf(stderr, "%s", errStr);
ezspPrintUsage(argv[0]);
}
return (*errStr == '\0');
}
//------------------------------------------------------------------------------
......
......@@ -1122,26 +1122,26 @@ typedef struct {
* an upgrade file.
*/
typedef struct {
uint16_t manufacturerId;
uint16_t imageTypeId;
uint32_t firmwareVersion;
uint16_t manufacturerId;
uint16_t imageTypeId;
uint32_t firmwareVersion;
/**
* This is only used for device specific files.
* It will be set to all 0's when the image does not
* have an upgrade destination field in it.
* Little endian format.
*/
uint8_t deviceSpecificFileEui64[EUI64_SIZE];
/**
* This is only used for device specific files.
* It will be set to all 0's when the image does not
* have an upgrade destination field in it.
* Little endian format.
*/
uint8_t deviceSpecificFileEui64[EUI64_SIZE];
} EmberAfOtaImageId;
/**
* @brief The list of options possible for the image block request/response.
*/
enum {
EMBER_AF_IMAGE_BLOCK_REQUEST_OPTIONS_NONE = 0,
EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_CLIENT = 1,
EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_SERVER = 2,
EMBER_AF_IMAGE_BLOCK_REQUEST_OPTIONS_NONE = 0,
EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_CLIENT = 1,
EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_SERVER = 2,
};
typedef uint8_t EmberAfImageBlockRequestOptions;
......@@ -1150,18 +1150,18 @@ typedef uint8_t EmberAfImageBlockRequestOptions;
* emberAfImageBlockRequestCallback() to let the application decide what to do.
*/
typedef struct {
const EmberAfOtaImageId* id;
uint32_t offset;
const EmberAfOtaImageId* id;
uint32_t offset;
uint32_t waitTimeSecondsResponse;
EmberNodeId source;
EmberEUI64 sourceEui; // optionally present in messages
// The minBlockRequestPeriod can be treated as milliseconds or seconds on the
// client. The OTA server plugin has optional support to probe clients and
// treat this field with appropriate units (ms or sec)
uint16_t minBlockRequestPeriod; // optionally present in messages
uint8_t maxDataSize;
uint8_t clientEndpoint;
EmberAfImageBlockRequestOptions bitmask;
EmberNodeId source;
EmberEUI64 sourceEui; // optionally present in messages
// The minBlockRequestPeriod can be treated as milliseconds or seconds on the
// client. The OTA server plugin has optional support to probe clients and
// treat this field with appropriate units (ms or sec)
uint16_t minBlockRequestPeriod; // optionally present in messages
uint8_t maxDataSize;
uint8_t clientEndpoint;
EmberAfImageBlockRequestOptions bitmask;
} EmberAfImageBlockRequestCallbackStruct;
/**
......@@ -1169,11 +1169,11 @@ typedef struct {
* device operation.
*/
typedef enum {
EMBER_AF_OTA_STORAGE_SUCCESS = 0,
EMBER_AF_OTA_STORAGE_ERROR = 1,
EMBER_AF_OTA_STORAGE_RETURN_DATA_TOO_LONG = 2,
EMBER_AF_OTA_STORAGE_PARTIAL_FILE_FOUND = 3,
EMBER_AF_OTA_STORAGE_OPERATION_IN_PROGRESS = 4,
EMBER_AF_OTA_STORAGE_SUCCESS = 0,
EMBER_AF_OTA_STORAGE_ERROR = 1,
EMBER_AF_OTA_STORAGE_RETURN_DATA_TOO_LONG = 2,
EMBER_AF_OTA_STORAGE_PARTIAL_FILE_FOUND = 3,
EMBER_AF_OTA_STORAGE_OPERATION_IN_PROGRESS = 4,
} EmberAfOtaStorageStatus;
/**
......@@ -1203,41 +1203,41 @@ typedef uint8_t EmberAfOtaDownloadResult;
* It is not a byte-for-byte copy.
*/
typedef struct {
// Magic Number omitted since it is always the same.
uint16_t headerVersion;
uint16_t headerLength;
uint16_t fieldControl;
uint16_t manufacturerId;
uint16_t imageTypeId; // a.k.a. Device ID
uint32_t firmwareVersion;
uint16_t zigbeeStackVersion;
// Magic Number omitted since it is always the same.
uint16_t headerVersion;
uint16_t headerLength;
uint16_t fieldControl;
uint16_t manufacturerId;
uint16_t imageTypeId; // a.k.a. Device ID
uint32_t firmwareVersion;
uint16_t zigbeeStackVersion;
/**
* @brief The spec. does NOT require that the string be NULL terminated in the
* header stored on disk. Therefore we make sure we can support a
* 32-character string without a NULL terminator by adding +1 in the data
* structure.
*/
uint8_t headerString[EMBER_AF_OTA_MAX_HEADER_STRING_LENGTH + 1];
/**
* @brief The spec. does NOT require that the string be NULL terminated in the
* header stored on disk. Therefore we make sure we can support a
* 32-character string without a NULL terminator by adding +1 in the data
* structure.
*/
uint8_t headerString[EMBER_AF_OTA_MAX_HEADER_STRING_LENGTH + 1];
/**
* @brief When reading the header this will be the complete length of
* the file. When writing the header, this must be set to
* the length of the MFG image data portion including all tags.
*/
uint32_t imageSize;
/**
* @brief When reading the header this will be the complete length of
* the file. When writing the header, this must be set to
* the length of the MFG image data portion including all tags.
*/
uint32_t imageSize;
/**
* @brief The remaining four fields are optional. The field control should be checked
* to determine if their values are valid.
*/
uint8_t securityCredentials;
union {
uint8_t EUI64[EUI64_SIZE];
uint8_t UID[UID_SIZE];
} upgradeFileDestination;
uint16_t minimumHardwareVersion;
uint16_t maximumHardwareVersion;
/**
* @brief The remaining four fields are optional. The field control should be checked
* to determine if their values are valid.
*/
uint8_t securityCredentials;
union {
uint8_t EUI64[EUI64_SIZE];
uint8_t UID[UID_SIZE];
} upgradeFileDestination;
uint16_t minimumHardwareVersion;
uint16_t maximumHardwareVersion;
} EmberAfOtaHeader;
/**
......
......@@ -89,94 +89,94 @@ static int myVprintf(int fd, const char* formatString, va_list ap);
EmberStatus backchannelStartServer(uint8_t port)
{
struct sockaddr_in serverAddress;
int flags;
if (!backchannelEnable) {
return EMBER_INVALID_CALL;
}
if (port > 1) {
return EMBER_SERIAL_INVALID_PORT;
}
if (socketFd[port] != -1) {
return EMBER_INVALID_CALL;
}
socketFd[port] = socket(AF_INET, SOCK_STREAM, 0);
if (socketFd[port] < 0) {
unixError("Error: Could not open socket");
return EMBER_ERR_FATAL;
}
flags = 1; // Enable SO_REUSEADDR to reduce bind() complaints
struct sockaddr_in serverAddress;
int flags;
if (!backchannelEnable) {
return EMBER_INVALID_CALL;
}
if (port > 1) {
return EMBER_SERIAL_INVALID_PORT;
}
if (socketFd[port] != -1) {
return EMBER_INVALID_CALL;
}
socketFd[port] = socket(AF_INET, SOCK_STREAM, 0);
if (socketFd[port] < 0) {
unixError("Error: Could not open socket");
return EMBER_ERR_FATAL;
}
flags = 1; // Enable SO_REUSEADDR to reduce bind() complaints
(void) setsockopt(socketFd[port], SOL_SOCKET, SO_REUSEADDR, &flags,
sizeof(flags));
bzero(&(clientConnections[port]), sizeof(struct sockaddr_in));
bzero((char *) &serverAddress, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_ANY;
serverAddress.sin_port = htons(SERVER_PORT_OFFSET + port);
if (bind(socketFd[port],
(struct sockaddr *) &serverAddress,
sizeof(serverAddress)) < 0) {
unixError("Error: Could not bind socket to %u", SERVER_PORT_OFFSET + port);
return EMBER_ERR_FATAL;
}
if (0 > listen(socketFd[port], 0)) {
unixError("Error: Could not mark socket as listening");
return EMBER_ERR_FATAL;
}
infoPrint("Listening for connections on port %u", SERVER_PORT_OFFSET + port);
return EMBER_SUCCESS;
bzero(&(clientConnections[port]), sizeof(struct sockaddr_in));
bzero((char *) &serverAddress, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_ANY;
serverAddress.sin_port = htons(SERVER_PORT_OFFSET + port);
if (bind(socketFd[port],
(struct sockaddr *) &serverAddress,
sizeof(serverAddress)) < 0) {
unixError("Error: Could not bind socket to %u", SERVER_PORT_OFFSET + port);
return EMBER_ERR_FATAL;
}
if (0 > listen(socketFd[port], 0)) {
unixError("Error: Could not mark socket as listening");
return EMBER_ERR_FATAL;
}
infoPrint("Listening for connections on port %u", SERVER_PORT_OFFSET + port);
return EMBER_SUCCESS;
}
EmberStatus backchannelClientConnectionCleanup(uint8_t port)
{
if (!backchannelEnable) {
return EMBER_INVALID_CALL;
}
if (port > 1) {
return EMBER_SERIAL_INVALID_PORT;
}
if (clientFd[port] != INVALID_FD) {
myPrintf(REMOTE_STDOUT,
"Server closing client connection from %s:%u on port %u\n",
inet_ntoa(clientConnections[port].sin_addr),
ntohs(clientConnections[port].sin_port),
SERVER_PORT_OFFSET + port);
infoPrint("Server closing client connection from %s:%u on port %u\n",
inet_ntoa(clientConnections[port].sin_addr),
ntohs(clientConnections[port].sin_port),
SERVER_PORT_OFFSET + port);
close(clientFd[port]);
clientFd[port] = INVALID_FD;
bzero(&(clientConnections[port]), sizeof(struct sockaddr_in));
}
return EMBER_SUCCESS;
if (!backchannelEnable) {
return EMBER_INVALID_CALL;
}
if (port > 1) {
return EMBER_SERIAL_INVALID_PORT;
}
if (clientFd[port] != INVALID_FD) {
myPrintf(REMOTE_STDOUT,
"Server closing client connection from %s:%u on port %u\n",
inet_ntoa(clientConnections[port].sin_addr),
ntohs(clientConnections[port].sin_port),
SERVER_PORT_OFFSET + port);
infoPrint("Server closing client connection from %s:%u on port %u\n",
inet_ntoa(clientConnections[port].sin_addr),
ntohs(clientConnections[port].sin_port),
SERVER_PORT_OFFSET + port);
close(clientFd[port]);
clientFd[port] = INVALID_FD;
bzero(&(clientConnections[port]), sizeof(struct sockaddr_in));
}
return EMBER_SUCCESS;
}
EmberStatus backchannelStopServer(uint8_t port)
{
if (!backchannelEnable) {
return EMBER_INVALID_CALL;
}
if (port > 1) {
return EMBER_SERIAL_INVALID_PORT;
}
backchannelClientConnectionCleanup(port);
if (socketFd[port] != INVALID_FD) {
myPrintf(LOCAL_STDOUT, "Server closing socket connection %u\n",
SERVER_PORT_OFFSET + port);
close(socketFd[port]);
socketFd[port] = INVALID_FD;
}
return EMBER_SUCCESS;
if (!backchannelEnable) {
return EMBER_INVALID_CALL;
}
if (port > 1) {
return EMBER_SERIAL_INVALID_PORT;
}
backchannelClientConnectionCleanup(port);
if (socketFd[port] != INVALID_FD) {
myPrintf(LOCAL_STDOUT, "Server closing socket connection %u\n",
SERVER_PORT_OFFSET + port);
close(socketFd[port]);
socketFd[port] = INVALID_FD;
}
return EMBER_SUCCESS;
}
// Retrieves a single byte from the client connection. Returns the number
......@@ -226,23 +226,23 @@ EmberStatus backchannelSend(uint8_t port, uint8_t * data, uint8_t length)
// If one doesn't exist, it can wait for a new connection and return
// the result.
BackchannelState backchannelCheckConnection(uint8_t port,
bool waitForConnection)
bool waitForConnection)
{
if (!backchannelEnable) {
return CONNECTION_ERROR;
} else if (clientFd[port] >= 0) {
return CONNECTION_EXISTS;
}
bool getConnection = false;
if (waitForConnection) {
infoPrint("Waiting for client connection on port %u",
port + SERVER_PORT_OFFSET);
getConnection = true;
} else if (!willServerConnectionBlock(port)) {
getConnection = true;
}
if (!backchannelEnable) {
return CONNECTION_ERROR;
} else if (clientFd[port] >= 0) {
return CONNECTION_EXISTS;
}
bool getConnection = false;
if (waitForConnection) {
infoPrint("Waiting for client connection on port %u",
port + SERVER_PORT_OFFSET);
getConnection = true;
} else if (!willServerConnectionBlock(port)) {
getConnection = true;
}
if (getConnection) {
return (getNewConnection(port)
......@@ -450,32 +450,32 @@ static bool getNewConnection(uint8_t port)
// Print an error message plus the associated 'errno' string
static void unixError(const char* format, ...)
{
va_list ap = { 0 };
va_start(ap, format);
myVprintf(LOCAL_STDERR, format, ap);
va_end(ap);
myPrintf(LOCAL_STDERR, ": %s\n", strerror(errno));
va_list ap = { 0 };
va_start(ap, format);
myVprintf(LOCAL_STDERR, format, ap);
va_end(ap);
myPrintf(LOCAL_STDERR, ": %s\n", strerror(errno));
}
static void debugPrint(const char* formatString, ...)
{
if (debugOn) {
va_list ap = { 0 };
myPrintf(LOCAL_STDERR, "[%s] ", debugString);
va_start(ap, formatString);
myVprintf(LOCAL_STDERR, formatString, ap);
va_end(ap);
myPrintf(LOCAL_STDERR, "\n");
}
if (debugOn) {
va_list ap = { 0 };
myPrintf(LOCAL_STDERR, "[%s] ", debugString);
va_start(ap, formatString);
myVprintf(LOCAL_STDERR, formatString, ap);
va_end(ap);
myPrintf(LOCAL_STDERR, "\n");
}
}
static void infoPrint(const char* formatString, ...)
{
va_list ap = { 0 };
va_start(ap, formatString);
myVprintf(LOCAL_STDOUT, formatString, ap);
va_end(ap);
myPrintf(LOCAL_STDOUT, "\n");
va_list ap = { 0 };
va_start(ap, formatString);
myVprintf(LOCAL_STDOUT, formatString, ap);
va_end(ap);
myPrintf(LOCAL_STDOUT, "\n");
}
// Because we mess around with FDs, we need our own printing routine
......@@ -483,20 +483,20 @@ static void infoPrint(const char* formatString, ...)
static int myPrintf(int fd, const char* formatString, ...)
{
va_list ap = { 0 };
int returnCode;
va_start(ap, formatString);
returnCode = myVprintf(fd, formatString, ap);
va_end(ap);
return returnCode;
va_list ap = { 0 };
int returnCode;
va_start(ap, formatString);
returnCode = myVprintf(fd, formatString, ap);
va_end(ap);
return returnCode;
}
// Returns 0 on success, 1 on error
static int myVprintf(int fd, const char* formatString, va_list ap)
{
int length;
char string[MAX_STRING_LENGTH];
length = vsnprintf(string, MAX_STRING_LENGTH - 1, formatString, ap);
string[length] = '\0';
return (length != write(fd, string, length));
int length;
char string[MAX_STRING_LENGTH];
length = vsnprintf(string, MAX_STRING_LENGTH - 1, formatString, ap);
string[length] = '\0';
return (length != write(fd, string, length));
}
......@@ -65,28 +65,28 @@ static void ezspSerialPortCallback(EzspSerialPortEvent event, int fileDescriptor
static EmberStatus gatewayBackchannelStart(void)
{
if (backchannelEnable) {
if (EMBER_SUCCESS != backchannelStartServer(SERIAL_PORT_CLI)) {
fprintf(stderr,
"Fatal: Failed to start backchannel services for CLI.\n");
return EMBER_ERR_FATAL;
}
if (EMBER_SUCCESS != backchannelStartServer(SERIAL_PORT_RAW)) {
fprintf(stderr,
"Fatal: Failed to start backchannel services for RAW data.\n");
return EMBER_ERR_FATAL;
}
}
return EMBER_SUCCESS;
if (backchannelEnable) {
if (EMBER_SUCCESS != backchannelStartServer(SERIAL_PORT_CLI)) {
fprintf(stderr,
"Fatal: Failed to start backchannel services for CLI.\n");
return EMBER_ERR_FATAL;
}
if (EMBER_SUCCESS != backchannelStartServer(SERIAL_PORT_RAW)) {
fprintf(stderr,
"Fatal: Failed to start backchannel services for RAW data.\n");
return EMBER_ERR_FATAL;
}
}
return EMBER_SUCCESS;
}
void gatewayBackchannelStop(void)
{
if (backchannelEnable) {
backchannelStopServer(SERIAL_PORT_CLI);
backchannelStopServer(SERIAL_PORT_RAW);
}
if (backchannelEnable) {
backchannelStopServer(SERIAL_PORT_CLI);
backchannelStopServer(SERIAL_PORT_RAW);
}
}
bool emberAfMainStartCallback(int* returnCode,
......@@ -95,21 +95,21 @@ bool emberAfMainStartCallback(int* returnCode,
{
debugPrint("gatewaitInit()");
// This will process EZSP command-line options as well as determine
// whether the backchannel should be turned on.
if (!ezspProcessCommandOptions(argc, argv)) {
*returnCode = EMBER_ERR_FATAL;
return true;
}
// This will process EZSP command-line options as well as determine
// whether the backchannel should be turned on.
if (!ezspProcessCommandOptions(argc, argv)) {
*returnCode = EMBER_ERR_FATAL;
return true;
}
*returnCode = gatewayBackchannelStart();
if (*returnCode != EMBER_SUCCESS) {
return true;
}
*returnCode = gatewayBackchannelStart();
if (*returnCode != EMBER_SUCCESS) {
return true;
}
emberSerialSetPrompt(cliPrompt);
emberSerialSetPrompt(cliPrompt);
emberSerialCommandCompletionInit(emberCommandTable);
emberSerialCommandCompletionInit(emberCommandTable);
return false;
}
......
......@@ -3,10 +3,10 @@ void gatewayBackchannelStop(void);
typedef uint8_t BackchannelState;
enum {
NO_CONNECTION = 0,
CONNECTION_EXISTS = 1,
NEW_CONNECTION = 2,
CONNECTION_ERROR = 3,
NO_CONNECTION = 0,
CONNECTION_EXISTS = 1,
NEW_CONNECTION = 2,
CONNECTION_ERROR = 3,
};
extern const bool backchannelSupported;
......
......@@ -38,7 +38,7 @@ void emAfOtaImageDelete(void);
// Server CLI interface
#if !defined (EMBER_AF_PLUGIN_OTA_SERVER)
#define OTA_SERVER_COMMANDS
#define OTA_SERVER_COMMANDS
#endif
void otaImageNotifyCommand(void);
......@@ -21,9 +21,9 @@
// This relates all OTA command IDs in app/framework/gen/command-id.h
// to minimum message lengths (does not include EMBER_AF_ZCL_OVERHEAD)
PGM uint8_t emAfOtaMinMessageLengths[] = {
2, // Image Notify
2, // Image Notify
8, // Query Next Image Request
1, // Query Next Image Response
1, // Query Next Image Response
13, // Image Block Request
13, // Image Page Request
1, // Image Block Response (abort is shortest)
......@@ -48,14 +48,14 @@ EmberAfOtaImageId emAfOtaCreateEmberAfOtaImageIdStruct(uint16_t manufacturerId,
// This assumes the message has already been validated for its length
uint8_t emAfOtaParseImageIdFromMessage(EmberAfOtaImageId* returnId,
const uint8_t* buffer,
uint8_t length)
const uint8_t* buffer,
uint8_t length)
{
returnId->manufacturerId = emberAfGetInt16u(buffer, 0, length);
returnId->imageTypeId = emberAfGetInt16u(buffer, 2, length);
returnId->firmwareVersion = emberAfGetInt32u(buffer, 4, length);
MEMSET(returnId->deviceSpecificFileEui64, 0, EUI64_SIZE);
return 8;
returnId->manufacturerId = emberAfGetInt16u(buffer, 0, length);
returnId->imageTypeId = emberAfGetInt16u(buffer, 2, length);
returnId->firmwareVersion = emberAfGetInt32u(buffer, 4, length);
MEMSET(returnId->deviceSpecificFileEui64, 0, EUI64_SIZE);
return 8;
}
#if defined(EMBER_AF_PRINT_CORE)
......
......@@ -22,19 +22,19 @@
// -----------------------------------------------------------------------------
typedef enum {
UPGRADE_IF_SERVER_HAS_NEWER = 0,
DOWNGRADE_IF_SERVER_HAS_OLDER = 1,
REINSTALL_IF_SERVER_HAS_SAME = 2,
NO_NEXT_VERSION = 3,
UPGRADE_IF_SERVER_HAS_NEWER = 0,
DOWNGRADE_IF_SERVER_HAS_OLDER = 1,
REINSTALL_IF_SERVER_HAS_SAME = 2,
NO_NEXT_VERSION = 3,
} NextVersionPolicy;
#define QUERY_POLICY_MAX NO_NEXT_VERSION
#ifdef EMBER_COMMAND_INTEPRETER_HAS_DESCRIPTION_FIELD
static PGM_P nextVersionPolicyStrings[] = {
"Upgrade if server has newer",
"Downgrade if server has older",
"Reinstall if server has same",
"No next version",
"Upgrade if server has newer",
"Downgrade if server has older",
"Reinstall if server has same",
"No next version",
};
#endif // EMBER_COMMAND_INTEPRETER_HAS_DESCRIPTION_FIELD
......@@ -70,10 +70,10 @@ PGM_P imageBlockRequestPolicyStrings[] = {
#endif
typedef enum {
UPGRADE_NOW = 0,
UPGRADE_SOON = 1,
UPGRADE_ASK_ME_LATER = 2,
UPGRADE_ABORT = 3,
UPGRADE_NOW = 0,
UPGRADE_SOON = 1,
UPGRADE_ASK_ME_LATER = 2,
UPGRADE_ABORT = 3,
} UpgradePolicy;
#define UPGRADE_POLICY_MAX UPGRADE_ABORT
......@@ -81,18 +81,18 @@ UpgradePolicy upgradePolicy = UPGRADE_NOW;
#define UPGRADE_SOON_TIME_SECONDS (2 * 60)
PGM_P upgradePolicyStrings[] = {
"Upgrade Now",
"Upgrade In a few minutes",
"Ask me later to upgrade",
"Abort upgrade",
"Upgrade Now",
"Upgrade In a few minutes",
"Ask me later to upgrade",
"Abort upgrade",
};
// This corresponds to the enumerated UpgradePolicy list.
PGM uint32_t upgradeTimes[] = {
0, // Now
UPGRADE_SOON_TIME_SECONDS, // in a little while
0xFFFFFFFFL, // go ask your father (wait forever)
0, // unused
0, // Now
UPGRADE_SOON_TIME_SECONDS, // in a little while
0xFFFFFFFFL, // go ask your father (wait forever)
0, // unused
};
#if defined EM_AF_TEST_HARNESS_CODE
......@@ -112,8 +112,8 @@ static uint8_t pageRequestStatus = PAGE_REQUEST_STATUS_CODE;
static uint16_t otaMinimumBlockPeriodMs = 0;
#define SERVER_AND_CLIENT_SUPPORT_MIN_BLOCK_REQUEST \
(EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_CLIENT \
| EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_SERVER)
(EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_CLIENT \
| EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_SERVER)
#ifdef EMBER_TEST
uint8_t testClientDelayUnit = OTA_SERVER_DO_NOT_OVERRIDE_CLIENT_DELAY_UNITS;
......@@ -164,135 +164,140 @@ void emAfOtaServerPolicyPrint(void)
}
static bool determineNextSoftwareVersion(uint32_t versionServerHas,
uint32_t versionClientHas)
uint32_t versionClientHas)
{
// Our system here controls whether we tell the client to
// (A) upgrade, because we have a newer version
// (B) downgrade, because we have an older version we want to install
// (C) reinstall, because we have the same version you have currently
// (D) do nothing (no 'next' image is avaiable)
switch (nextVersionPolicy) {
case UPGRADE_IF_SERVER_HAS_NEWER:
if (versionServerHas > versionClientHas) {
return true;
}
break;
case DOWNGRADE_IF_SERVER_HAS_OLDER:
if (versionServerHas < versionClientHas) {
return true;
}
break;
case REINSTALL_IF_SERVER_HAS_SAME:
if (versionServerHas == versionClientHas) {
return true;
}
break;
case NO_NEXT_VERSION:
default:
break;
}
return false;
// Our system here controls whether we tell the client to
// (A) upgrade, because we have a newer version
// (B) downgrade, because we have an older version we want to install
// (C) reinstall, because we have the same version you have currently
// (D) do nothing (no 'next' image is avaiable)
switch (nextVersionPolicy) {
case UPGRADE_IF_SERVER_HAS_NEWER:
if (versionServerHas > versionClientHas) {
return true;
}
break;
case DOWNGRADE_IF_SERVER_HAS_OLDER:
if (versionServerHas < versionClientHas) {
return true;
}
break;
case REINSTALL_IF_SERVER_HAS_SAME:
if (versionServerHas == versionClientHas) {
return true;
}
break;
case NO_NEXT_VERSION:
default:
break;
}
return false;
}
uint8_t emberAfOtaServerQueryCallback(const EmberAfOtaImageId* currentImageId,
uint16_t* hardwareVersion,
EmberAfOtaImageId* nextUpgradeImageId)
uint16_t* hardwareVersion,
EmberAfOtaImageId* nextUpgradeImageId)
{
// This function is called by the OTA cluster server to determine what
// the 'next' version of software is for a particular device requesting
// a new download image. The server returns a status code indicating
// EMBER_ZCL_STATUS_NO_IMAGE_AVAILABLE, or EMBER_ZCL_STATUS_SUCCESS
// (new image is available). It then also fills in the 'nextUpgradeImageId'
// structure with the appropriate version.
// The server can use whatever criteria it wants to dictate what
// the 'next' version is and if it is currently available.
// This sample does this based on a global policy value.
uint8_t status = EMBER_ZCL_STATUS_NO_IMAGE_AVAILABLE;
bool hardwareVersionMismatch = false;
otaPrintln("QueryNextImageRequest mfgId:0x%2x imageTypeId:0x%2x, fw:0x%4x",
currentImageId->manufacturerId,
currentImageId->imageTypeId,
currentImageId->firmwareVersion);
*nextUpgradeImageId
= emberAfOtaStorageSearchCallback(currentImageId->manufacturerId,
currentImageId->imageTypeId,
hardwareVersion);
if (emberAfIsOtaImageIdValid(nextUpgradeImageId)) {
// We only perform a check if both the query and the
// file have hardware version(s). If one or the other doesn't
// have them, we assume a match is still possible.
if (hardwareVersion) {
EmberAfOtaHeader header;
emberAfOtaStorageGetFullHeaderCallback(nextUpgradeImageId,
&header);
if (header.fieldControl & HARDWARE_VERSIONS_PRESENT_MASK) {
if (*hardwareVersion < header.minimumHardwareVersion
|| header.maximumHardwareVersion < *hardwareVersion) {
otaPrintln("Hardware version 0x%02X does not fall within the min (0x%02X) and max (0x%02X) hardware versions in the file.",
*hardwareVersion,
header.minimumHardwareVersion,
header.maximumHardwareVersion);
hardwareVersionMismatch = true;
}
}
}
// "!hardwareVersionMismatch" does not mean the hardware
// versions match. It just means we don't *disqualify* the image
// as a potential upgrade candidate because the hardware is out
// of range.
if (!hardwareVersionMismatch) {
status = (determineNextSoftwareVersion(nextUpgradeImageId->firmwareVersion,
currentImageId->firmwareVersion)
? EMBER_ZCL_STATUS_SUCCESS
: EMBER_ZCL_STATUS_NO_IMAGE_AVAILABLE);
if (status == EMBER_ZCL_STATUS_SUCCESS) {
otaPrintln("Next fw version is: 0x%4X",
nextUpgradeImageId->firmwareVersion);
}
}
}
return status;
// This function is called by the OTA cluster server to determine what
// the 'next' version of software is for a particular device requesting
// a new download image. The server returns a status code indicating
// EMBER_ZCL_STATUS_NO_IMAGE_AVAILABLE, or EMBER_ZCL_STATUS_SUCCESS
// (new image is available). It then also fills in the 'nextUpgradeImageId'
// structure with the appropriate version.
// The server can use whatever criteria it wants to dictate what
// the 'next' version is and if it is currently available.
// This sample does this based on a global policy value.
uint8_t status = EMBER_ZCL_STATUS_NO_IMAGE_AVAILABLE;
bool hardwareVersionMismatch = false;
otaPrintln("QueryNextImageRequest mfgId:0x%2x imageTypeId:0x%2x, fw:0x%4x",
currentImageId->manufacturerId,
currentImageId->imageTypeId,
currentImageId->firmwareVersion);
*nextUpgradeImageId
= emberAfOtaStorageSearchCallback(currentImageId->manufacturerId,
currentImageId->imageTypeId,
hardwareVersion);
if (emberAfIsOtaImageIdValid(nextUpgradeImageId)) {
// We only perform a check if both the query and the
// file have hardware version(s). If one or the other doesn't
// have them, we assume a match is still possible.
otaPrintln("[OTA]2222,hardwareVersion=%d",hardwareVersion);
if (hardwareVersion) {
EmberAfOtaHeader header;
emberAfOtaStorageGetFullHeaderCallback(nextUpgradeImageId,
&header);
if (header.fieldControl & HARDWARE_VERSIONS_PRESENT_MASK) {
otaPrintln("[OTA]3333");
if (*hardwareVersion < header.minimumHardwareVersion
|| header.maximumHardwareVersion < *hardwareVersion) {
otaPrintln("Hardware version 0x%02X does not fall within the min (0x%02X) and max (0x%02X) hardware versions in the file.",
*hardwareVersion,
header.minimumHardwareVersion,
header.maximumHardwareVersion);
hardwareVersionMismatch = true;
}
}
}
// "!hardwareVersionMismatch" does not mean the hardware
// versions match. It just means we don't *disqualify* the image
// as a potential upgrade candidate because the hardware is out
// of range.
if (!hardwareVersionMismatch) {
otaPrintln("[OTA]4444,%x,%x",nextUpgradeImageId->firmwareVersion,currentImageId->firmwareVersion);
status = (determineNextSoftwareVersion(nextUpgradeImageId->firmwareVersion,
currentImageId->firmwareVersion)
? EMBER_ZCL_STATUS_SUCCESS
: EMBER_ZCL_STATUS_NO_IMAGE_AVAILABLE);
otaPrintln("[OTA]555,status=%x",status);
if (status == EMBER_ZCL_STATUS_SUCCESS) {
otaPrintln("Next fw version is: 0x%4X",
nextUpgradeImageId->firmwareVersion);
}
}
}
otaPrintln("[OTA]11111");
return status;
}
uint8_t emberAfOtaServerBlockSizeCallback(EmberNodeId clientNodeId)
{
// This function provides a way for the server to potentially
// adjust the block size based on the client who is requesting.
// In other words if we are using source routing we will limit
// data returned by enough to put a source route into the message.
EmberApsFrame apsFrame;
uint8_t maxSize;
apsFrame.options = EMBER_APS_OPTION_NONE;
if (emberAfIsCurrentSecurityProfileSmartEnergy()) {
apsFrame.options |= EMBER_APS_OPTION_ENCRYPTION;
}
maxSize = emberAfMaximumApsPayloadLength(EMBER_OUTGOING_DIRECT,
clientNodeId,
&apsFrame);
maxSize -= IMAGE_BLOCK_RESPONSE_OVERHEAD;
return maxSize;
// This function provides a way for the server to potentially
// adjust the block size based on the client who is requesting.
// In other words if we are using source routing we will limit
// data returned by enough to put a source route into the message.
EmberApsFrame apsFrame;
uint8_t maxSize;
apsFrame.options = EMBER_APS_OPTION_NONE;
if (emberAfIsCurrentSecurityProfileSmartEnergy()) {
apsFrame.options |= EMBER_APS_OPTION_ENCRYPTION;
}
maxSize = emberAfMaximumApsPayloadLength(EMBER_OUTGOING_DIRECT,
clientNodeId,
&apsFrame);
maxSize -= IMAGE_BLOCK_RESPONSE_OVERHEAD;
return maxSize;
}
uint8_t emberAfOtaServerImageBlockRequestCallback(EmberAfImageBlockRequestCallbackStruct* data)
{
uint8_t status;
uint8_t status;
uint16_t serverBlockPeriodValue = otaMinimumBlockPeriodMs;
uint8_t clientDelayUnit;
bool useSecondsDelay = false;
bool useSecondsDelay = false;
if (SERVER_AND_CLIENT_SUPPORT_MIN_BLOCK_REQUEST
== (data->bitmask & SERVER_AND_CLIENT_SUPPORT_MIN_BLOCK_REQUEST)) {
clientDelayUnit = emberAfPluginOtaServerPolicyGetClientDelayUnits(
data->source,
data->sourceEui);
if (SERVER_AND_CLIENT_SUPPORT_MIN_BLOCK_REQUEST
== (data->bitmask & SERVER_AND_CLIENT_SUPPORT_MIN_BLOCK_REQUEST)) {
clientDelayUnit = emberAfPluginOtaServerPolicyGetClientDelayUnits(
data->source,
data->sourceEui);
#ifdef EMBER_TEST
if (OTA_SERVER_DO_NOT_OVERRIDE_CLIENT_DELAY_UNITS != testClientDelayUnit) {
......@@ -300,46 +305,46 @@ uint8_t emberAfOtaServerImageBlockRequestCallback(EmberAfImageBlockRequestCallba
}
#endif // EMBER_TEST
switch (clientDelayUnit) {
case OTA_SERVER_NO_RATE_LIMITING_FOR_CLIENT:
return EMBER_ZCL_STATUS_SUCCESS;
break;
case OTA_SERVER_CLIENT_USES_MILLISECONDS:
break;
case OTA_SERVER_CLIENT_USES_SECONDS:
useSecondsDelay = true;
break;
case OTA_SERVER_DISCOVER_CLIENT_DELAY_UNITS:
{
// If we support dynamic block request, check to see if we know how the
// client treats the field. If we haven't tested the client yet or don't
// have a spot for it as an active OTA session, we tell it to
// WAIT_FOR_DATA
status = emAfOtaServerCheckDynamicBlockPeriodDownload(data);
if (EMBER_ZCL_STATUS_WAIT_FOR_DATA == status) {
return EMBER_ZCL_STATUS_WAIT_FOR_DATA;
}
useSecondsDelay = emAfOtaServerDynamicBlockPeriodClientUsesSeconds(data->source);
}
break;
default:
break;
}
if (useSecondsDelay) {
serverBlockPeriodValue /= 1000;
}
if (data->minBlockRequestPeriod != serverBlockPeriodValue) {
data->minBlockRequestPeriod = serverBlockPeriodValue;
return EMBER_ZCL_STATUS_WAIT_FOR_DATA;
}
}
switch (clientDelayUnit) {
case OTA_SERVER_NO_RATE_LIMITING_FOR_CLIENT:
return EMBER_ZCL_STATUS_SUCCESS;
break;
case OTA_SERVER_CLIENT_USES_MILLISECONDS:
break;
case OTA_SERVER_CLIENT_USES_SECONDS:
useSecondsDelay = true;
break;
case OTA_SERVER_DISCOVER_CLIENT_DELAY_UNITS:
{
// If we support dynamic block request, check to see if we know how the
// client treats the field. If we haven't tested the client yet or don't
// have a spot for it as an active OTA session, we tell it to
// WAIT_FOR_DATA
status = emAfOtaServerCheckDynamicBlockPeriodDownload(data);
if (EMBER_ZCL_STATUS_WAIT_FOR_DATA == status) {
return EMBER_ZCL_STATUS_WAIT_FOR_DATA;
}
useSecondsDelay = emAfOtaServerDynamicBlockPeriodClientUsesSeconds(data->source);
}
break;
default:
break;
}
if (useSecondsDelay) {
serverBlockPeriodValue /= 1000;
}
if (data->minBlockRequestPeriod != serverBlockPeriodValue) {
data->minBlockRequestPeriod = serverBlockPeriodValue;
return EMBER_ZCL_STATUS_WAIT_FOR_DATA;
}
}
#if defined EM_AF_TEST_HARNESS_CODE
// TEST Harness code
......@@ -357,41 +362,41 @@ uint8_t emberAfOtaServerImageBlockRequestCallback(EmberAfImageBlockRequestCallba
}
#endif
return EMBER_ZCL_STATUS_SUCCESS;
return EMBER_ZCL_STATUS_SUCCESS;
}
bool emberAfOtaServerUpgradeEndRequestCallback(EmberNodeId source,
uint8_t status,
uint32_t* returnValue,
const EmberAfOtaImageId* imageId)
uint8_t status,
uint32_t* returnValue,
const EmberAfOtaImageId* imageId)
{
otaPrintln("Client 0x%2X indicated upgrade status: 0x%X",
source,
status);
otaPrintln("Client 0x%2X indicated upgrade status: 0x%X",
source,
status);
emAfOtaServerCompleteDynamicBlockPeriodDownload(source);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
// If status != EMBER_ZCL_STATUS_SUCCESS then this callback is
// only informative. Return code will be ignored.
return false;
}
if (status != EMBER_ZCL_STATUS_SUCCESS) {
// If status != EMBER_ZCL_STATUS_SUCCESS then this callback is
// only informative. Return code will be ignored.
return false;
}
otaPrintln("Upgrade End Response: %p", upgradePolicyStrings[upgradePolicy]);
otaPrintln("Upgrade End Response: %p", upgradePolicyStrings[upgradePolicy]);
if (upgradePolicy == UPGRADE_ABORT) {
return false;
}
if (upgradePolicy == UPGRADE_ABORT) {
return false;
}
*returnValue = upgradeTimes[upgradePolicy];
return true;
*returnValue = upgradeTimes[upgradePolicy];
return true;
}
void emAfOtaServerSetQueryPolicy(uint8_t value)
{
if (value <= QUERY_POLICY_MAX) {
nextVersionPolicy = (NextVersionPolicy)value;
}
if (value <= QUERY_POLICY_MAX) {
nextVersionPolicy = (NextVersionPolicy)value;
}
}
void emAfOtaServerSetBlockRequestPolicy(uint8_t value)
......@@ -401,7 +406,7 @@ void emAfOtaServerSetBlockRequestPolicy(uint8_t value)
imageBlockRequestPolicy = (ImageBlockRequestPolicy)value;
}
#else
otaPrintln("Unsupported.");
otaPrintln("Unsupported.");
#endif
}
......
......@@ -149,9 +149,9 @@ static PGM_P notifyArguments[] = {
emberCommandEntryTerminator(),
EmberCommandEntry emberAfPluginOtaServerCommands[] = {
OTA_SERVER_COMMANDS
OTA_SERVER_COMMANDS
emberCommandEntryTerminator(),
emberCommandEntryTerminator(),
};
#endif // EMBER_AF_GENERATE_CLI
......
......@@ -79,13 +79,13 @@ uint8_t getIndexForDownloadingNodeId(EmberNodeId nodeId)
void emAfOtaServerDynamicBlockPeriodInit()
{
// Initialize array to have no current OTA downloads
uint8_t nodeIndex;
for (nodeIndex = 0; nodeIndex < MAX_DOWNLOADS; nodeIndex++) {
downloadingNodes[nodeIndex].nodeId = EMBER_NULL_NODE_ID;
downloadingNodes[nodeIndex].unitDiscoveryState = STATE_NONE;
downloadingNodes[nodeIndex].msTickWhenLastSeen = 0;
}
// Initialize array to have no current OTA downloads
uint8_t nodeIndex;
for (nodeIndex = 0; nodeIndex < MAX_DOWNLOADS; nodeIndex++) {
downloadingNodes[nodeIndex].nodeId = EMBER_NULL_NODE_ID;
downloadingNodes[nodeIndex].unitDiscoveryState = STATE_NONE;
downloadingNodes[nodeIndex].msTickWhenLastSeen = 0;
}
}
void emAfOtaServerDynamicBlockPeriodTick()
......
......@@ -111,11 +111,11 @@ uint8_t emAfOtaPageRequestHandler(uint8_t clientEndpoint,
void emAfOtaPageRequestTick(uint8_t endpoint)
{
if (requesterNodeId == EMBER_NULL_NODE_ID) {
return;
}
if (requesterNodeId == EMBER_NULL_NODE_ID) {
return;
}
sendBlockRequest();
sendBlockRequest();
emberAfScheduleServerTickExtended(endpoint,
ZCL_OTA_BOOTLOAD_CLUSTER_ID,
requesterResponseSpacing,
......
......@@ -46,9 +46,9 @@ uint8_t otaServerEndpoint = 0; // invalid endpoint
#define MAX_POSSIBLE_SERVER_BLOCK_SIZE 63
#if defined(EMBER_AF_PLUGIN_OTA_SERVER_MIN_BLOCK_REQUEST_SUPPORT)
#define MIN_BLOCK_REQUEST_SERVER_SUPPORT EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_SERVER
#define MIN_BLOCK_REQUEST_SERVER_SUPPORT EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_SERVER
#else
#define MIN_BLOCK_REQUEST_SERVER_SUPPORT 0
#define MIN_BLOCK_REQUEST_SERVER_SUPPORT 0
#endif
// -----------------------------------------------------------------------------
......@@ -58,7 +58,7 @@ uint8_t otaServerEndpoint = 0; // invalid endpoint
static bool commandParse(EmberAfClusterCommand* command);
#define prepareClusterResponse(commandId, status) \
emAfOtaServerPrepareResponse(false, \
emAfOtaServerPrepareResponse(false, \
(commandId), \
(status), \
0) // defaultResponsePayloadCommandId
......@@ -77,14 +77,14 @@ static void addEmberAfOtaImageIdIntoResponse(const EmberAfOtaImageId* id);
void emberAfOtaBootloadClusterServerInitCallback(uint8_t endpoint)
{
emberAfOtaStorageInitCallback();
otaServerEndpoint = endpoint;
emAfOtaServerDynamicBlockPeriodInit();
emberAfOtaStorageInitCallback();
otaServerEndpoint = endpoint;
emAfOtaServerDynamicBlockPeriodInit();
}
void emberAfOtaBootloadClusterServerTickCallback(uint8_t endpoint)
{
emAfOtaPageRequestTick(endpoint);
emAfOtaPageRequestTick(endpoint);
}
// This tick is endpointless and declared differently in plugin.properties
......@@ -98,11 +98,11 @@ void emberAfOtaServerTick(void)
// -------------------------------------------------------
bool emberAfOtaServerIncomingMessageRawCallback(EmberAfClusterCommand* command)
{
EmberStatus status;
if (!commandParse(command)) {
emberAfOtaBootloadClusterPrintln("ClusterError: failed parsing cmd 0x%x",
command->commandId);
emberAfOtaBootloadClusterFlush();
EmberStatus status;
if (!commandParse(command)) {
emberAfOtaBootloadClusterPrintln("ClusterError: failed parsing cmd 0x%x",
command->commandId);
emberAfOtaBootloadClusterFlush();
status = emberAfSendDefaultResponse(command, EMBER_ZCL_STATUS_INVALID_FIELD);
} else {
status = emberAfSendResponse();
......@@ -115,29 +115,29 @@ bool emberAfOtaServerIncomingMessageRawCallback(EmberAfClusterCommand* command)
status);
}
// Always return true to indicate we processed the message.
return true;
// Always return true to indicate we processed the message.
return true;
}
static uint8_t queryNextImageRequestHandler(const EmberAfOtaImageId* currentImageId,
uint16_t* hardwareVersion)
uint16_t* hardwareVersion)
{
uint8_t status;
EmberAfOtaImageId upgradeImageId;
status = emberAfOtaServerQueryCallback(currentImageId,
hardwareVersion,
&upgradeImageId);
prepareClusterResponse(ZCL_QUERY_NEXT_IMAGE_RESPONSE_COMMAND_ID,
status);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
return status;
}
addEmberAfOtaImageIdIntoResponse(&upgradeImageId);
emberAfPutInt32uInResp(emberAfOtaStorageGetTotalImageSizeCallback(&upgradeImageId));
return status;
uint8_t status;
EmberAfOtaImageId upgradeImageId;
status = emberAfOtaServerQueryCallback(currentImageId,
hardwareVersion,
&upgradeImageId);
prepareClusterResponse(ZCL_QUERY_NEXT_IMAGE_RESPONSE_COMMAND_ID,
status);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
return status;
}
addEmberAfOtaImageIdIntoResponse(&upgradeImageId);
emberAfPutInt32uInResp(emberAfOtaStorageGetTotalImageSizeCallback(&upgradeImageId));
return status;
}
bool emberAfOtaServerSendImageNotifyCallback(EmberNodeId dest,
......@@ -185,28 +185,28 @@ bool emberAfOtaServerSendImageNotifyCallback(EmberNodeId dest,
}
static void printBlockRequestInfo(const EmberAfOtaImageId* id,
uint8_t maxDataSize,
uint32_t offset)
uint8_t maxDataSize,
uint32_t offset)
{
// To reduce the redundant data printed by the server, it will only print
// a request for a different image than the last one. To change this
// behavior update the bool below.
const bool printAllRequests = false;
static EmberAfOtaImageId lastImageId = INVALID_OTA_IMAGE_ID;
if (!printAllRequests
&& (0 == MEMCOMPARE(id, &lastImageId, sizeof(EmberAfOtaImageId)))) {
return;
}
MEMMOVE(&lastImageId, id, sizeof(EmberAfOtaImageId));
emberAfOtaBootloadClusterPrintln("NEW ImageBlockReq mfgId:%2x imageTypeId:%2x, file:%4x, maxDataSize:%d, offset:0x%4x",
id->manufacturerId,
id->imageTypeId,
id->firmwareVersion,
maxDataSize,
offset);
// To reduce the redundant data printed by the server, it will only print
// a request for a different image than the last one. To change this
// behavior update the bool below.
const bool printAllRequests = false;
static EmberAfOtaImageId lastImageId = INVALID_OTA_IMAGE_ID;
if (!printAllRequests
&& (0 == MEMCOMPARE(id, &lastImageId, sizeof(EmberAfOtaImageId)))) {
return;
}
MEMMOVE(&lastImageId, id, sizeof(EmberAfOtaImageId));
emberAfOtaBootloadClusterPrintln("NEW ImageBlockReq mfgId:%2x imageTypeId:%2x, file:%4x, maxDataSize:%d, offset:0x%4x",
id->manufacturerId,
id->imageTypeId,
id->firmwareVersion,
maxDataSize,
offset);
//emberAfPluginOtaServerUpdateStartedCallback(id->manufacturerId,
//id->imageTypeId,
// id->firmwareVersion,
......@@ -218,28 +218,28 @@ static void printBlockRequestInfo(const EmberAfOtaImageId* id,
// It returns 0 on error, or the number of bytes sent on success.
uint8_t emAfOtaImageBlockRequestHandler(EmberAfImageBlockRequestCallbackStruct* callbackData)
{
uint8_t data[MAX_POSSIBLE_SERVER_BLOCK_SIZE];
uint32_t actualLength;
uint8_t status = EMBER_ZCL_STATUS_SUCCESS;
uint8_t serverBlockSize = emberAfOtaServerBlockSizeCallback(callbackData->source);
uint8_t data[MAX_POSSIBLE_SERVER_BLOCK_SIZE];
uint32_t actualLength;
uint8_t status = EMBER_ZCL_STATUS_SUCCESS;
uint8_t serverBlockSize = emberAfOtaServerBlockSizeCallback(callbackData->source);
#ifdef EMBER_AF_PLUGIN_SUB_GHZ_SERVER
EmberDutyCycleState dcState;
#endif
if (serverBlockSize > MAX_POSSIBLE_SERVER_BLOCK_SIZE) {
serverBlockSize = MAX_POSSIBLE_SERVER_BLOCK_SIZE;
}
// Delay checks
// We delay the OTA client if the following conditions happen, listed by order
// 1. The duty cycle state has moved to Critical state (for SE1.4 subghz)
// 2. We support a dynamically-treated Minimum Block Period, which means we
// can only support so many clients downloading at once. When we're at that
// max, we tell new clients to delay and come back later
// 3. The OTA client indicates that it supports the Minimum Block Period, and
// its value differs from the value the server holds. We tell it to wait
// for data with the updated block period value
// 4. Test code desires a delay
if (serverBlockSize > MAX_POSSIBLE_SERVER_BLOCK_SIZE) {
serverBlockSize = MAX_POSSIBLE_SERVER_BLOCK_SIZE;
}
// Delay checks
// We delay the OTA client if the following conditions happen, listed by order
// 1. The duty cycle state has moved to Critical state (for SE1.4 subghz)
// 2. We support a dynamically-treated Minimum Block Period, which means we
// can only support so many clients downloading at once. When we're at that
// max, we tell new clients to delay and come back later
// 3. The OTA client indicates that it supports the Minimum Block Period, and
// its value differs from the value the server holds. We tell it to wait
// for data with the updated block period value
// 4. Test code desires a delay
#ifdef EMBER_AF_PLUGIN_SUB_GHZ_SERVER
// SE1.4 says that when in Critical Duty Cycle state... "If active, the OTA server
......@@ -251,67 +251,67 @@ uint8_t emAfOtaImageBlockRequestHandler(EmberAfImageBlockRequestCallbackStruct*
status = EMBER_ZCL_STATUS_WAIT_FOR_DATA;
} else
#endif
status = emberAfOtaServerImageBlockRequestCallback(callbackData);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
prepareClusterResponse(ZCL_IMAGE_BLOCK_RESPONSE_COMMAND_ID,
status);
if (status == EMBER_ZCL_STATUS_WAIT_FOR_DATA) {
// Current time (0 = use relative time, not UTC)
emberAfPutInt32uInResp(0);
emberAfPutInt32uInResp(callbackData->waitTimeSecondsResponse);
status = emberAfOtaServerImageBlockRequestCallback(callbackData);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
prepareClusterResponse(ZCL_IMAGE_BLOCK_RESPONSE_COMMAND_ID,
status);
if (status == EMBER_ZCL_STATUS_WAIT_FOR_DATA) {
// Current time (0 = use relative time, not UTC)
emberAfPutInt32uInResp(0);
emberAfPutInt32uInResp(callbackData->waitTimeSecondsResponse);
#if defined(EMBER_AF_PLUGIN_OTA_SERVER_MIN_BLOCK_REQUEST_SUPPORT)
// The min block request period is in milliseconds as defined in af-types.h
// This attribute, BockRequestDelay, used to be in milliseconds
// (09-5264-23, section 6.7.10)
// It now (15-0324-02) is called MinimumBlockPeriod and is defined to be
// in seconds (section 11.10.10)
emberAfPutInt16uInResp(callbackData->minBlockRequestPeriod);
// The min block request period is in milliseconds as defined in af-types.h
// This attribute, BockRequestDelay, used to be in milliseconds
// (09-5264-23, section 6.7.10)
// It now (15-0324-02) is called MinimumBlockPeriod and is defined to be
// in seconds (section 11.10.10)
emberAfPutInt16uInResp(callbackData->minBlockRequestPeriod);
#endif
}
return 0;
}
MEMSET(data, 0, MAX_POSSIBLE_SERVER_BLOCK_SIZE);
printBlockRequestInfo(callbackData->id,
callbackData->maxDataSize,
callbackData->offset);
callbackData->maxDataSize = (callbackData->maxDataSize < serverBlockSize
? callbackData->maxDataSize
: serverBlockSize);
if (EMBER_AF_OTA_STORAGE_SUCCESS
!= emberAfOtaStorageReadImageDataCallback(callbackData->id,
callbackData->offset,
callbackData->maxDataSize,
data,
&actualLength)
|| actualLength == 0) {
status = EMBER_ZCL_STATUS_NO_IMAGE_AVAILABLE;
// emberAfPluginOtaServerUpdateCompleteCallback(callbackData->id->manufacturerId,
// callbackData->id->imageTypeId,
// callbackData->id->firmwareVersion,
// callbackData->source,
// status);
}
if (status != EMBER_ZCL_STATUS_SUCCESS) {
if (!emAfOtaPageRequestErrorHandler()) {
// If the page request code didn't handle the error (because this code
// wasn't called due to a page request) then we send a normal
// response. We don't generate an error message because in that case
// we were sending an unsolicited image block response.
prepareDefaultResponse(status, ZCL_IMAGE_BLOCK_REQUEST_COMMAND_ID);
}
return 0;
}
prepareClusterResponse(ZCL_IMAGE_BLOCK_RESPONSE_COMMAND_ID, status);
addEmberAfOtaImageIdIntoResponse(callbackData->id);
emberAfPutInt32uInResp(callbackData->offset);
emberAfPutInt8uInResp((uint8_t)actualLength);
emberAfPutBlockInResp(data, actualLength);
emberAfSetCommandEndpoints(otaServerEndpoint, callbackData->clientEndpoint);
}
return 0;
}
MEMSET(data, 0, MAX_POSSIBLE_SERVER_BLOCK_SIZE);
printBlockRequestInfo(callbackData->id,
callbackData->maxDataSize,
callbackData->offset);
callbackData->maxDataSize = (callbackData->maxDataSize < serverBlockSize
? callbackData->maxDataSize
: serverBlockSize);
if (EMBER_AF_OTA_STORAGE_SUCCESS
!= emberAfOtaStorageReadImageDataCallback(callbackData->id,
callbackData->offset,
callbackData->maxDataSize,
data,
&actualLength)
|| actualLength == 0) {
status = EMBER_ZCL_STATUS_NO_IMAGE_AVAILABLE;
// emberAfPluginOtaServerUpdateCompleteCallback(callbackData->id->manufacturerId,
// callbackData->id->imageTypeId,
// callbackData->id->firmwareVersion,
// callbackData->source,
// status);
}
if (status != EMBER_ZCL_STATUS_SUCCESS) {
if (!emAfOtaPageRequestErrorHandler()) {
// If the page request code didn't handle the error (because this code
// wasn't called due to a page request) then we send a normal
// response. We don't generate an error message because in that case
// we were sending an unsolicited image block response.
prepareDefaultResponse(status, ZCL_IMAGE_BLOCK_REQUEST_COMMAND_ID);
}
return 0;
}
prepareClusterResponse(ZCL_IMAGE_BLOCK_RESPONSE_COMMAND_ID, status);
addEmberAfOtaImageIdIntoResponse(callbackData->id);
emberAfPutInt32uInResp(callbackData->offset);
emberAfPutInt8uInResp((uint8_t)actualLength);
emberAfPutBlockInResp(data, actualLength);
emberAfSetCommandEndpoints(otaServerEndpoint, callbackData->clientEndpoint);
//emberAfPluginOtaServerBlockSentCallback((uint8_t)actualLength,
// callbackData->id->manufacturerId,
......@@ -319,85 +319,85 @@ uint8_t emAfOtaImageBlockRequestHandler(EmberAfImageBlockRequestCallbackStruct*
// callbackData->id->firmwareVersion);
// We can't send more than 128 bytes in a packet so we can safely cast this
// to a 1-byte number.
return (uint8_t)actualLength;
return (uint8_t)actualLength;
}
static void constructUpgradeEndResponse(const EmberAfOtaImageId* imageId,
uint32_t upgradeTime)
uint32_t upgradeTime)
{
prepareClusterResponse(ZCL_UPGRADE_END_RESPONSE_COMMAND_ID,
0); // status code (will ignore)
prepareClusterResponse(ZCL_UPGRADE_END_RESPONSE_COMMAND_ID,
0); // status code (will ignore)
appResponseLength--; // The above function wrote an extra byte which we
// don't want because there is no status code for this
// message
appResponseLength--; // The above function wrote an extra byte which we
// don't want because there is no status code for this
// message
addEmberAfOtaImageIdIntoResponse(imageId);
addEmberAfOtaImageIdIntoResponse(imageId);
// We always use relative time. There is no benefit in using
// UTC time since the client has to support both.
emberAfPutInt32uInResp(0); // current time
emberAfPutInt32uInResp(upgradeTime);
// We always use relative time. There is no benefit in using
// UTC time since the client has to support both.
emberAfPutInt32uInResp(0); // current time
emberAfPutInt32uInResp(upgradeTime);
}
static void upgradeEndRequestHandler(EmberNodeId source,
uint8_t status,
const EmberAfOtaImageId* imageId)
uint8_t status,
const EmberAfOtaImageId* imageId)
{
uint32_t upgradeTime;
bool goAhead;
EmberAfStatus defaultRespStatus = EMBER_ZCL_STATUS_SUCCESS;
emberAfOtaBootloadClusterPrintln("RX UpgradeEndReq status:%x",
status);
// This callback is considered only informative when the status
// is a failure.
goAhead = emberAfOtaServerUpgradeEndRequestCallback(source,
status,
&upgradeTime,
imageId);
if (status == EMBER_ZCL_STATUS_SUCCESS) {
if (goAhead) {
constructUpgradeEndResponse(imageId, upgradeTime);
//emberAfPluginOtaServerUpdateCompleteCallback(imageId->manufacturerId,
// imageId->imageTypeId,
// imageId->firmwareVersion,
// source,
// status);
return;
} else {
defaultRespStatus = EMBER_ZCL_STATUS_ABORT;
}
}
prepareDefaultResponse(defaultRespStatus,
ZCL_UPGRADE_END_REQUEST_COMMAND_ID);
uint32_t upgradeTime;
bool goAhead;
EmberAfStatus defaultRespStatus = EMBER_ZCL_STATUS_SUCCESS;
emberAfOtaBootloadClusterPrintln("RX UpgradeEndReq status:%x",
status);
// This callback is considered only informative when the status
// is a failure.
goAhead = emberAfOtaServerUpgradeEndRequestCallback(source,
status,
&upgradeTime,
imageId);
if (status == EMBER_ZCL_STATUS_SUCCESS) {
if (goAhead) {
constructUpgradeEndResponse(imageId, upgradeTime);
//emberAfPluginOtaServerUpdateCompleteCallback(imageId->manufacturerId,
// imageId->imageTypeId,
// imageId->firmwareVersion,
// source,
// status);
return;
} else {
defaultRespStatus = EMBER_ZCL_STATUS_ABORT;
}
}
prepareDefaultResponse(defaultRespStatus,
ZCL_UPGRADE_END_REQUEST_COMMAND_ID);
}
static void querySpecificFileRequestHandler(uint8_t* requestNodeAddress,
const EmberAfOtaImageId* imageId,
uint16_t currentStackVersion)
const EmberAfOtaImageId* imageId,
uint16_t currentStackVersion)
{
// Not supported yet.
prepareDefaultResponse(EMBER_ZCL_STATUS_UNSUP_CLUSTER_COMMAND,
ZCL_QUERY_SPECIFIC_FILE_REQUEST_COMMAND_ID);
// Not supported yet.
prepareDefaultResponse(EMBER_ZCL_STATUS_UNSUP_CLUSTER_COMMAND,
ZCL_QUERY_SPECIFIC_FILE_REQUEST_COMMAND_ID);
}
void emAfOtaServerPrepareResponse(bool useDefaultResponse,
uint8_t commandId,
uint8_t status,
uint8_t defaultResponsePayloadCommandId)
uint8_t commandId,
uint8_t status,
uint8_t defaultResponsePayloadCommandId)
{
emberAfResponseApsFrame.sourceEndpoint = otaServerEndpoint;
appResponseLength = 0;
emberAfResponseApsFrame.clusterId = ZCL_OTA_BOOTLOAD_CLUSTER_ID;
emberAfResponseApsFrame.sourceEndpoint = otaServerEndpoint;
appResponseLength = 0;
emberAfResponseApsFrame.clusterId = ZCL_OTA_BOOTLOAD_CLUSTER_ID;
emberAfResponseApsFrame.options =
((emAfOtaServerHandlingPageRequest()
&& commandId == ZCL_IMAGE_BLOCK_RESPONSE_COMMAND_ID)
? EMBER_APS_OPTION_NONE
: EMBER_APS_OPTION_RETRY);
emberAfResponseApsFrame.options =
((emAfOtaServerHandlingPageRequest()
&& commandId == ZCL_IMAGE_BLOCK_RESPONSE_COMMAND_ID)
? EMBER_APS_OPTION_NONE
: EMBER_APS_OPTION_RETRY);
// Assume emberAfResponseApsFrame.destinationEndpoint has already
// been set based on the framework. In most cases it is as simple
......@@ -423,189 +423,236 @@ void emAfOtaServerPrepareResponse(bool useDefaultResponse,
static void addEmberAfOtaImageIdIntoResponse(const EmberAfOtaImageId* id)
{
emberAfPutInt16uInResp(id->manufacturerId);
emberAfPutInt16uInResp(id->imageTypeId);
emberAfPutInt32uInResp(id->firmwareVersion);
emberAfPutInt16uInResp(id->manufacturerId);
emberAfPutInt16uInResp(id->imageTypeId);
emberAfPutInt32uInResp(id->firmwareVersion);
}
static bool commandParse(EmberAfClusterCommand* command)
{
uint8_t commandId = command->commandId;
uint8_t* buffer = command->buffer;
uint8_t length = command->bufLen;
uint8_t index = EMBER_AF_ZCL_OVERHEAD;
EmberAfOtaImageId imageId = INVALID_OTA_IMAGE_ID;
if (commandId > LAST_MESSAGE_ID
|| (length
< (EMBER_AF_ZCL_OVERHEAD + emAfOtaMinMessageLengths[commandId]))) {
return false;
}
switch (commandId) {
case ZCL_QUERY_NEXT_IMAGE_REQUEST_COMMAND_ID:
case ZCL_IMAGE_BLOCK_REQUEST_COMMAND_ID:
case ZCL_IMAGE_PAGE_REQUEST_COMMAND_ID: {
uint8_t fieldControl;
uint32_t offset;
uint8_t maxDataSize;
uint16_t hardwareVersion;
bool hardwareVersionPresent = false;
fieldControl = emberAfGetInt8u(buffer, index, length);
index++;
index += emAfOtaParseImageIdFromMessage(&imageId,
&(buffer[index]),
length);
if (commandId == ZCL_QUERY_NEXT_IMAGE_REQUEST_COMMAND_ID) {
if (fieldControl & QUERY_NEXT_IMAGE_HW_VER_PRESENT_MASK) {
hardwareVersionPresent = true;
hardwareVersion = emberAfGetInt16u(buffer, index, length);
index += 2;
}
queryNextImageRequestHandler(&imageId,
(hardwareVersionPresent
? &hardwareVersion
: NULL));
return true;
} // else // ZCL_IMAGE_BLOCK_REQUEST_COMMAND_ID
// or ZCL_IMAGE_PAGE_REQUEST_COMMAND_ID
offset = emberAfGetInt32u(buffer, index, length);
index += 4;
maxDataSize = emberAfGetInt8u(buffer, index, length);
index++;
if (commandId == ZCL_IMAGE_BLOCK_REQUEST_COMMAND_ID) {
EmberAfImageBlockRequestCallbackStruct callbackStruct;
MEMSET(&callbackStruct, 0, sizeof(EmberAfImageBlockRequestCallbackStruct));
if (fieldControl & OTA_FIELD_CONTROL_NODE_EUI64_PRESENT_BIT) {
MEMCOPY(callbackStruct.sourceEui, &(buffer[index]), EUI64_SIZE);
index += EUI64_SIZE;
}
if (fieldControl & OTA_FIELD_CONTROL_MIN_BLOCK_REQUEST_PRESENT_BIT) {
callbackStruct.minBlockRequestPeriod = emberAfGetInt16u(buffer, index, length);
callbackStruct.bitmask |= EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_CLIENT;
index += 2;
}
callbackStruct.clientEndpoint = command->apsFrame->sourceEndpoint;
callbackStruct.source = command->source;
callbackStruct.id = &imageId;
callbackStruct.offset = offset;
callbackStruct.maxDataSize = maxDataSize;
callbackStruct.bitmask |= MIN_BLOCK_REQUEST_SERVER_SUPPORT;
emAfOtaImageBlockRequestHandler(&callbackStruct);
} else { // ZCL_IMAGE_PAGE_REQUEST_COMMAND_ID
uint16_t pageSize;
uint16_t responseSpacing;
uint8_t status;
pageSize = emberAfGetInt16u(buffer, index, length);
index += 2;
responseSpacing = emberAfGetInt16u(buffer, index, length);
index += 2;
status = emAfOtaPageRequestHandler(command->apsFrame->sourceEndpoint,
otaServerEndpoint,
&imageId,
offset,
maxDataSize,
pageSize,
responseSpacing);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
prepareDefaultResponse(status,
ZCL_IMAGE_PAGE_REQUEST_COMMAND_ID);
}
}
return true;
}
case ZCL_UPGRADE_END_REQUEST_COMMAND_ID: {
uint8_t status = emberAfGetInt8u(buffer, index, length);
index++;
if (status == EMBER_ZCL_STATUS_SUCCESS) {
index += emAfOtaParseImageIdFromMessage(&imageId,
&(buffer[index]),
length);
}
upgradeEndRequestHandler(command->source, status, &imageId);
return true;
}
case ZCL_QUERY_SPECIFIC_FILE_REQUEST_COMMAND_ID: {
uint8_t* requestNodeAddress = &(buffer[index]);
uint16_t currentStackVersion;
index += 8; // add 8 to jump over the requestNodeAddress
index += emAfOtaParseImageIdFromMessage(&imageId,
&(buffer[index]),
length);
currentStackVersion = emberAfGetInt16u(buffer, index, length);
index += 2;
querySpecificFileRequestHandler(requestNodeAddress,
&imageId,
currentStackVersion);
return true;
}
}
return false;
uint8_t commandId = command->commandId;
uint8_t* buffer = command->buffer;
uint8_t length = command->bufLen;
uint8_t index = EMBER_AF_ZCL_OVERHEAD;
EmberAfOtaImageId imageId = INVALID_OTA_IMAGE_ID;
if (commandId > LAST_MESSAGE_ID
|| (length
< (EMBER_AF_ZCL_OVERHEAD + emAfOtaMinMessageLengths[commandId]))) {
return false;
}
switch (commandId) {
case ZCL_QUERY_NEXT_IMAGE_REQUEST_COMMAND_ID:
case ZCL_IMAGE_BLOCK_REQUEST_COMMAND_ID:
case ZCL_IMAGE_PAGE_REQUEST_COMMAND_ID: {
uint8_t fieldControl;
uint32_t offset;
uint8_t maxDataSize;
uint16_t hardwareVersion;
bool hardwareVersionPresent = false;
fieldControl = emberAfGetInt8u(buffer, index, length);
index++;
index += emAfOtaParseImageIdFromMessage(&imageId,
&(buffer[index]),
length);
if (commandId == ZCL_QUERY_NEXT_IMAGE_REQUEST_COMMAND_ID) {
if (fieldControl & QUERY_NEXT_IMAGE_HW_VER_PRESENT_MASK) {
hardwareVersionPresent = true;
hardwareVersion = emberAfGetInt16u(buffer, index, length);
index += 2;
}
queryNextImageRequestHandler(&imageId,
(hardwareVersionPresent
? &hardwareVersion
: NULL));
return true;
} // else // ZCL_IMAGE_BLOCK_REQUEST_COMMAND_ID
// or ZCL_IMAGE_PAGE_REQUEST_COMMAND_ID
offset = emberAfGetInt32u(buffer, index, length);
index += 4;
maxDataSize = emberAfGetInt8u(buffer, index, length);
index++;
if (commandId == ZCL_IMAGE_BLOCK_REQUEST_COMMAND_ID) {
EmberAfImageBlockRequestCallbackStruct callbackStruct;
MEMSET(&callbackStruct, 0, sizeof(EmberAfImageBlockRequestCallbackStruct));
if (fieldControl & OTA_FIELD_CONTROL_NODE_EUI64_PRESENT_BIT) {
MEMCOPY(callbackStruct.sourceEui, &(buffer[index]), EUI64_SIZE);
index += EUI64_SIZE;
}
if (fieldControl & OTA_FIELD_CONTROL_MIN_BLOCK_REQUEST_PRESENT_BIT) {
callbackStruct.minBlockRequestPeriod = emberAfGetInt16u(buffer, index, length);
callbackStruct.bitmask |= EMBER_AF_IMAGE_BLOCK_REQUEST_MIN_BLOCK_REQUEST_SUPPORTED_BY_CLIENT;
index += 2;
}
callbackStruct.clientEndpoint = command->apsFrame->sourceEndpoint;
callbackStruct.source = command->source;
callbackStruct.id = &imageId;
callbackStruct.offset = offset;
callbackStruct.maxDataSize = maxDataSize;
callbackStruct.bitmask |= MIN_BLOCK_REQUEST_SERVER_SUPPORT;
emAfOtaImageBlockRequestHandler(&callbackStruct);
} else { // ZCL_IMAGE_PAGE_REQUEST_COMMAND_ID
uint16_t pageSize;
uint16_t responseSpacing;
uint8_t status;
pageSize = emberAfGetInt16u(buffer, index, length);
index += 2;
responseSpacing = emberAfGetInt16u(buffer, index, length);
index += 2;
status = emAfOtaPageRequestHandler(command->apsFrame->sourceEndpoint,
otaServerEndpoint,
&imageId,
offset,
maxDataSize,
pageSize,
responseSpacing);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
prepareDefaultResponse(status,
ZCL_IMAGE_PAGE_REQUEST_COMMAND_ID);
}
}
return true;
}
case ZCL_UPGRADE_END_REQUEST_COMMAND_ID: {
uint8_t status = emberAfGetInt8u(buffer, index, length);
index++;
if (status == EMBER_ZCL_STATUS_SUCCESS) {
index += emAfOtaParseImageIdFromMessage(&imageId,
&(buffer[index]),
length);
}
upgradeEndRequestHandler(command->source, status, &imageId);
return true;
}
case ZCL_QUERY_SPECIFIC_FILE_REQUEST_COMMAND_ID: {
uint8_t* requestNodeAddress = &(buffer[index]);
uint16_t currentStackVersion;
index += 8; // add 8 to jump over the requestNodeAddress
index += emAfOtaParseImageIdFromMessage(&imageId,
&(buffer[index]),
length);
currentStackVersion = emberAfGetInt16u(buffer, index, length);
index += 2;
querySpecificFileRequestHandler(requestNodeAddress,
&imageId,
currentStackVersion);
return true;
}
}
return false;
}
void emberAfOtaServerSendUpgradeCommandCallback(EmberNodeId dest,
uint8_t endpoint,
const EmberAfOtaImageId* id)
uint8_t endpoint,
const EmberAfOtaImageId* id)
{
EmberStatus status;
emberAfResponseDestination = dest;
emberAfResponseApsFrame.destinationEndpoint = endpoint;
constructUpgradeEndResponse(id,
0); // upgrade time (0 = now)
status = emberAfSendResponse();
if (EMBER_SUCCESS != status) {
emberAfOtaBootloadClusterPrintln("OTA: failed sending upgrade response: "
"error 0x%x", status);
}
EmberStatus status;
emberAfResponseDestination = dest;
emberAfResponseApsFrame.destinationEndpoint = endpoint;
constructUpgradeEndResponse(id,
0); // upgrade time (0 = now)
status = emberAfSendResponse();
if (EMBER_SUCCESS != status) {
emberAfOtaBootloadClusterPrintln("OTA: failed sending upgrade response: "
"error 0x%x", status);
}
}
void kk_ota_test123123123()
{
EmberAfOtaImageId id;
id.manufacturerId = 0x1268;
id.imageTypeId = 0x2904;
id.firmwareVersion = 15;
emberAfOtaServerSendImageNotifyCallback(0xffff,
1,
0x03,
0,
&id);
}
void kk_ota_test111()
{
/*zclBufferSetup(ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT|ZCL_DISABLE_DEFAULT_RESPONSE_MASK, \
(ZCL_OTA_BOOTLOAD_CLUSTER_ID), \
(ZCL_IMAGE_NOTIFY_COMMAND_ID));
zclBufferAddByte(0x03);
zclBufferAddByte(0x00);
zclBufferAddWord(0x1234);
zclBufferAddWord(0x5678);
zclBufferAddInt32(0x11223344);
rpcSendCommand(0xffff,1,1,false);
emberAfCorePrintln("[kk_ota_test111] \r\n");*/
emberAfCorePrintln("[kk_ota_test111] \r\n");
static int cnt;
EmberAfOtaImageId id = emberAfOtaStorageIteratorFirstCallback();
cnt = 0;
do {
emberAfCorePrintln("**********%d********\r\n",++cnt);
emberAfCorePrintln("manufacturerId=0x%4X \r\n",id.manufacturerId);
emberAfCorePrintln("imageTypeId=0x%4X \r\n",id.imageTypeId);
emberAfCorePrintln("firmwareVersion=0x%8X \r\n",id.firmwareVersion);
id = emberAfOtaStorageIteratorNextCallback();
} while (emberAfIsOtaImageIdValid(&id));
}
static int s_ota_server_init = 0;
EmberAfOtaImageId kk_get_imageId(char* filename){
char* oatfile = NULL;
char* oatfile = NULL;
EmberAfOtaImageId id = emberAfOtaStorageIteratorFirstCallback();
do {
oatfile = emAfOtaStorageGetFilepath(&id);
if (oatfile != NULL && strcmp(oatfile, filename) == 0){
return id;
}
EmberAfOtaImageId id = emberAfOtaStorageIteratorFirstCallback();
do {
oatfile = emAfOtaStorageGetFilepath(&id);
if (oatfile != NULL && strcmp(oatfile, filename) == 0){
return id;
}
id = emberAfOtaStorageIteratorNextCallback();
} while (emberAfIsOtaImageIdValid(&id));
id = emberAfOtaStorageIteratorNextCallback();
} while (emberAfIsOtaImageIdValid(&id));
return emberAfInvalidImageId;
return emberAfInvalidImageId;
}
void kk_ota_notify(char* filepath){
if (s_ota_server_init == 0){
s_ota_server_init = 1;
emberAfOtaBootloadClusterServerInitCallback(0x00);
}
//add file
emAfOtaReloadStorageDevice();
//TODO:开启通知, 通过mac地址获取dest,endpoint
EmberNodeId dest;
uint8_t endpoint;
EmberAfOtaImageId id;
id = kk_get_imageId(filepath);
emberAfOtaServerSendUpgradeCommandCallback(dest, endpoint, &id);
if (s_ota_server_init == 0){
s_ota_server_init = 1;
emberAfOtaBootloadClusterServerInitCallback(0x00);
}
//add file
emAfOtaReloadStorageDevice();
//TODO:开启通知, 通过mac地址获取dest,endpoint
EmberNodeId dest;
uint8_t endpoint;
EmberAfOtaImageId id;
id = kk_get_imageId(filepath);
emberAfOtaServerSendUpgradeCommandCallback(dest, endpoint, &id);
}
......@@ -142,10 +142,10 @@ void emAfOtaImageDelete(void)
void emAfOtaReloadStorageDevice(void)
{
#if defined (EMBER_AF_PLUGIN_OTA_STORAGE_POSIX_FILESYSTEM)
emAfOtaStorageClose();
emAfOtaStorageClose();
#endif
emberAfOtaStorageInitCallback();
emberAfOtaStorageInitCallback();
}
#define PRINT_BLOCK_LENGTH 64
......
......@@ -39,18 +39,18 @@
EmberAfOtaImageId emAfOtaStorageGetImageIdFromHeader(const EmberAfOtaHeader* header)
{
EmberAfOtaImageId id = INVALID_OTA_IMAGE_ID;
id.manufacturerId = header->manufacturerId;
id.imageTypeId = header->imageTypeId;
id.firmwareVersion = header->firmwareVersion;
if (headerHasUpgradeFileDest(header)) {
MEMCOPY(id.deviceSpecificFileEui64,
&header->upgradeFileDestination,
EUI64_SIZE);
}
return id;
EmberAfOtaImageId id = INVALID_OTA_IMAGE_ID;
id.manufacturerId = header->manufacturerId;
id.imageTypeId = header->imageTypeId;
id.firmwareVersion = header->firmwareVersion;
if (headerHasUpgradeFileDest(header)) {
MEMCOPY(id.deviceSpecificFileEui64,
&header->upgradeFileDestination,
EUI64_SIZE);
}
return id;
}
uint16_t emGetUpgradeFileDestinationLength(uint16_t headerVersion)
......@@ -249,7 +249,7 @@ EmberAfOtaStorageStatus emAfOtaStorageReadAllTagInfo(const EmberAfOtaImageId* id
bool emberAfIsOtaImageIdValid(const EmberAfOtaImageId* idToCompare)
{
return (0 != MEMPGMCOMPARE(idToCompare,
&emberAfInvalidImageId,
sizeof(EmberAfOtaImageId)));
return (0 != MEMPGMCOMPARE(idToCompare,
&emberAfInvalidImageId,
sizeof(EmberAfOtaImageId)));
}
......@@ -40,18 +40,18 @@
#define TAG_OVERHEAD (2 + 4) // 2 bytes for the tag ID, 4 bytes for the length
#define isValidHeaderVersion(headerVersion) \
((headerVersion == OTA_HEADER_VERSION_ZIGBEE) || (headerVersion == OTA_HEADER_VERSION_THREAD))
((headerVersion == OTA_HEADER_VERSION_ZIGBEE) || (headerVersion == OTA_HEADER_VERSION_THREAD))
#define SECURITY_CREDENTIAL_VERSION_FIELD_PRESENT_MASK 0x0001
#define DEVICE_SPECIFIC_FILE_PRESENT_MASK 0x0002
#define HARDWARE_VERSIONS_PRESENT_MASK 0x0004
#define headerHasSecurityCredentials(header) \
((header)->fieldControl & SECURITY_CREDENTIAL_VERSION_FIELD_PRESENT_MASK)
((header)->fieldControl & SECURITY_CREDENTIAL_VERSION_FIELD_PRESENT_MASK)
#define headerHasUpgradeFileDest(header) \
((header)->fieldControl & DEVICE_SPECIFIC_FILE_PRESENT_MASK)
((header)->fieldControl & DEVICE_SPECIFIC_FILE_PRESENT_MASK)
#define headerHasHardwareVersions(header) \
((header)->fieldControl & HARDWARE_VERSIONS_PRESENT_MASK)
((header)->fieldControl & HARDWARE_VERSIONS_PRESENT_MASK)
// This size does NOT include the tag overhead.
#define SIGNATURE_TAG_DATA_SIZE (EUI64_SIZE + EMBER_SIGNATURE_SIZE)
......@@ -63,11 +63,11 @@
#define INVALID_EUI64 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
#define INVALID_OTA_IMAGE_ID \
{ INVALID_MANUFACTURER_ID, \
INVALID_DEVICE_ID, \
INVALID_FIRMWARE_VERSION, \
INVALID_EUI64, \
}
{ INVALID_MANUFACTURER_ID, \
INVALID_DEVICE_ID, \
INVALID_FIRMWARE_VERSION, \
INVALID_EUI64, \
}
#define INVALID_SLOT (uint32_t)-1
......
......@@ -64,12 +64,12 @@ static char* tempStorageFilepath = NULL;
static const char* tempStorageFile = "temporary-storage.ota";
typedef struct {
EmberAfOtaHeader* header;
char* filepath;
const char* filenameStart; // ptr to data in 'filepath'
EmberAfOtaHeader* header;
char* filepath;
const char* filenameStart; // ptr to data in 'filepath'
struct OtaImage* next;
struct OtaImage* prev;
off_t fileSize;
off_t fileSize;
} OtaImage;
static OtaImage* imageListFirst = NULL;
......@@ -79,26 +79,26 @@ static uint8_t imageCount = 0;
#define OTA_MAX_FILENAME_LENGTH 1000
static const uint8_t otaFileMagicNumberBytes[] = {
(uint8_t)(OTA_FILE_MAGIC_NUMBER),
(uint8_t)(OTA_FILE_MAGIC_NUMBER >> 8),
(uint8_t)(OTA_FILE_MAGIC_NUMBER >> 16),
(uint8_t)(OTA_FILE_MAGIC_NUMBER >> 24),
(uint8_t)(OTA_FILE_MAGIC_NUMBER),
(uint8_t)(OTA_FILE_MAGIC_NUMBER >> 8),
(uint8_t)(OTA_FILE_MAGIC_NUMBER >> 16),
(uint8_t)(OTA_FILE_MAGIC_NUMBER >> 24),
};
#define ALWAYS_PRESENT_MASK 0xFFFF
enum FieldType{
INVALID_FIELD = 0,
INTEGER_FIELD = 1,
BYTE_ARRAY_FIELD = 2,
STRING_FIELD = 3,
INVALID_FIELD = 0,
INTEGER_FIELD = 1,
BYTE_ARRAY_FIELD = 2,
STRING_FIELD = 3,
};
typedef struct {
const char* name;
const enum FieldType type;
const uint16_t length;
const uint16_t maskForOptionalField;
const char* name;
const enum FieldType type;
const uint16_t length;
const uint16_t maskForOptionalField;
} EmberAfOtaHeaderFieldDefinition;
// This global is used to define what is in the OTA header and help map that
......@@ -106,54 +106,54 @@ typedef struct {
// emGetOtaHeaderFieldDefinition() to get the field definitions. Do not use this
// array directly.
const static EmberAfOtaHeaderFieldDefinition otaHeaderFieldDefinitions[] = {
{ "Magic Number", INTEGER_FIELD, 4, ALWAYS_PRESENT_MASK },
{ "Header Version", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
// The fields returned above SHALL not depend on otaHeaderVersion, because
// when reading in the header, we must be able to determine the location of
// the header version
{ "Header Length", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Field Control", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Manufacturer ID", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Image Type", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Firmware Version", INTEGER_FIELD, 4, ALWAYS_PRESENT_MASK },
{ "Zigbee Stack Version", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Header String", STRING_FIELD, 32, ALWAYS_PRESENT_MASK },
{ "Total Image Size", INTEGER_FIELD, 4, ALWAYS_PRESENT_MASK },
{ "Security Credentials", INTEGER_FIELD, 1, SECURITY_CREDENTIAL_VERSION_FIELD_PRESENT_MASK },
{ "Upgrade File Destination", BYTE_ARRAY_FIELD, EUI64_SIZE, DEVICE_SPECIFIC_FILE_PRESENT_MASK },
{ "Minimum Hardware Version", INTEGER_FIELD, 2, HARDWARE_VERSIONS_PRESENT_MASK },
{ "Maximum Hardware Version", INTEGER_FIELD, 2, HARDWARE_VERSIONS_PRESENT_MASK },
// Tag Data information omitted
{ NULL, INVALID_FIELD, 0, 0 },
{ "Magic Number", INTEGER_FIELD, 4, ALWAYS_PRESENT_MASK },
{ "Header Version", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
// The fields returned above SHALL not depend on otaHeaderVersion, because
// when reading in the header, we must be able to determine the location of
// the header version
{ "Header Length", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Field Control", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Manufacturer ID", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Image Type", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Firmware Version", INTEGER_FIELD, 4, ALWAYS_PRESENT_MASK },
{ "Zigbee Stack Version", INTEGER_FIELD, 2, ALWAYS_PRESENT_MASK },
{ "Header String", STRING_FIELD, 32, ALWAYS_PRESENT_MASK },
{ "Total Image Size", INTEGER_FIELD, 4, ALWAYS_PRESENT_MASK },
{ "Security Credentials", INTEGER_FIELD, 1, SECURITY_CREDENTIAL_VERSION_FIELD_PRESENT_MASK },
{ "Upgrade File Destination", BYTE_ARRAY_FIELD, EUI64_SIZE, DEVICE_SPECIFIC_FILE_PRESENT_MASK },
{ "Minimum Hardware Version", INTEGER_FIELD, 2, HARDWARE_VERSIONS_PRESENT_MASK },
{ "Maximum Hardware Version", INTEGER_FIELD, 2, HARDWARE_VERSIONS_PRESENT_MASK },
// Tag Data information omitted
{ NULL, INVALID_FIELD, 0, 0 },
};
typedef enum {
// Don't define all index numbers so the compiler
// automatically numbers them. We just define the first.
MAGIC_NUMBER_INDEX = 0,
HEADER_VERSION_INDEX,
HEADER_LENGTH_INDEX,
FIELD_CONTROL_INDEX,
MANUFACTURER_ID_INDEX,
IMAGE_TYPE_INDEX,
FIRMWARE_VERSION_INDEX,
ZIGBEE_STACK_VERSION_INDEX,
HEADER_STRING_INDEX,
TOTAL_IMAGE_SIZE_INDEX,
SECURITY_CREDENTIALS_INDEX,
UPGRADE_FILE_DESTINATION_INDEX,
MINIMUM_HARDWARE_VERSION_INDEX,
MAXIMUM_HARDWARE_VERSION_INDEX,
// This defines the maximum number of entries and refers to the invalid entry
INVALID_FIELD_INDEX,
// Don't define all index numbers so the compiler
// automatically numbers them. We just define the first.
MAGIC_NUMBER_INDEX = 0,
HEADER_VERSION_INDEX,
HEADER_LENGTH_INDEX,
FIELD_CONTROL_INDEX,
MANUFACTURER_ID_INDEX,
IMAGE_TYPE_INDEX,
FIRMWARE_VERSION_INDEX,
ZIGBEE_STACK_VERSION_INDEX,
HEADER_STRING_INDEX,
TOTAL_IMAGE_SIZE_INDEX,
SECURITY_CREDENTIALS_INDEX,
UPGRADE_FILE_DESTINATION_INDEX,
MINIMUM_HARDWARE_VERSION_INDEX,
MAXIMUM_HARDWARE_VERSION_INDEX,
// This defines the maximum number of entries and refers to the invalid entry
INVALID_FIELD_INDEX,
} EmberAfOtaBootloadFileHeaderFieldIndex_t;
#define FIELD_INDEX_MAX INVALID_FIELD_INDEX
typedef struct {
void* location;
bool found;
void* location;
bool found;
} EmberAfOtaHeaderFieldLocation;
static EmberAfOtaHeaderFieldLocation otaHeaderFieldLocations[FIELD_INDEX_MAX];
......@@ -170,9 +170,9 @@ static OtaImage* iterator = NULL;
#endif
static EmAfOtaStorageLinuxConfig config = {
false, // memoryDebug
false, // fileDebug
false, // fieldDebug
true, // memoryDebug
true, // fileDebug
true, // fieldDebug
true, // ignoreFilesWithUnderscorePrefix
true, // printFileDiscoveryOrRemoval
NULL, // fileAddedHandler
......@@ -235,493 +235,493 @@ static bool createStorageDirectory = true;
// loads a single file into its header cache.
EmberAfOtaStorageStatus emAfOtaSetStorageDevice(const void* device)
{
if (initDone) {
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (device == NULL) {
// We interpet this to mean, don't use a storage directory.
// This option is useful when creating files via the PC
// tool.
createStorageDirectory = false;
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
const char* directoryOrFile = (const char*)device;
int length = strnlen(directoryOrFile, MAX_FILEPATH_LENGTH + 1);
if (MAX_FILEPATH_LENGTH < length) {
error("Storage directory path too long (max = %d)!\n",
MAX_FILEPATH_LENGTH);
return EMBER_AF_OTA_STORAGE_ERROR;
}
// Add 1 for '\0' and 1 for '/'. This may or may not be necessary
// because the path already has it or it is only a file.
storageDevice = myMalloc(length + 2,
"emAfSetStorageDevice(): storageDevice");
if (storageDevice == NULL) {
error("Could not allocate %d bytes!\n", length);
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (directoryOrFile[length - 1] == '/') {
// We don't want to copy the '/' yet, since stat() will
// complain if we pass it in.
length--;
}
MEMSET(storageDevice, 0, length + 2);
strncpy(storageDevice, directoryOrFile, length);
struct stat statInfo;
int returnValue = stat(storageDevice, &statInfo);
debug(config.fileDebug,
"Checking for existence of '%s'\n",
storageDevice);
if (returnValue != 0) {
error("Could not read storage device '%s'. %s\n",
directoryOrFile,
strerror(errno));
myFree(storageDevice);
storageDevice = NULL;
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (S_ISDIR(statInfo.st_mode)) {
storageDeviceIsDirectory = true;
storageDevice[length] = '/';
}
debug(config.fileDebug, "Storage device set to '%s'.\n", storageDevice);
return EMBER_AF_OTA_STORAGE_SUCCESS;
if (initDone) {
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (device == NULL) {
// We interpet this to mean, don't use a storage directory.
// This option is useful when creating files via the PC
// tool.
createStorageDirectory = false;
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
const char* directoryOrFile = (const char*)device;
int length = strnlen(directoryOrFile, MAX_FILEPATH_LENGTH + 1);
if (MAX_FILEPATH_LENGTH < length) {
error("Storage directory path too long (max = %d)!\n",
MAX_FILEPATH_LENGTH);
return EMBER_AF_OTA_STORAGE_ERROR;
}
// Add 1 for '\0' and 1 for '/'. This may or may not be necessary
// because the path already has it or it is only a file.
storageDevice = myMalloc(length + 2,
"emAfSetStorageDevice(): storageDevice");
if (storageDevice == NULL) {
error("Could not allocate %d bytes!\n", length);
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (directoryOrFile[length - 1] == '/') {
// We don't want to copy the '/' yet, since stat() will
// complain if we pass it in.
length--;
}
MEMSET(storageDevice, 0, length + 2);
strncpy(storageDevice, directoryOrFile, length);
struct stat statInfo;
int returnValue = stat(storageDevice, &statInfo);
debug(config.fileDebug,
"Checking for existence of '%s'\n",
storageDevice);
if (returnValue != 0) {
error("Could not read storage device '%s'. %s\n",
directoryOrFile,
strerror(errno));
myFree(storageDevice);
storageDevice = NULL;
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (S_ISDIR(statInfo.st_mode)) {
storageDeviceIsDirectory = true;
storageDevice[length] = '/';
}
debug(config.fileDebug, "Storage device set to '%s'.\n", storageDevice);
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
void emAfOtaStorageGetConfig(EmAfOtaStorageLinuxConfig* currentConfig)
{
memcpy(currentConfig, &config, sizeof(EmAfOtaStorageLinuxConfig));
memcpy(currentConfig, &config, sizeof(EmAfOtaStorageLinuxConfig));
}
void emAfOtaStorageSetConfig(const EmAfOtaStorageLinuxConfig* newConfig)
{
memcpy(&config, newConfig, sizeof(EmAfOtaStorageLinuxConfig));
memcpy(&config, newConfig, sizeof(EmAfOtaStorageLinuxConfig));
}
EmberAfOtaStorageStatus emberAfOtaStorageInitCallback(void)
{
if (initDone) {
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
if (config.fileDebug) {
char cwd[MAX_FILEPATH_LENGTH];
debug(config.fileDebug, "Current Working Directory: %s\n",
(NULL == getcwd(cwd, MAX_FILEPATH_LENGTH)
? "UNKNOWN!"
: cwd));
}
EmberAfOtaStorageStatus status;
if (storageDevice == NULL) {
if (createStorageDirectory) {
status = createDefaultStorageDirectory();
if (status != EMBER_AF_OTA_STORAGE_SUCCESS) {
return status;
}
} else {
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
}
if (storageDeviceIsDirectory) {
status = initImageDirectory();
} else {
OtaImage* newImage = addImageFileToList(storageDevice, true);
if (config.fileAddedHandler != NULL
&& newImage != NULL) {
(config.fileAddedHandler)(newImage->header);
}
status = (NULL == newImage
? EMBER_AF_OTA_STORAGE_ERROR
: EMBER_AF_OTA_STORAGE_SUCCESS);
}
iterator = NULL; // Must be initialized via
// otaStorageIteratorReset()
if (status == EMBER_AF_OTA_STORAGE_SUCCESS) {
initDone = true;
}
return status;
if (initDone) {
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
if (config.fileDebug) {
char cwd[MAX_FILEPATH_LENGTH];
debug(config.fileDebug, "Current Working Directory: %s\n",
(NULL == getcwd(cwd, MAX_FILEPATH_LENGTH)
? "UNKNOWN!"
: cwd));
}
EmberAfOtaStorageStatus status;
if (storageDevice == NULL) {
if (createStorageDirectory) {
status = createDefaultStorageDirectory();
if (status != EMBER_AF_OTA_STORAGE_SUCCESS) {
return status;
}
} else {
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
}
if (storageDeviceIsDirectory) {
status = initImageDirectory();
} else {
OtaImage* newImage = addImageFileToList(storageDevice, true);
if (config.fileAddedHandler != NULL
&& newImage != NULL) {
(config.fileAddedHandler)(newImage->header);
}
status = (NULL == newImage
? EMBER_AF_OTA_STORAGE_ERROR
: EMBER_AF_OTA_STORAGE_SUCCESS);
}
iterator = NULL; // Must be initialized via
// otaStorageIteratorReset()
if (status == EMBER_AF_OTA_STORAGE_SUCCESS) {
initDone = true;
}
return status;
}
void emAfOtaStorageClose(void)
{
OtaImage* ptr = imageListLast;
while (ptr != NULL) {
OtaImage* current = ptr;
ptr = (OtaImage*)ptr->prev;
freeOtaImage(current);
}
imageListLast = NULL;
imageListFirst = NULL;
if (storageDevice != NULL) {
myFree(storageDevice);
storageDevice = NULL;
}
if (tempStorageFilepath != NULL) {
myFree(tempStorageFilepath);
tempStorageFilepath = NULL;
}
initDone = false;
imageCount = 0;
OtaImage* ptr = imageListLast;
while (ptr != NULL) {
OtaImage* current = ptr;
ptr = (OtaImage*)ptr->prev;
freeOtaImage(current);
}
imageListLast = NULL;
imageListFirst = NULL;
if (storageDevice != NULL) {
myFree(storageDevice);
storageDevice = NULL;
}
if (tempStorageFilepath != NULL) {
myFree(tempStorageFilepath);
tempStorageFilepath = NULL;
}
initDone = false;
imageCount = 0;
}
uint8_t emberAfOtaStorageGetCountCallback(void)
{
return imageCount;
return imageCount;
}
EmberAfOtaImageId emberAfOtaStorageSearchCallback(uint16_t manufacturerId,
uint16_t manufacturerDeviceId,
const uint16_t* hardwareVersion)
uint16_t manufacturerDeviceId,
const uint16_t* hardwareVersion)
{
EmberAfOtaImageId id = {
manufacturerId,
manufacturerDeviceId,
INVALID_FIRMWARE_VERSION,
INVALID_EUI64,
};
OtaImage* image = imageSearchInternal(&id);
if (image == NULL) {
return emberAfInvalidImageId;
}
// We assume that if there is no hardware version information in the header
// then it can be used on ANY hardware version. The behavior of the server
// in this case is not spelled out by the specification.
if (hardwareVersion != NULL
&& headerHasHardwareVersions(image->header)
&& !(*hardwareVersion >= image->header->minimumHardwareVersion
&& *hardwareVersion <= image->header->maximumHardwareVersion)) {
return emberAfInvalidImageId;
}
id.firmwareVersion = image->header->firmwareVersion;
return id;
EmberAfOtaImageId id = {
manufacturerId,
manufacturerDeviceId,
INVALID_FIRMWARE_VERSION,
INVALID_EUI64,
};
OtaImage* image = imageSearchInternal(&id);
if (image == NULL) {
return emberAfInvalidImageId;
}
// We assume that if there is no hardware version information in the header
// then it can be used on ANY hardware version. The behavior of the server
// in this case is not spelled out by the specification.
if (hardwareVersion != NULL
&& headerHasHardwareVersions(image->header)
&& !(*hardwareVersion >= image->header->minimumHardwareVersion
&& *hardwareVersion <= image->header->maximumHardwareVersion)) {
return emberAfInvalidImageId;
}
id.firmwareVersion = image->header->firmwareVersion;
return id;
}
const char* emAfOtaStorageGetFilepath(const EmberAfOtaImageId* id)
{
OtaImage* image = findImageById(id);
if (image == NULL) {
return NULL;
}
return image->filepath;
OtaImage* image = findImageById(id);
if (image == NULL) {
return NULL;
}
return image->filepath;
}
EmberAfOtaImageId emberAfOtaStorageIteratorFirstCallback(void)
{
iterator = imageListFirst;
return getIteratorImageId();
iterator = imageListFirst;
return getIteratorImageId();
}
EmberAfOtaImageId emberAfOtaStorageIteratorNextCallback(void)
{
if (iterator != NULL) {
iterator = (OtaImage*)iterator->next;
}
return getIteratorImageId();
if (iterator != NULL) {
iterator = (OtaImage*)iterator->next;
}
return getIteratorImageId();
}
EmberAfOtaStorageStatus emberAfOtaStorageReadImageDataCallback(const EmberAfOtaImageId* id,
uint32_t offset,
uint32_t length,
uint8_t* returnData,
uint32_t* returnedLength)
uint32_t offset,
uint32_t length,
uint8_t* returnData,
uint32_t* returnedLength)
{
OtaImage* image = imageSearchInternal(id);
if (image == NULL) {
error("No such Image (Mfg ID: 0x%04X, Image ID: 0x%04X, Version: 0x%08X\n",
id->manufacturerId,
id->imageTypeId,
id->firmwareVersion);
return EMBER_AF_OTA_STORAGE_ERROR;
}
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
FILE* fileHandle = fopen(image->filepath, "rb");
if (fileHandle == NULL) {
error("Failed to open file '%s' for reading: %s\n",
image->filenameStart,
strerror(errno));
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (0 != fseek(fileHandle, offset, SEEK_SET)) {
error("Failed to seek in file '%s' to offset %d\n",
image->filenameStart,
offset);
fclose(fileHandle);
return EMBER_AF_OTA_STORAGE_ERROR;
}
*returnedLength = fread(returnData, 1, length, fileHandle);
fclose(fileHandle);
return EMBER_AF_OTA_STORAGE_SUCCESS;
OtaImage* image = imageSearchInternal(id);
if (image == NULL) {
error("No such Image (Mfg ID: 0x%04X, Image ID: 0x%04X, Version: 0x%08X\n",
id->manufacturerId,
id->imageTypeId,
id->firmwareVersion);
return EMBER_AF_OTA_STORAGE_ERROR;
}
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
FILE* fileHandle = fopen(image->filepath, "rb");
if (fileHandle == NULL) {
error("Failed to open file '%s' for reading: %s\n",
image->filenameStart,
strerror(errno));
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (0 != fseek(fileHandle, offset, SEEK_SET)) {
error("Failed to seek in file '%s' to offset %d\n",
image->filenameStart,
offset);
fclose(fileHandle);
return EMBER_AF_OTA_STORAGE_ERROR;
}
*returnedLength = fread(returnData, 1, length, fileHandle);
fclose(fileHandle);
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
EmberAfOtaStorageStatus emAfOtaStorageCreateImage(EmberAfOtaHeader* header,
const char* filename)
const char* filename)
{
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
FILE* fileHandle = fopen(filename, "wb");
if (fileHandle == NULL) {
error("Could not open file '%s' for writing: %s\n",
filename,
strerror(errno));
return EMBER_AF_OTA_STORAGE_ERROR;
}
uint8_t buffer[OTA_MAXIMUM_HEADER_LENGTH];
uint8_t* bufferPtr = buffer;
memcpy(bufferPtr, otaFileMagicNumberBytes, 4);
bufferPtr += 4;
bufferPtr[0] = (uint8_t)(header->headerVersion);
bufferPtr[1] = (uint8_t)(header->headerVersion >> 8);
bufferPtr += 2;
mapHeaderFieldDefinitionToDataStruct(header);
header->headerLength = calculateOtaFileHeaderLength(header);
header->imageSize += header->headerLength;
EmberAfOtaBootloadFileHeaderFieldIndex_t fieldIndex = HEADER_LENGTH_INDEX;
while (fieldIndex < FIELD_INDEX_MAX) {
const EmberAfOtaHeaderFieldDefinition *definition = emGetOtaHeaderFieldDefinition(header->headerVersion, fieldIndex);
debug(config.memoryDebug,
"Writing Header Field: %s, bufferPtr: 0x%08X\n",
definition->name,
(unsigned int)bufferPtr);
bufferPtr = writeHeaderDataToBuffer(fieldIndex,
header->headerVersion,
bufferPtr);
// I have run into stack corruption bugs in the past so this
// is to try and catch any new issues.
assert(bufferPtr - buffer <= OTA_MAXIMUM_HEADER_LENGTH);
fieldIndex++;
}
unmapHeaderFieldDefinitions();
size_t written = fwrite(buffer, 1, header->headerLength, fileHandle);
if (written != header->headerLength) {
error("Wrote only %d bytes but expected to write %d.\n",
written,
header->headerLength);
fclose(fileHandle);
return EMBER_AF_OTA_STORAGE_ERROR;
}
fflush(fileHandle);
fclose(fileHandle);
return EMBER_AF_OTA_STORAGE_SUCCESS;
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
FILE* fileHandle = fopen(filename, "wb");
if (fileHandle == NULL) {
error("Could not open file '%s' for writing: %s\n",
filename,
strerror(errno));
return EMBER_AF_OTA_STORAGE_ERROR;
}
uint8_t buffer[OTA_MAXIMUM_HEADER_LENGTH];
uint8_t* bufferPtr = buffer;
memcpy(bufferPtr, otaFileMagicNumberBytes, 4);
bufferPtr += 4;
bufferPtr[0] = (uint8_t)(header->headerVersion);
bufferPtr[1] = (uint8_t)(header->headerVersion >> 8);
bufferPtr += 2;
mapHeaderFieldDefinitionToDataStruct(header);
header->headerLength = calculateOtaFileHeaderLength(header);
header->imageSize += header->headerLength;
EmberAfOtaBootloadFileHeaderFieldIndex_t fieldIndex = HEADER_LENGTH_INDEX;
while (fieldIndex < FIELD_INDEX_MAX) {
const EmberAfOtaHeaderFieldDefinition *definition = emGetOtaHeaderFieldDefinition(header->headerVersion, fieldIndex);
debug(config.memoryDebug,
"Writing Header Field: %s, bufferPtr: 0x%08X\n",
definition->name,
(unsigned int)bufferPtr);
bufferPtr = writeHeaderDataToBuffer(fieldIndex,
header->headerVersion,
bufferPtr);
// I have run into stack corruption bugs in the past so this
// is to try and catch any new issues.
assert(bufferPtr - buffer <= OTA_MAXIMUM_HEADER_LENGTH);
fieldIndex++;
}
unmapHeaderFieldDefinitions();
size_t written = fwrite(buffer, 1, header->headerLength, fileHandle);
if (written != header->headerLength) {
error("Wrote only %d bytes but expected to write %d.\n",
written,
header->headerLength);
fclose(fileHandle);
return EMBER_AF_OTA_STORAGE_ERROR;
}
fflush(fileHandle);
fclose(fileHandle);
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
EmberAfOtaStorageStatus emAfOtaStorageAddImageFile(const char* filename)
{
return (NULL == addImageFileToList(filename,
false) // print image info?
? EMBER_AF_OTA_STORAGE_ERROR
: EMBER_AF_OTA_STORAGE_SUCCESS);
return (NULL == addImageFileToList(filename,
false) // print image info?
? EMBER_AF_OTA_STORAGE_ERROR
: EMBER_AF_OTA_STORAGE_SUCCESS);
}
EmberAfOtaStorageStatus emberAfOtaStorageDeleteImageCallback(const EmberAfOtaImageId* id)
{
OtaImage* image = imageSearchInternal(id);
// EMAPPFWKV2-1169: if we find the image, we delete it and return success.
// If we don't have the image, then we are ready to receive another one,
// so we should return success.
if (image == NULL) {
if (config.printFileDiscoveryOrRemoval) {
note("No such Image (Mfg ID: 0x%04X, Image ID: 0x%04X, Version: 0x%08X\n",
id->manufacturerId,
id->imageTypeId,
id->firmwareVersion);
}
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
if (config.printFileDiscoveryOrRemoval) {
note("Image '%s' removed from storage list. NOT deleted from disk.\n",
image->filenameStart);
}
removeImage(image);
return EMBER_AF_OTA_STORAGE_SUCCESS;
OtaImage* image = imageSearchInternal(id);
// EMAPPFWKV2-1169: if we find the image, we delete it and return success.
// If we don't have the image, then we are ready to receive another one,
// so we should return success.
if (image == NULL) {
if (config.printFileDiscoveryOrRemoval) {
note("No such Image (Mfg ID: 0x%04X, Image ID: 0x%04X, Version: 0x%08X\n",
id->manufacturerId,
id->imageTypeId,
id->firmwareVersion);
}
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
if (config.printFileDiscoveryOrRemoval) {
note("Image '%s' removed from storage list. NOT deleted from disk.\n",
image->filenameStart);
}
removeImage(image);
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
// Although the call is to "clear" temp data we actually end up allocating
// space for the filename and creating an empty file.
EmberAfOtaStorageStatus emberAfOtaStorageClearTempDataCallback(void)
{
EmberAfOtaStorageStatus status = EMBER_AF_OTA_STORAGE_ERROR;
FILE* fileHandle = NULL;
if (!storageDeviceIsDirectory) {
error("Cannot create temp. OTA data because storage device is a file, not a directory.\n");
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (storageDevice == NULL) {
error("No storage device defined!");
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (tempStorageFilepath == NULL) {
// Add 1 to make sure we have room for a NULL terminating character
int tempFilepathLength = (strlen(storageDevice)
+ strlen(tempStorageFile) + 1);
if (tempFilepathLength > MAX_FILEPATH_LENGTH) {
goto clearTempDataCallbackDone;
}
tempStorageFilepath = myMalloc(tempFilepathLength,
"otaStorageCreateTempData(): tempFilepath");
if (tempStorageFilepath == NULL) {
goto clearTempDataCallbackDone;
}
snprintf(tempStorageFilepath,
tempFilepathLength,
"%s%s",
storageDevice,
tempStorageFile);
}
OtaImage* image = findImageByFilename(tempStorageFilepath);
if (image) {
removeImage(image);
}
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
fileHandle = fopen(tempStorageFilepath,
"wb"); // truncate the file to zero length
if (fileHandle == NULL) {
error("Could not open temporary file '%s' for writing: %s\n",
tempStorageFilepath,
strerror(errno));
} else {
status = EMBER_AF_OTA_STORAGE_SUCCESS;
}
clearTempDataCallbackDone:
// tempStorageFilepath is used throughout the life of the storage
// device, so we only need to free it if there was an error,
// or when the storage device changed. We expect we can
// only change the storage device if we first call emAfOtaStorageClose()
// which frees the tempStorageFilepath data as well.
if ((status != EMBER_AF_OTA_STORAGE_SUCCESS)
&& tempStorageFilepath) {
myFree(tempStorageFilepath);
tempStorageFilepath = NULL;
}
if (fileHandle) {
fclose(fileHandle);
}
return status;
EmberAfOtaStorageStatus status = EMBER_AF_OTA_STORAGE_ERROR;
FILE* fileHandle = NULL;
if (!storageDeviceIsDirectory) {
error("Cannot create temp. OTA data because storage device is a file, not a directory.\n");
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (storageDevice == NULL) {
error("No storage device defined!");
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (tempStorageFilepath == NULL) {
// Add 1 to make sure we have room for a NULL terminating character
int tempFilepathLength = (strlen(storageDevice)
+ strlen(tempStorageFile) + 1);
if (tempFilepathLength > MAX_FILEPATH_LENGTH) {
goto clearTempDataCallbackDone;
}
tempStorageFilepath = myMalloc(tempFilepathLength,
"otaStorageCreateTempData(): tempFilepath");
if (tempStorageFilepath == NULL) {
goto clearTempDataCallbackDone;
}
snprintf(tempStorageFilepath,
tempFilepathLength,
"%s%s",
storageDevice,
tempStorageFile);
}
OtaImage* image = findImageByFilename(tempStorageFilepath);
if (image) {
removeImage(image);
}
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
fileHandle = fopen(tempStorageFilepath,
"wb"); // truncate the file to zero length
if (fileHandle == NULL) {
error("Could not open temporary file '%s' for writing: %s\n",
tempStorageFilepath,
strerror(errno));
} else {
status = EMBER_AF_OTA_STORAGE_SUCCESS;
}
clearTempDataCallbackDone:
// tempStorageFilepath is used throughout the life of the storage
// device, so we only need to free it if there was an error,
// or when the storage device changed. We expect we can
// only change the storage device if we first call emAfOtaStorageClose()
// which frees the tempStorageFilepath data as well.
if ((status != EMBER_AF_OTA_STORAGE_SUCCESS)
&& tempStorageFilepath) {
myFree(tempStorageFilepath);
tempStorageFilepath = NULL;
}
if (fileHandle) {
fclose(fileHandle);
}
return status;
}
EmberAfOtaStorageStatus emberAfOtaStorageWriteTempDataCallback(uint32_t offset,
uint32_t length,
const uint8_t* data)
uint32_t length,
const uint8_t* data)
{
if (tempStorageFilepath == NULL) {
return EMBER_AF_OTA_STORAGE_ERROR;
}
return writeRawData(offset, tempStorageFilepath, length, data);
if (tempStorageFilepath == NULL) {
return EMBER_AF_OTA_STORAGE_ERROR;
}
return writeRawData(offset, tempStorageFilepath, length, data);
}
EmberAfOtaStorageStatus emberAfOtaStorageCheckTempDataCallback(uint32_t* returnOffset,
uint32_t* returnTotalSize,
EmberAfOtaImageId* returnOtaImageId)
uint32_t* returnTotalSize,
EmberAfOtaImageId* returnOtaImageId)
{
OtaImage* image;
if (tempStorageFilepath == NULL) {
return EMBER_AF_OTA_STORAGE_ERROR;
}
image = addImageFileToList(tempStorageFilepath, true);
if (image == NULL) {
return EMBER_AF_OTA_STORAGE_ERROR;
}
*returnTotalSize = image->header->imageSize;
*returnOffset = image->fileSize;
MEMSET(returnOtaImageId, 0, sizeof(EmberAfOtaImageId));
*returnOtaImageId = emAfOtaStorageGetImageIdFromHeader(image->header);
return EMBER_AF_OTA_STORAGE_SUCCESS;
OtaImage* image;
if (tempStorageFilepath == NULL) {
return EMBER_AF_OTA_STORAGE_ERROR;
}
image = addImageFileToList(tempStorageFilepath, true);
if (image == NULL) {
return EMBER_AF_OTA_STORAGE_ERROR;
}
*returnTotalSize = image->header->imageSize;
*returnOffset = image->fileSize;
MEMSET(returnOtaImageId, 0, sizeof(EmberAfOtaImageId));
*returnOtaImageId = emAfOtaStorageGetImageIdFromHeader(image->header);
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
EmberAfOtaStorageStatus emAfOtaStorageAppendImageData(const char* filename,
uint32_t length,
const uint8_t* data)
uint32_t length,
const uint8_t* data)
{
OtaImage* image = findImageByFilename(filename);
if (image != NULL) {
// The file is already in use by the storage device, so it cannot
// be modified.
return EMBER_AF_OTA_STORAGE_ERROR;
}
return writeRawData(APPEND_OFFSET, filename, length, data);
OtaImage* image = findImageByFilename(filename);
if (image != NULL) {
// The file is already in use by the storage device, so it cannot
// be modified.
return EMBER_AF_OTA_STORAGE_ERROR;
}
return writeRawData(APPEND_OFFSET, filename, length, data);
}
int remainingAllocations(void)
{
return allocations;
return allocations;
}
EmberAfOtaStorageStatus emberAfOtaStorageGetFullHeaderCallback(const EmberAfOtaImageId* id,
EmberAfOtaHeader* returnData)
EmberAfOtaHeader* returnData)
{
OtaImage* image = imageSearchInternal(id);
if (image == NULL) {
return false;
}
MEMCOPY(returnData, image->header, sizeof(EmberAfOtaHeader));
return EMBER_AF_OTA_STORAGE_SUCCESS;
OtaImage* image = imageSearchInternal(id);
if (image == NULL) {
return false;
}
MEMCOPY(returnData, image->header, sizeof(EmberAfOtaHeader));
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
uint32_t emberAfOtaStorageGetTotalImageSizeCallback(const EmberAfOtaImageId* id)
{
OtaImage* image = imageSearchInternal(id);
if (image == NULL) {
return 0;
}
return image->fileSize;
OtaImage* image = imageSearchInternal(id);
if (image == NULL) {
return 0;
}
return image->fileSize;
}
EmberAfOtaStorageStatus emberAfOtaStorageFinishDownloadCallback(uint32_t offset)
{
// Not used. The OS filesystem keeps track of the latest download offset.
return EMBER_AF_OTA_STORAGE_SUCCESS;
// Not used. The OS filesystem keeps track of the latest download offset.
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
void emAfOtaStorageInfoPrint(void)
{
note("Storage Module: OTA POSIX Filesystem Storage Module\n");
note("Storage Directory: %s\n", defaultStorageDirectory);
note("Storage Module: OTA POSIX Filesystem Storage Module\n");
note("Storage Directory: %s\n", defaultStorageDirectory);
}
uint32_t emberAfOtaStorageDriverMaxDownloadSizeCallback(void)
{
// In theory we are limited by the local disk space, but for now
// assume there is no limit.
return 0xFFFFFFFFUL;
// In theory we are limited by the local disk space, but for now
// assume there is no limit.
return 0xFFFFFFFFUL;
}
EmberAfOtaStorageStatus emberAfOtaStorageDriverPrepareToResumeDownloadCallback(void)
{
return EMBER_AF_OTA_STORAGE_SUCCESS;
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
//==============================================================================
......@@ -730,193 +730,185 @@ EmberAfOtaStorageStatus emberAfOtaStorageDriverPrepareToResumeDownloadCallback(v
static EmberAfOtaStorageStatus createDefaultStorageDirectory(void)
{
struct stat statInfo;
int returnValue = stat(defaultStorageDirectory, &statInfo);
if (returnValue == 0) {
if (!S_ISDIR(statInfo.st_mode)) {
error("Default storage directory '%s' is not a directory!\n",
defaultStorageDirectory);
return EMBER_AF_OTA_STORAGE_ERROR;
}
debug(config.fileDebug,
"Default storage directory already exists '%s'\n",
defaultStorageDirectory);
} else {
// Does not exist, therefore we must create it.
debug(config.fileDebug,
"Creating default storage directory '%s'\n",
defaultStorageDirectory);
int status = portableMkdir(defaultStorageDirectory);
if (status != 0) {
error("Could not create default directory '%s': %s\n",
defaultStorageDirectory,
strerror(errno));
return EMBER_AF_OTA_STORAGE_ERROR;
}
}
return emAfOtaSetStorageDevice(defaultStorageDirectory);
struct stat statInfo;
int returnValue = stat(defaultStorageDirectory, &statInfo);
if (returnValue == 0) {
if (!S_ISDIR(statInfo.st_mode)) {
error("Default storage directory '%s' is not a directory!\n",
defaultStorageDirectory);
return EMBER_AF_OTA_STORAGE_ERROR;
}
debug(config.fileDebug,
"Default storage directory already exists '%s'\n",
defaultStorageDirectory);
} else {
// Does not exist, therefore we must create it.
debug(config.fileDebug,
"Creating default storage directory '%s'\n",
defaultStorageDirectory);
int status = portableMkdir(defaultStorageDirectory);
if (status != 0) {
error("Could not create default directory '%s': %s\n",
defaultStorageDirectory,
strerror(errno));
return EMBER_AF_OTA_STORAGE_ERROR;
}
}
return emAfOtaSetStorageDevice(defaultStorageDirectory);
}
static bool doEui64sMatch(const uint8_t* firstEui64,
const uint8_t* secondEui64)
const uint8_t* secondEui64)
{
return (0 == MEMCOMPARE(firstEui64, secondEui64, EUI64_SIZE));
return (0 == MEMCOMPARE(firstEui64, secondEui64, EUI64_SIZE));
}
static OtaImage* findImageByFilename(const char* tempFilepath)
{
OtaImage* ptr = imageListFirst;
while (ptr != NULL) {
if (0 == strcmp(ptr->filepath, tempFilepath)) {
return ptr;
}
ptr = (OtaImage*)ptr->next;
}
return NULL;
OtaImage* ptr = imageListFirst;
while (ptr != NULL) {
if (0 == strcmp(ptr->filepath, tempFilepath)) {
return ptr;
}
ptr = (OtaImage*)ptr->next;
}
return NULL;
}
static EmberAfOtaStorageStatus writeRawData(uint32_t offset,
const char* filepath,
uint32_t length,
const uint8_t* data)
const char* filepath,
uint32_t length,
const uint8_t* data)
{
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
FILE* fileHandle = fopen(filepath,
"r+b");
EmberAfOtaStorageStatus status = EMBER_AF_OTA_STORAGE_ERROR;
int whence = SEEK_SET;
if (fileHandle == NULL) {
error("Could not open file '%s' for writing: %s\n",
filepath,
strerror(errno));
goto writeEnd;
}
if (offset == APPEND_OFFSET) {
offset = 0;
whence = SEEK_END;
}
if (0 != fseek(fileHandle, offset, whence)) {
error("Could not seek to offset 0x%08X (%s) in file '%s': %s\n",
offset,
(whence == SEEK_END ? "SEEK_END" : "SEEK_SET"),
filepath,
strerror(errno));
goto writeEnd;
}
size_t written = fwrite(data, 1, length, fileHandle);
if (written != length) {
error("Tried to write %d bytes but wrote %d\n", length, written);
goto writeEnd;
}
status = EMBER_AF_OTA_STORAGE_SUCCESS;
writeEnd:
if (fileHandle) {
fclose(fileHandle);
}
return status;
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
FILE* fileHandle = fopen(filepath,
"r+b");
EmberAfOtaStorageStatus status = EMBER_AF_OTA_STORAGE_ERROR;
int whence = SEEK_SET;
if (fileHandle == NULL) {
error("Could not open file '%s' for writing: %s\n",
filepath,
strerror(errno));
goto writeEnd;
}
if (offset == APPEND_OFFSET) {
offset = 0;
whence = SEEK_END;
}
if (0 != fseek(fileHandle, offset, whence)) {
error("Could not seek to offset 0x%08X (%s) in file '%s': %s\n",
offset,
(whence == SEEK_END ? "SEEK_END" : "SEEK_SET"),
filepath,
strerror(errno));
goto writeEnd;
}
size_t written = fwrite(data, 1, length, fileHandle);
if (written != length) {
error("Tried to write %d bytes but wrote %d\n", length, written);
goto writeEnd;
}
status = EMBER_AF_OTA_STORAGE_SUCCESS;
writeEnd:
if (fileHandle) {
fclose(fileHandle);
}
return status;
}
static void removeImage(OtaImage* image)
{
OtaImage* before = (OtaImage*)image->prev;
OtaImage* after = (OtaImage*)image->next;
if (before) {
before->next = (struct OtaImage*)after;
}
if (after) {
after->prev = (struct OtaImage*)before;
}
if (image == imageListFirst) {
imageListFirst = after;
}
if (image == imageListLast) {
imageListLast = before;
}
freeOtaImage(image);
imageCount--;
OtaImage* before = (OtaImage*)image->prev;
OtaImage* after = (OtaImage*)image->next;
if (before) {
before->next = (struct OtaImage*)after;
}
if (after) {
after->prev = (struct OtaImage*)before;
}
if (image == imageListFirst) {
imageListFirst = after;
}
if (image == imageListLast) {
imageListLast = before;
}
freeOtaImage(image);
imageCount--;
}
static void printEui64(uint8_t* eui64)
{
note("%02X%02X%02X%02X%02X%02X%02X%02X",
eui64[0],
eui64[1],
eui64[2],
eui64[3],
eui64[4],
eui64[5],
eui64[6],
eui64[7]);
note("%02X%02X%02X%02X%02X%02X%02X%02X",
eui64[0],
eui64[1],
eui64[2],
eui64[3],
eui64[4],
eui64[5],
eui64[6],
eui64[7]);
}
static void printHeaderInfo(const EmberAfOtaHeader* header)
{
if (!config.printFileDiscoveryOrRemoval) {
return;
}
// printf(" Header Version: 0x%04X\n",
// header->headerVersion);
// printf(" Header Length: 0x%04X\n",
// header->headerLength);
// printf(" Field Control: 0x%04X\n",
// header->fieldControl);
note(" Manufacturer ID: 0x%04X\n",
header->manufacturerId);
note(" Image Type ID: 0x%04X\n",
header->imageTypeId);
note(" Version: 0x%08X\n",
header->firmwareVersion);
note(" Header String: %s\n",
header->headerString);
// printf("\n");
if (!config.printFileDiscoveryOrRemoval) {
return;
}
note(" Manufacturer ID: 0x%04X\n",
header->manufacturerId);
note(" Image Type ID: 0x%04X\n",
header->imageTypeId);
note(" Version: 0x%08X\n",
header->firmwareVersion);
note(" Header String: %s\n",
header->headerString);
}
static OtaImage* addImageFileToList(const char* filename,
bool printImageInfo)
bool printImageInfo)
{
OtaImage* newImage = (OtaImage*)myMalloc(sizeof(OtaImage),
"addImageFileToList():OtaImage");
if (newImage == NULL) {
return NULL;
}
memset(newImage, 0, sizeof(OtaImage));
struct stat statInfo;
if (0 != stat(filename, &statInfo)) {
myFree(newImage);
return NULL;
}
newImage->fileSize = statInfo.st_size;
int length = 1 + strnlen(filename, OTA_MAX_FILENAME_LENGTH);
newImage->filepath = myMalloc(length, "filename");
if (newImage->filepath == NULL) {
goto dontAdd;
}
strncpy(newImage->filepath, filename, length);
newImage->filepath[length - 1] = '\0';
newImage->filenameStart = strrchr(newImage->filepath,
'/');
if (newImage->filenameStart == NULL) {
newImage->filenameStart = newImage->filepath;
} else {
newImage->filenameStart++; // +1 for the '/' character
}
newImage->header = readImageHeader(filename);
if (newImage->header == NULL) {
goto dontAdd;
}
OtaImage* newImage = (OtaImage*)myMalloc(sizeof(OtaImage),
"addImageFileToList():OtaImage");
if (newImage == NULL) {
return NULL;
}
memset(newImage, 0, sizeof(OtaImage));
struct stat statInfo;
if (0 != stat(filename, &statInfo)) {
myFree(newImage);
return NULL;
}
newImage->fileSize = statInfo.st_size;
int length = 1 + strnlen(filename, OTA_MAX_FILENAME_LENGTH);
newImage->filepath = myMalloc(length, "filename");
if (newImage->filepath == NULL) {
goto dontAdd;
}
strncpy(newImage->filepath, filename, length);
newImage->filepath[length - 1] = '\0';
newImage->filenameStart = strrchr(newImage->filepath,
'/');
if (newImage->filenameStart == NULL) {
newImage->filenameStart = newImage->filepath;
} else {
newImage->filenameStart++; // +1 for the '/' character
}
newImage->header = readImageHeader(filename);
if (newImage->header == NULL) {
goto dontAdd;
}
EmberAfOtaImageId new = {
newImage->header->manufacturerId,
......@@ -924,7 +916,7 @@ static OtaImage* addImageFileToList(const char* filename,
INVALID_FIRMWARE_VERSION,
INVALID_EUI64,
};
OtaImage* test;
OtaImage* test;
if (headerHasUpgradeFileDest(newImage->header)) {
MEMCOPY(new.deviceSpecificFileEui64,
&newImage->header->upgradeFileDestination,
......@@ -990,241 +982,241 @@ static OtaImage* addImageFileToList(const char* filename,
imageCount++;
return newImage;
dontAdd:
freeOtaImage(newImage);
return NULL;
dontAdd:
freeOtaImage(newImage);
return NULL;
}
// This function assumes that the file pointer is at the start of the
// file. It will modify the pointer in the passed FILE*
static bool checkMagicNumber(FILE* fileHandle, bool printError)
{
uint8_t magicNumber[4];
if (1 != fread((void*)&magicNumber, 4, 1, fileHandle)) {
return false;
}
if (0 != memcmp(magicNumber, otaFileMagicNumberBytes, 4)) {
if (printError) {
error("File has bad magic number.\n");
}
return false;
}
return true;
uint8_t magicNumber[4];
if (1 != fread((void*)&magicNumber, 4, 1, fileHandle)) {
return false;
}
if (0 != memcmp(magicNumber, otaFileMagicNumberBytes, 4)) {
if (printError) {
error("File has bad magic number.\n");
}
return false;
}
return true;
}
static EmberAfOtaStorageStatus initImageDirectory(void)
{
DIR* dir = opendir(storageDevice);
if (dir == NULL) {
error("Could not open directory: %s\n", strerror(errno));
return EMBER_AF_OTA_STORAGE_ERROR;
}
debug(config.fileDebug, "Opened Storage Directory: %s\n", storageDevice);
struct dirent* dirEntry = readdir(dir);
while (dirEntry != NULL) {
FILE* fileHandle = NULL;
debug(config.fileDebug, "Considering file '%s'\n", dirEntry->d_name);
// +2 for trailing '/' and '\0'
int pathLength = strlen(storageDevice) + strlen(dirEntry->d_name) + 2;
if (pathLength > MAX_FILEPATH_LENGTH) {
error("Filepath too long (max: %d) skipping file '%s'",
MAX_FILEPATH_LENGTH,
dirEntry->d_name);
goto continueReadingDir;
}
char* filePath = myMalloc(pathLength, "initImageDirectory(): filepath");
if (filePath == NULL) {
error("Failed to allocate memory for filepath.\n");
goto continueReadingDir;
}
sprintf(filePath, "%s%s",
storageDevice,
dirEntry->d_name);
debug(config.fileDebug, "Full filepath: '%s'\n", filePath);
struct stat buffer;
if (0 != stat(filePath, &buffer)) {
fprintf(stderr,
"Error: Could not stat file '%s': %s\n",
filePath,
strerror(errno));
goto continueReadingDir;
} else if (S_ISDIR(buffer.st_mode) || !S_ISREG(buffer.st_mode)) {
debug(config.fileDebug,
"Ignoring '%s' because it is not a regular file.\n",
dirEntry->d_name);
goto continueReadingDir;
}
// NOTE: dirent.d_name may have a limited length due to POSIX compliance.
// Not sure if it will work for all possible filenames. However
// the length does NOT include the directory portion, so it should be
// able to store most all filenames (<256 characters).
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
fileHandle = fopen(filePath, "rb");
if (fileHandle == NULL) {
error("Could not open file '%s' for reading: %s\n",
filePath,
strerror(errno));
goto continueReadingDir;
}
if (!checkMagicNumber(fileHandle, false)) {
goto continueReadingDir;
}
if (config.ignoreFilesWithUnderscorePrefix
&& dirEntry->d_name[0] == '_') {
// As a means of making this program omit certain OTA files from
// processing, we arbitrarily choose to ignore files starting with '_'.
// This is done in part to be able to store multiple OTA files in
// the same directory that have the same unique manufacturer and image
// type ID. Normally when this code finds a second image with the
// same manufacturer and image type ID it picks the one with latest
// version number.
// By changing the file name we can keep the file intact and
// have this code just skip it.
printf("Ignoring OTA file '%s' since it starts with '_'.\n",
dirEntry->d_name);
goto continueReadingDir;
}
if (config.printFileDiscoveryOrRemoval) {
note("Found OTA file '%s'\n", dirEntry->d_name);
}
// We don't really care about the return code because we want to keep trying
// to add files.
OtaImage* newImage = addImageFileToList(filePath, true);
if (config.fileAddedHandler != NULL
&& newImage != NULL) {
(config.fileAddedHandler)(newImage->header);
}
continueReadingDir:
if (fileHandle) {
fclose(fileHandle);
fileHandle = NULL;
}
if (filePath) {
myFree(filePath);
filePath = NULL;
}
dirEntry = readdir(dir);
}
if (config.printFileDiscoveryOrRemoval) {
printf("Found %d files\n\n", imageCount);
}
closedir(dir);
return EMBER_AF_OTA_STORAGE_SUCCESS;
DIR* dir = opendir(storageDevice);
if (dir == NULL) {
error("Could not open directory: %s\n", strerror(errno));
return EMBER_AF_OTA_STORAGE_ERROR;
}
debug(config.fileDebug, "Opened Storage Directory: %s\n", storageDevice);
struct dirent* dirEntry = readdir(dir);
while (dirEntry != NULL) {
FILE* fileHandle = NULL;
debug(config.fileDebug, "Considering file '%s'\n", dirEntry->d_name);
// +2 for trailing '/' and '\0'
int pathLength = strlen(storageDevice) + strlen(dirEntry->d_name) + 2;
if (pathLength > MAX_FILEPATH_LENGTH) {
error("Filepath too long (max: %d) skipping file '%s'",
MAX_FILEPATH_LENGTH,
dirEntry->d_name);
goto continueReadingDir;
}
char* filePath = myMalloc(pathLength, "initImageDirectory(): filepath");
if (filePath == NULL) {
error("Failed to allocate memory for filepath.\n");
goto continueReadingDir;
}
sprintf(filePath, "%s%s",
storageDevice,
dirEntry->d_name);
debug(config.fileDebug, "Full filepath: '%s'\n", filePath);
struct stat buffer;
if (0 != stat(filePath, &buffer)) {
fprintf(stderr,
"Error: Could not stat file '%s': %s\n",
filePath,
strerror(errno));
goto continueReadingDir;
} else if (S_ISDIR(buffer.st_mode) || !S_ISREG(buffer.st_mode)) {
debug(config.fileDebug,
"Ignoring '%s' because it is not a regular file.\n",
dirEntry->d_name);
goto continueReadingDir;
}
// NOTE: dirent.d_name may have a limited length due to POSIX compliance.
// Not sure if it will work for all possible filenames. However
// the length does NOT include the directory portion, so it should be
// able to store most all filenames (<256 characters).
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
fileHandle = fopen(filePath, "rb");
if (fileHandle == NULL) {
error("Could not open file '%s' for reading: %s\n",
filePath,
strerror(errno));
goto continueReadingDir;
}
if (!checkMagicNumber(fileHandle, false)) {
goto continueReadingDir;
}
if (config.ignoreFilesWithUnderscorePrefix
&& dirEntry->d_name[0] == '_') {
// As a means of making this program omit certain OTA files from
// processing, we arbitrarily choose to ignore files starting with '_'.
// This is done in part to be able to store multiple OTA files in
// the same directory that have the same unique manufacturer and image
// type ID. Normally when this code finds a second image with the
// same manufacturer and image type ID it picks the one with latest
// version number.
// By changing the file name we can keep the file intact and
// have this code just skip it.
printf("Ignoring OTA file '%s' since it starts with '_'.\n",
dirEntry->d_name);
goto continueReadingDir;
}
if (config.printFileDiscoveryOrRemoval) {
note("Found OTA file '%s'\n", dirEntry->d_name);
}
// We don't really care about the return code because we want to keep trying
// to add files.
OtaImage* newImage = addImageFileToList(filePath, true);
if (config.fileAddedHandler != NULL
&& newImage != NULL) {
(config.fileAddedHandler)(newImage->header);
}
continueReadingDir:
if (fileHandle) {
fclose(fileHandle);
fileHandle = NULL;
}
if (filePath) {
myFree(filePath);
filePath = NULL;
}
dirEntry = readdir(dir);
}
if (config.printFileDiscoveryOrRemoval) {
printf("Found %d files\n\n", imageCount);
}
closedir(dir);
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
static void freeIfNotNull(void** ptr)
{
if (*ptr != NULL) {
myFree(*ptr);
*ptr = NULL;
}
if (*ptr != NULL) {
myFree(*ptr);
*ptr = NULL;
}
}
static void freeOtaImage(OtaImage* image)
{
if (image == NULL) {
return;
}
freeIfNotNull((void**)&(image->header));
freeIfNotNull((void**)&(image->filepath));
myFree(image);
if (image == NULL) {
return;
}
freeIfNotNull((void**)&(image->header));
freeIfNotNull((void**)&(image->filepath));
myFree(image);
}
const EmberAfOtaHeaderFieldDefinition *emGetOtaHeaderFieldDefinition(uint16_t headerVersion, EmberAfOtaBootloadFileHeaderFieldIndex_t headerIndex)
{
const EmberAfOtaHeaderFieldDefinition invalidField = { NULL, INVALID_FIELD, 0, 0 };
if ((!isValidHeaderVersion(headerVersion)) && (headerIndex != MAGIC_NUMBER_INDEX) && (headerIndex != HEADER_VERSION_INDEX)) {
return &otaHeaderFieldDefinitions[INVALID_FIELD_INDEX];
}
const EmberAfOtaHeaderFieldDefinition invalidField = { NULL, INVALID_FIELD, 0, 0 };
if ((!isValidHeaderVersion(headerVersion)) && (headerIndex != MAGIC_NUMBER_INDEX) && (headerIndex != HEADER_VERSION_INDEX)) {
return &otaHeaderFieldDefinitions[INVALID_FIELD_INDEX];
}
if ((headerIndex == UPGRADE_FILE_DESTINATION_INDEX) && (headerVersion == OTA_HEADER_VERSION_THREAD)) {
static const EmberAfOtaHeaderFieldDefinition threadUpgradeFileDestination = { "Upgrade File Destination", BYTE_ARRAY_FIELD, UID_SIZE, DEVICE_SPECIFIC_FILE_PRESENT_MASK };
return &threadUpgradeFileDestination;
}
if ((headerIndex == UPGRADE_FILE_DESTINATION_INDEX) && (headerVersion == OTA_HEADER_VERSION_THREAD)) {
static const EmberAfOtaHeaderFieldDefinition threadUpgradeFileDestination = { "Upgrade File Destination", BYTE_ARRAY_FIELD, UID_SIZE, DEVICE_SPECIFIC_FILE_PRESENT_MASK };
return &threadUpgradeFileDestination;
}
return &otaHeaderFieldDefinitions[headerIndex];
return &otaHeaderFieldDefinitions[headerIndex];
}
static EmberAfOtaHeader* readImageHeader(const char* filename)
{
EmberAfOtaHeader* header = NULL;
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
FILE* fileHandle = fopen(filename, "rb");
if (fileHandle == NULL) {
goto imageReadError;
}
header = (EmberAfOtaHeader*)myMalloc(sizeof(EmberAfOtaHeader), "readImageHeader():OtaImage");
if (header == NULL) {
goto imageReadError;
}
memset(header, 0, sizeof(EmberAfOtaHeader));
if (!checkMagicNumber(fileHandle, true)) {
goto imageReadError;
}
// In all the following code, we subtract 4 from the lengths because we have
// already read the magic number and therefore do not need to include it in
// our calculations.
uint8_t buffer[OTA_MAXIMUM_HEADER_LENGTH - 4];
uint8_t* bufferPtr = buffer;
int dataRead = fread((void*)bufferPtr,
1, // block size (bytes)
OTA_MAXIMUM_HEADER_LENGTH - 4, // count
fileHandle);
if (dataRead < (OTA_MINIMUM_HEADER_LENGTH - 4)) {
error("OTA header is too short (length = %d but should be a minimum of %d)\n",
dataRead,
OTA_MINIMUM_HEADER_LENGTH - 4);
goto imageReadError;
}
mapHeaderFieldDefinitionToDataStruct(header);
// Read the Version and length first so we can use those to validate the rest
// of the image.
EmberAfOtaStorageStatus status;
uint16_t headerVersionLength = emGetOtaHeaderFieldDefinition(0, HEADER_VERSION_INDEX)->length;
status = readHeaderDataFromBuffer(HEADER_VERSION_INDEX,
0, // we don't know the version yet
bufferPtr,
headerVersionLength,
headerVersionLength);
if (status != EMBER_AF_OTA_STORAGE_SUCCESS) {
goto imageReadError;
}
if (!isValidHeaderVersion(header->headerVersion)) {
error("Unknown header version number 0x%04X in file, cannot parse.\n", header->headerVersion);
goto imageReadError;
}
bufferPtr += 2; // header version field length
uint16_t headerLengthLength = emGetOtaHeaderFieldDefinition(header->headerVersion, HEADER_LENGTH_INDEX)->length;
status = readHeaderDataFromBuffer(HEADER_LENGTH_INDEX,
header->headerVersion,
bufferPtr,
headerLengthLength,
headerLengthLength);
if (status != EMBER_AF_OTA_STORAGE_SUCCESS) {
goto imageReadError;
}
bufferPtr += 2; // header length field length
// subtract 4 for length of "header length" and "header version" fields.
int32_t lengthRemaining = header->headerLength - 4;
dataRead -= 4;
int fieldIndex = FIELD_CONTROL_INDEX;
EmberAfOtaHeader* header = NULL;
// Windows requires the 'b' (binary) as part of the mode so that line endings
// are not truncated. POSIX ignores this.
FILE* fileHandle = fopen(filename, "rb");
if (fileHandle == NULL) {
goto imageReadError;
}
header = (EmberAfOtaHeader*)myMalloc(sizeof(EmberAfOtaHeader), "readImageHeader():OtaImage");
if (header == NULL) {
goto imageReadError;
}
memset(header, 0, sizeof(EmberAfOtaHeader));
if (!checkMagicNumber(fileHandle, true)) {
goto imageReadError;
}
// In all the following code, we subtract 4 from the lengths because we have
// already read the magic number and therefore do not need to include it in
// our calculations.
uint8_t buffer[OTA_MAXIMUM_HEADER_LENGTH - 4];
uint8_t* bufferPtr = buffer;
int dataRead = fread((void*)bufferPtr,
1, // block size (bytes)
OTA_MAXIMUM_HEADER_LENGTH - 4, // count
fileHandle);
if (dataRead < (OTA_MINIMUM_HEADER_LENGTH - 4)) {
error("OTA header is too short (length = %d but should be a minimum of %d)\n",
dataRead,
OTA_MINIMUM_HEADER_LENGTH - 4);
goto imageReadError;
}
mapHeaderFieldDefinitionToDataStruct(header);
// Read the Version and length first so we can use those to validate the rest
// of the image.
EmberAfOtaStorageStatus status;
uint16_t headerVersionLength = emGetOtaHeaderFieldDefinition(0, HEADER_VERSION_INDEX)->length;
status = readHeaderDataFromBuffer(HEADER_VERSION_INDEX,
0, // we don't know the version yet
bufferPtr,
headerVersionLength,
headerVersionLength);
if (status != EMBER_AF_OTA_STORAGE_SUCCESS) {
goto imageReadError;
}
if (!isValidHeaderVersion(header->headerVersion)) {
error("Unknown header version number 0x%04X in file, cannot parse.\n", header->headerVersion);
goto imageReadError;
}
bufferPtr += 2; // header version field length
uint16_t headerLengthLength = emGetOtaHeaderFieldDefinition(header->headerVersion, HEADER_LENGTH_INDEX)->length;
status = readHeaderDataFromBuffer(HEADER_LENGTH_INDEX,
header->headerVersion,
bufferPtr,
headerLengthLength,
headerLengthLength);
if (status != EMBER_AF_OTA_STORAGE_SUCCESS) {
goto imageReadError;
}
bufferPtr += 2; // header length field length
// subtract 4 for length of "header length" and "header version" fields.
int32_t lengthRemaining = header->headerLength - 4;
dataRead -= 4;
int fieldIndex = FIELD_CONTROL_INDEX;
while (fieldIndex < FIELD_INDEX_MAX && dataRead > 0 && lengthRemaining > 0) {
EmberAfOtaStorageStatus status = readHeaderDataFromBuffer(fieldIndex,
header->headerVersion,
......@@ -1264,230 +1256,230 @@ static EmberAfOtaHeader* readImageHeader(const char* filename)
return header;
imageReadError:
unmapHeaderFieldDefinitions();
freeIfNotNull((void**)&header);
fclose(fileHandle);
return NULL;
imageReadError:
unmapHeaderFieldDefinitions();
freeIfNotNull((void**)&header);
fclose(fileHandle);
return NULL;
}
static EmberAfOtaStorageStatus readHeaderDataFromBuffer(EmberAfOtaBootloadFileHeaderFieldIndex_t fieldIndex,
uint16_t headerVersion,
uint8_t* bufferPtr,
int32_t headerLengthRemaining,
int32_t actualBufferDataRemaining)
uint16_t headerVersion,
uint8_t* bufferPtr,
int32_t headerLengthRemaining,
int32_t actualBufferDataRemaining)
{
const EmberAfOtaHeaderFieldDefinition *definition = emGetOtaHeaderFieldDefinition(headerVersion, fieldIndex);
if (definition->maskForOptionalField != ALWAYS_PRESENT_MASK) {
uint16_t fieldControl = *(uint16_t*)(otaHeaderFieldLocations[FIELD_CONTROL_INDEX].location);
if (!(fieldControl & definition->maskForOptionalField)) {
// No more processing.
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
}
if (headerLengthRemaining < definition->length
|| actualBufferDataRemaining < definition->length) {
error("OTA Header does not contain enough data for field '%s'\n",
definition->name);
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (definition->type == INTEGER_FIELD) {
// Unfortunately we have to break up parsing of different integer lengths
// into separate pieces of code because of the way the data
// may be packed in the data structure.
// Previously we tried to just use a generic 'uint32_t*' to point
// to either the location of a 32-bit, 16-bit, or 8-bit value and then write
// the data appropriately. However if the location could only store a 16-bit
// value and we are referring to it as an uint32_t*, then we may write to the
// wrong bytes depending on the how the data is packed.
// Using the correct pointer based on the length sidesteps that problem.
if (1 == definition->length) {
uint8_t *value = (uint8_t*)(otaHeaderFieldLocations[fieldIndex].location);
*value = bufferPtr[0];
} else if (2 == definition->length) {
uint16_t *value = (uint16_t*)(otaHeaderFieldLocations[fieldIndex].location);
*value = (bufferPtr[0]
+ (bufferPtr[1] << 8));
} else if (4 == definition->length) {
uint32_t *value = (uint32_t*)(otaHeaderFieldLocations[fieldIndex].location);
*value = (bufferPtr[0]
+ (bufferPtr[1] << 8)
+ (bufferPtr[2] << 16)
+ (bufferPtr[3] << 24));
} else {
error("Unsupported data value length '%d' for type '%s'.\n",
definition->length,
definition->name);
return EMBER_AF_OTA_STORAGE_ERROR;
}
} else if (definition->type == BYTE_ARRAY_FIELD
|| definition->type == STRING_FIELD) {
memcpy(otaHeaderFieldLocations[fieldIndex].location, bufferPtr, definition->length);
} else {
// Programatic error
error("Unkown field type '%d'\n", definition->type);
return EMBER_AF_OTA_STORAGE_ERROR;
}
otaHeaderFieldLocations[fieldIndex].found = true;
return EMBER_AF_OTA_STORAGE_SUCCESS;
const EmberAfOtaHeaderFieldDefinition *definition = emGetOtaHeaderFieldDefinition(headerVersion, fieldIndex);
if (definition->maskForOptionalField != ALWAYS_PRESENT_MASK) {
uint16_t fieldControl = *(uint16_t*)(otaHeaderFieldLocations[FIELD_CONTROL_INDEX].location);
if (!(fieldControl & definition->maskForOptionalField)) {
// No more processing.
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
}
if (headerLengthRemaining < definition->length
|| actualBufferDataRemaining < definition->length) {
error("OTA Header does not contain enough data for field '%s'\n",
definition->name);
return EMBER_AF_OTA_STORAGE_ERROR;
}
if (definition->type == INTEGER_FIELD) {
// Unfortunately we have to break up parsing of different integer lengths
// into separate pieces of code because of the way the data
// may be packed in the data structure.
// Previously we tried to just use a generic 'uint32_t*' to point
// to either the location of a 32-bit, 16-bit, or 8-bit value and then write
// the data appropriately. However if the location could only store a 16-bit
// value and we are referring to it as an uint32_t*, then we may write to the
// wrong bytes depending on the how the data is packed.
// Using the correct pointer based on the length sidesteps that problem.
if (1 == definition->length) {
uint8_t *value = (uint8_t*)(otaHeaderFieldLocations[fieldIndex].location);
*value = bufferPtr[0];
} else if (2 == definition->length) {
uint16_t *value = (uint16_t*)(otaHeaderFieldLocations[fieldIndex].location);
*value = (bufferPtr[0]
+ (bufferPtr[1] << 8));
} else if (4 == definition->length) {
uint32_t *value = (uint32_t*)(otaHeaderFieldLocations[fieldIndex].location);
*value = (bufferPtr[0]
+ (bufferPtr[1] << 8)
+ (bufferPtr[2] << 16)
+ (bufferPtr[3] << 24));
} else {
error("Unsupported data value length '%d' for type '%s'.\n",
definition->length,
definition->name);
return EMBER_AF_OTA_STORAGE_ERROR;
}
} else if (definition->type == BYTE_ARRAY_FIELD
|| definition->type == STRING_FIELD) {
memcpy(otaHeaderFieldLocations[fieldIndex].location, bufferPtr, definition->length);
} else {
// Programatic error
error("Unkown field type '%d'\n", definition->type);
return EMBER_AF_OTA_STORAGE_ERROR;
}
otaHeaderFieldLocations[fieldIndex].found = true;
return EMBER_AF_OTA_STORAGE_SUCCESS;
}
static void mapHeaderFieldDefinitionToDataStruct(EmberAfOtaHeader* header)
{
otaHeaderFieldLocations[HEADER_VERSION_INDEX].location = &(header->headerVersion);
otaHeaderFieldLocations[HEADER_LENGTH_INDEX].location = &(header->headerLength);
otaHeaderFieldLocations[FIELD_CONTROL_INDEX].location = &(header->fieldControl);
otaHeaderFieldLocations[MANUFACTURER_ID_INDEX].location = &(header->manufacturerId);
otaHeaderFieldLocations[IMAGE_TYPE_INDEX].location = &(header->imageTypeId);
otaHeaderFieldLocations[FIRMWARE_VERSION_INDEX].location = &(header->firmwareVersion);
otaHeaderFieldLocations[ZIGBEE_STACK_VERSION_INDEX].location = &(header->zigbeeStackVersion);
otaHeaderFieldLocations[TOTAL_IMAGE_SIZE_INDEX].location = &(header->imageSize);
otaHeaderFieldLocations[SECURITY_CREDENTIALS_INDEX].location = &(header->securityCredentials);
otaHeaderFieldLocations[MINIMUM_HARDWARE_VERSION_INDEX].location = &(header->minimumHardwareVersion);
otaHeaderFieldLocations[MAXIMUM_HARDWARE_VERSION_INDEX].location = &(header->maximumHardwareVersion);
// For byte arrays and strings, those are already pointers and we do not want
// with another layer of indirection
otaHeaderFieldLocations[HEADER_STRING_INDEX].location = header->headerString;
otaHeaderFieldLocations[UPGRADE_FILE_DESTINATION_INDEX].location = &header->upgradeFileDestination;
otaHeaderFieldLocations[HEADER_VERSION_INDEX].location = &(header->headerVersion);
otaHeaderFieldLocations[HEADER_LENGTH_INDEX].location = &(header->headerLength);
otaHeaderFieldLocations[FIELD_CONTROL_INDEX].location = &(header->fieldControl);
otaHeaderFieldLocations[MANUFACTURER_ID_INDEX].location = &(header->manufacturerId);
otaHeaderFieldLocations[IMAGE_TYPE_INDEX].location = &(header->imageTypeId);
otaHeaderFieldLocations[FIRMWARE_VERSION_INDEX].location = &(header->firmwareVersion);
otaHeaderFieldLocations[ZIGBEE_STACK_VERSION_INDEX].location = &(header->zigbeeStackVersion);
otaHeaderFieldLocations[TOTAL_IMAGE_SIZE_INDEX].location = &(header->imageSize);
otaHeaderFieldLocations[SECURITY_CREDENTIALS_INDEX].location = &(header->securityCredentials);
otaHeaderFieldLocations[MINIMUM_HARDWARE_VERSION_INDEX].location = &(header->minimumHardwareVersion);
otaHeaderFieldLocations[MAXIMUM_HARDWARE_VERSION_INDEX].location = &(header->maximumHardwareVersion);
// For byte arrays and strings, those are already pointers and we do not want
// with another layer of indirection
otaHeaderFieldLocations[HEADER_STRING_INDEX].location = header->headerString;
otaHeaderFieldLocations[UPGRADE_FILE_DESTINATION_INDEX].location = &header->upgradeFileDestination;
}
static void unmapHeaderFieldDefinitions(void)
{
for (int i = 0; i < FIELD_INDEX_MAX; i++) {
otaHeaderFieldLocations[i].location = NULL;
otaHeaderFieldLocations[i].found = false;
}
for (int i = 0; i < FIELD_INDEX_MAX; i++) {
otaHeaderFieldLocations[i].location = NULL;
otaHeaderFieldLocations[i].found = false;
}
}
static OtaImage* findImageById(const EmberAfOtaImageId* id)
{
OtaImage* ptr = imageListFirst;
while (ptr != NULL) {
if (id->manufacturerId == ptr->header->manufacturerId
&& id->imageTypeId == ptr->header->imageTypeId
&& id->firmwareVersion == ptr->header->firmwareVersion) {
return ptr;
}
ptr = (OtaImage*)ptr->next;
}
return NULL;
OtaImage* ptr = imageListFirst;
while (ptr != NULL) {
if (id->manufacturerId == ptr->header->manufacturerId
&& id->imageTypeId == ptr->header->imageTypeId
&& id->firmwareVersion == ptr->header->firmwareVersion) {
return ptr;
}
ptr = (OtaImage*)ptr->next;
}
return NULL;
}
static uint8_t* writeHeaderDataToBuffer(EmberAfOtaBootloadFileHeaderFieldIndex_t fieldIndex,
uint16_t headerVersion,
uint8_t* bufferPtr)
uint16_t headerVersion,
uint8_t* bufferPtr)
{
const EmberAfOtaHeaderFieldDefinition *definition = emGetOtaHeaderFieldDefinition(headerVersion, fieldIndex);
if (definition->maskForOptionalField != ALWAYS_PRESENT_MASK) {
uint16_t fieldControl = *(uint16_t*)(otaHeaderFieldLocations[FIELD_CONTROL_INDEX].location);
if (!(fieldControl & definition->maskForOptionalField)) {
debug(config.fieldDebug, "Skipping field %s\n", definition->name);
// No more processing.
return bufferPtr;
}
}
debug(config.fieldDebug,
"Writing field %s, type %d, length %d\n",
definition->name,
definition->type,
definition->length);
if (definition->type == BYTE_ARRAY_FIELD
|| definition->type == STRING_FIELD) {
memcpy(bufferPtr, otaHeaderFieldLocations[fieldIndex].location, definition->length);
} else if (definition->type == INTEGER_FIELD) {
if (definition->length == 1) {
uint8_t *value = otaHeaderFieldLocations[fieldIndex].location;
bufferPtr[0] = *value;
} else if (definition->length == 2) {
uint16_t *value = otaHeaderFieldLocations[fieldIndex].location;
bufferPtr[0] = (uint8_t)(*value);
bufferPtr[1] = (uint8_t)(*value >> 8);
} else if (definition->length == 4) {
uint32_t *value = otaHeaderFieldLocations[fieldIndex].location;
bufferPtr[0] = (uint8_t)(*value);
bufferPtr[1] = (uint8_t)(*value >> 8);
bufferPtr[2] = (uint8_t)(*value >> 16);
bufferPtr[3] = (uint8_t)(*value >> 24);
} else {
assert(0);
}
}
return (bufferPtr + definition->length);
const EmberAfOtaHeaderFieldDefinition *definition = emGetOtaHeaderFieldDefinition(headerVersion, fieldIndex);
if (definition->maskForOptionalField != ALWAYS_PRESENT_MASK) {
uint16_t fieldControl = *(uint16_t*)(otaHeaderFieldLocations[FIELD_CONTROL_INDEX].location);
if (!(fieldControl & definition->maskForOptionalField)) {
debug(config.fieldDebug, "Skipping field %s\n", definition->name);
// No more processing.
return bufferPtr;
}
}
debug(config.fieldDebug,
"Writing field %s, type %d, length %d\n",
definition->name,
definition->type,
definition->length);
if (definition->type == BYTE_ARRAY_FIELD
|| definition->type == STRING_FIELD) {
memcpy(bufferPtr, otaHeaderFieldLocations[fieldIndex].location, definition->length);
} else if (definition->type == INTEGER_FIELD) {
if (definition->length == 1) {
uint8_t *value = otaHeaderFieldLocations[fieldIndex].location;
bufferPtr[0] = *value;
} else if (definition->length == 2) {
uint16_t *value = otaHeaderFieldLocations[fieldIndex].location;
bufferPtr[0] = (uint8_t)(*value);
bufferPtr[1] = (uint8_t)(*value >> 8);
} else if (definition->length == 4) {
uint32_t *value = otaHeaderFieldLocations[fieldIndex].location;
bufferPtr[0] = (uint8_t)(*value);
bufferPtr[1] = (uint8_t)(*value >> 8);
bufferPtr[2] = (uint8_t)(*value >> 16);
bufferPtr[3] = (uint8_t)(*value >> 24);
} else {
assert(0);
}
}
return (bufferPtr + definition->length);
}
static uint16_t calculateOtaFileHeaderLength(EmberAfOtaHeader* header)
{
uint16_t length = 4; // the size of the magic number
int fieldIndex = HEADER_VERSION_INDEX;
while (fieldIndex < FIELD_INDEX_MAX) {
const EmberAfOtaHeaderFieldDefinition *definition = emGetOtaHeaderFieldDefinition(header->headerVersion, fieldIndex);
if (definition->maskForOptionalField == ALWAYS_PRESENT_MASK
|| (header->fieldControl & definition->maskForOptionalField)) {
length += definition->length;
}
fieldIndex++;
}
return length;
uint16_t length = 4; // the size of the magic number
int fieldIndex = HEADER_VERSION_INDEX;
while (fieldIndex < FIELD_INDEX_MAX) {
const EmberAfOtaHeaderFieldDefinition *definition = emGetOtaHeaderFieldDefinition(header->headerVersion, fieldIndex);
if (definition->maskForOptionalField == ALWAYS_PRESENT_MASK
|| (header->fieldControl & definition->maskForOptionalField)) {
length += definition->length;
}
fieldIndex++;
}
return length;
}
static OtaImage* imageSearchInternal(const EmberAfOtaImageId* id)
{
OtaImage* ptr = imageListFirst;
OtaImage* newest = NULL;
while (ptr != NULL) {
/*
note("imageSearchInternal: Considering file '%s' (MFG: 0x%04X, Image: 0x%04X)\n",
ptr->filenameStart,
ptr->header->manufacturerId,
ptr->header->imageTypeId);
*/
if (ptr->header->manufacturerId == id->manufacturerId
&& (ptr->header->imageTypeId == id->imageTypeId)) {
if ((id->firmwareVersion == INVALID_FIRMWARE_VERSION)
|| (ptr->header->firmwareVersion == id->firmwareVersion)) {
if (headerHasUpgradeFileDest(ptr->header)) {
if ((ptr->header->headerVersion == OTA_HEADER_VERSION_ZIGBEE)
&& (doEui64sMatch(id->deviceSpecificFileEui64,
&ptr->header->upgradeFileDestination))) {
// Because there is an exact match on the EUI64, we know
// this is the only one that can match the request and can
// return that now.
return ptr;
} else {
assert(0); // TODO: We need to handle Thread OTA headers
}
} else {
if (doEui64sMatch(emberAfInvalidImageId.deviceSpecificFileEui64,
id->deviceSpecificFileEui64)) {
// Save this entry. There is no upgrade file dest in either the
// search criteria or the file. This match may or may not be
// the latest version number.
newest = ptr;
}
}
}
}
ptr = (OtaImage*)ptr->next;
}
return newest;
OtaImage* ptr = imageListFirst;
OtaImage* newest = NULL;
while (ptr != NULL) {
/*
note("imageSearchInternal: Considering file '%s' (MFG: 0x%04X, Image: 0x%04X)\n",
ptr->filenameStart,
ptr->header->manufacturerId,
ptr->header->imageTypeId);
*/
if (ptr->header->manufacturerId == id->manufacturerId
&& (ptr->header->imageTypeId == id->imageTypeId)) {
if ((id->firmwareVersion == INVALID_FIRMWARE_VERSION)
|| (ptr->header->firmwareVersion == id->firmwareVersion)) {
if (headerHasUpgradeFileDest(ptr->header)) {
if ((ptr->header->headerVersion == OTA_HEADER_VERSION_ZIGBEE)
&& (doEui64sMatch(id->deviceSpecificFileEui64,
&ptr->header->upgradeFileDestination))) {
// Because there is an exact match on the EUI64, we know
// this is the only one that can match the request and can
// return that now.
return ptr;
} else {
assert(0); // TODO: We need to handle Thread OTA headers
}
} else {
if (doEui64sMatch(emberAfInvalidImageId.deviceSpecificFileEui64,
id->deviceSpecificFileEui64)) {
// Save this entry. There is no upgrade file dest in either the
// search criteria or the file. This match may or may not be
// the latest version number.
newest = ptr;
}
}
}
}
ptr = (OtaImage*)ptr->next;
}
return newest;
}
static EmberAfOtaImageId getIteratorImageId(void)
{
if (iterator == NULL) {
return emberAfInvalidImageId;
}
return emAfOtaStorageGetImageIdFromHeader(iterator->header);
if (iterator == NULL) {
return emberAfInvalidImageId;
}
return emAfOtaStorageGetImageIdFromHeader(iterator->header);
}
uint32_t emAfOtaStorageGetSlot(void)
{
return INVALID_SLOT;
return INVALID_SLOT;
}
//------------------------------------------------------------------------------
......@@ -1495,65 +1487,65 @@ uint32_t emAfOtaStorageGetSlot(void)
static void* myMalloc(size_t size, const char* allocName)
{
void* returnValue = malloc(size);
if (returnValue != NULL) {
allocations++;
debug(config.memoryDebug,
"[myMalloc] %s, %d bytes (0x%08X)\n",
allocName, size, returnValue);
}
return returnValue;
void* returnValue = malloc(size);
if (returnValue != NULL) {
allocations++;
debug(config.memoryDebug,
"[myMalloc] %s, %d bytes (0x%08X)\n",
allocName, size, returnValue);
}
return returnValue;
}
static void myFree(void* ptr)
{
debug(config.memoryDebug, "[myFree] 0x%08X\n", ptr);
free(ptr);
allocations--;
debug(config.memoryDebug, "[myFree] 0x%08X\n", ptr);
free(ptr);
allocations--;
}
//------------------------------------------------------------------------------
// Print routines.
static void message(FILE* stream,
bool error,
const char* formatString,
va_list ap)
bool error,
const char* formatString,
va_list ap)
{
if (messagePrefix) {
fprintf(stream, "[%s] ", messagePrefix);
}
if (error) {
fprintf(stream, "Error: ");
}
vfprintf(stream, formatString, ap);
fflush(stream);
if (messagePrefix) {
fprintf(stream, "[%s] ", messagePrefix);
}
if (error) {
fprintf(stream, "Error: ");
}
vfprintf(stream, formatString, ap);
fflush(stream);
}
static void note(const char* formatString, ...)
{
va_list ap = { 0 };
va_start(ap, formatString);
message(stdout, false, formatString, ap);
va_end(ap);
va_list ap = { 0 };
va_start(ap, formatString);
message(stdout, false, formatString, ap);
va_end(ap);
}
static void debug(bool debugOn, const char* formatString, ...)
{
if (debugOn) {
va_list ap = { 0 };
va_start(ap, formatString);
message(stdout, false, formatString, ap);
va_end(ap);
}
if (debugOn) {
va_list ap = { 0 };
va_start(ap, formatString);
message(stdout, false, formatString, ap);
va_end(ap);
}
}
static void error(const char* formatString, ...)
{
va_list ap = { 0 };
va_start(ap, formatString);
message(stderr, true, formatString, ap);
va_end(ap);
va_list ap = { 0 };
va_start(ap, formatString);
message(stderr, true, formatString, ap);
va_end(ap);
}
#endif // defined(GATEWAY_APP)
......@@ -3,12 +3,12 @@
typedef void (EmAfOtaStorageFileAddedHandler)(const EmberAfOtaHeader*);
typedef struct {
bool memoryDebug;
bool fileDebug;
bool fieldDebug;
bool ignoreFilesWithUnderscorePrefix;
bool printFileDiscoveryOrRemoval;
EmAfOtaStorageFileAddedHandler* fileAddedHandler;
bool memoryDebug;
bool fileDebug;
bool fieldDebug;
bool ignoreFilesWithUnderscorePrefix;
bool printFileDiscoveryOrRemoval;
EmAfOtaStorageFileAddedHandler* fileAddedHandler;
} EmAfOtaStorageLinuxConfig;
void emAfOtaStorageGetConfig(EmAfOtaStorageLinuxConfig* currentConfig);
......
......@@ -77,6 +77,6 @@ bool emberAfIsCurrentSecurityProfileSmartEnergy(void)
|| (emAfCurrentZigbeeProNetwork->securityProfile
== EMBER_AF_SECURITY_PROFILE_SE_FULL)));
#else
return false;
return false;
#endif
}
......@@ -545,16 +545,12 @@ int emberAfMain(MAIN_FUNCTION_PARAMETERS)
halInit();
INTERRUPTS_ON(); // Safe to enable interrupts at this point
{
int returnCode;
if (emberAfMainStartCallback(&returnCode,
APP_FRAMEWORK_MAIN_ARGUMENTS)) { //get serial port info
int returnCode;
if (emberAfMainStartCallback(&returnCode,
APP_FRAMEWORK_MAIN_ARGUMENTS)) { //get serial port info
return returnCode;
}
}
kk_print("*******************123****************\r\n");
kk_print_info("\r\n-----hello world![%s:%s]-----\r\n",__DATE__,__TIME__);
kk_print_version();
}
emberSerialInit(APP_SERIAL, BAUD_RATE, PARITY_NONE, 1); //fock child process
......
......@@ -379,6 +379,6 @@ EmberApsFrame *emberAfGetCommandApsFrame(void)
void emberAfSetCommandEndpoints(uint8_t sourceEndpoint, uint8_t destinationEndpoint)
{
emAfCommandApsFrame->sourceEndpoint = sourceEndpoint;
emAfCommandApsFrame->destinationEndpoint = destinationEndpoint;
emAfCommandApsFrame->sourceEndpoint = sourceEndpoint;
emAfCommandApsFrame->destinationEndpoint = destinationEndpoint;
}
......@@ -65,11 +65,11 @@ bool emAfProcessClusterSpecificCommand(EmberAfClusterCommand *cmd)
}
#endif
#ifdef ZCL_USING_OTA_BOOTLOAD_CLUSTER_SERVER
if (cmd->apsFrame->clusterId == ZCL_OTA_BOOTLOAD_CLUSTER_ID
&& cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER
&& emberAfOtaServerIncomingMessageRawCallback(cmd)) {
return true;
}
if (cmd->apsFrame->clusterId == ZCL_OTA_BOOTLOAD_CLUSTER_ID
&& cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER
&& emberAfOtaServerIncomingMessageRawCallback(cmd)) {
return true;
}
#endif
// Pass the command to the generated command parser for processing
......
......@@ -688,7 +688,7 @@ EmberStatus emberAfSendResponseWithCallback(EmberAfMessageSentFunction callback)
EmberStatus emberAfSendResponse(void)
{
return emberAfSendResponseWithCallback(NULL);
return emberAfSendResponseWithCallback(NULL);
}
EmberStatus emberAfSendImmediateDefaultResponseWithCallback(EmberAfStatus status,
......
......@@ -162,52 +162,52 @@ static bool sendGoAhead = true;
// loop executing ezspTick() and other functionality.
EmberStatus emberSerialInit(uint8_t port,
SerialBaudRate rate,
SerialParity parity,
uint8_t stopBits)
SerialBaudRate rate,
SerialParity parity,
uint8_t stopBits)
{
static bool emberSerialInitCalled = false;
static bool emberSerialInitCalled = false;
debugPrint("emberSerialInit()\n");
if (port > 1) {
return EMBER_SERIAL_INVALID_PORT;
}
debugPrint("emberSerialInit()\n");
if (port > 1) {
return EMBER_SERIAL_INVALID_PORT;
}
if (childPid[port] != INVALID_PID) {
debugPrint("Serial port %d already initialized.\n", port);
return EMBER_SUCCESS;
}
if (childPid[port] != INVALID_PID) {
debugPrint("Serial port %d already initialized.\n", port);
return EMBER_SUCCESS;
}
// Without the backchannel, there is only one serial port available (STDIN).
if (!backchannelEnable
&& emberSerialInitCalled) {
return EMBER_SERIAL_INVALID_PORT;
}
// Without the backchannel, there is only one serial port available (STDIN).
if (!backchannelEnable
&& emberSerialInitCalled) {
return EMBER_SERIAL_INVALID_PORT;
}
if (backchannelEnable) {
// For the CLI, wait here until a new client connects for the first time.
BackchannelState state =
backchannelCheckConnection(port,
(port // waitForConnection?
== SERIAL_PORT_CLI));
if (port == SERIAL_PORT_CLI && state != NEW_CONNECTION) {
debugPrint("Failed to get new backchannel connection.\n");
return EMBER_ERR_FATAL;
} else if ( !(state == NEW_CONNECTION || state == CONNECTION_EXISTS) ) {
// We will defer initializing the RAW serial port (spawning the child
// and that jazz) until we actually have a new client connection.
return EMBER_SUCCESS;
}
}
if (backchannelEnable) {
// For the CLI, wait here until a new client connects for the first time.
BackchannelState state =
backchannelCheckConnection(port,
(port // waitForConnection?
== SERIAL_PORT_CLI));
if (port == SERIAL_PORT_CLI && state != NEW_CONNECTION) {
debugPrint("Failed to get new backchannel connection.\n");
return EMBER_ERR_FATAL;
} else if ( !(state == NEW_CONNECTION || state == CONNECTION_EXISTS) ) {
// We will defer initializing the RAW serial port (spawning the child
// and that jazz) until we actually have a new client connection.
return EMBER_SUCCESS;
}
}
EmberStatus status = serialInitInternal(port);
EmberStatus status = serialInitInternal(port);
if (status == EMBER_SUCCESS) {
installSignalHandler();
emberSerialInitCalled = true;
}
if (status == EMBER_SUCCESS) {
installSignalHandler();
emberSerialInitCalled = true;
}
return status;
return status;
}
static EmberStatus serialInitInternal(uint8_t port)
......@@ -263,7 +263,7 @@ static EmberStatus serialInitInternal(uint8_t port)
// block indefinitely waiting for debugger to attach
sleep(1);
}
childRun(port);
childRun(port);
return EMBER_ERR_FATAL; // should never get here
} else if (pid == -1) {
fprintf(stderr, "FATAL: Could not fork! (%d): %s\n",
......@@ -311,13 +311,13 @@ static bool handleRemoteConnection(uint8_t port)
static void handleBackchannelConnection(uint8_t port)
{
// BugzId:12928 Close out sockets used by other children but not this one
int i;
for (i = 0; i < NUM_PORTS; i++) {
if (i != port) {
backchannelCloseConnection(i);
}
}
// BugzId:12928 Close out sockets used by other children but not this one
int i;
for (i = 0; i < NUM_PORTS; i++) {
if (i != port) {
backchannelCloseConnection(i);
}
}
if (EMBER_SUCCESS
== backchannelMapStandardInputOutputToRemoteConnection(port)) {
......@@ -333,10 +333,10 @@ static void handleBackchannelConnection(uint8_t port)
static void childRun(uint8_t port)
{
childProcessPort = port;
childProcessPort = port;
close(DATA_READER(port));
close(CONTROL_WRITER(port));
close(DATA_READER(port));
close(CONTROL_WRITER(port));
if (backchannelEnable) {
handleBackchannelConnection(port);
......@@ -364,23 +364,23 @@ static void childRun(uint8_t port)
installSignalHandler();
processSerialInput(port);
// Normally the above function NEVER returns
// If we get here it is an error.
assert(0);
// Normally the above function NEVER returns
// If we get here it is an error.
assert(0);
}
void emberSerialSetPrompt(const char* thePrompt)
{
if (thePrompt == NULL) {
promptSet = false;
return;
}
if (thePrompt == NULL) {
promptSet = false;
return;
}
// Substract one for the '>'
snprintf(prompt,
MAX_PROMPT_LENGTH - 1,
"%s>",
thePrompt);
// Substract one for the '>'
snprintf(prompt,
MAX_PROMPT_LENGTH - 1,
"%s>",
thePrompt);
}
static void setNonBlockingFD(int fd)
......@@ -449,18 +449,18 @@ static void parentCleanupAfterChildDied(void)
static void childCleanupAndExit(void)
{
if (childProcessPort == SERIAL_PORT_CLI) {
writeHistory();
}
if (childProcessPort == SERIAL_PORT_CLI) {
writeHistory();
}
if (childProcessPort != -1) {
if (backchannelEnable) {
backchannelStopServer(childProcessPort);
}
close(DATA_WRITER(childProcessPort));
close(CONTROL_READER(childProcessPort));
close(DATA_WRITER(childProcessPort));
close(CONTROL_READER(childProcessPort));
}
exit(0);
exit(0);
}
// This works only for the command interpreter.
......@@ -468,11 +468,11 @@ static void childCleanupAndExit(void)
void emberSerialCommandCompletionInit(EmberCommandEntry listOfCommands[])
{
#if READLINE_SUPPORT
allCommands = listOfCommands;
allCommands = listOfCommands;
rl_attempted_completion_function = commandCompletion;
rl_completion_entry_function = filenameCompletion;
#endif
usingCommandInterpreter = true;
usingCommandInterpreter = true;
}
// This is for QA's cli
......@@ -708,9 +708,9 @@ static void processSerialInput(uint8_t port)
char goAhead;
char singleByte[2];
if (port == SERIAL_PORT_CLI) {
initializeHistory();
}
if (port == SERIAL_PORT_CLI) {
initializeHistory();
}
debugPrint("Processing input for port %d.\n", port);
......@@ -932,18 +932,18 @@ static const char* commandArgumentPtr = NULL;
static const char* findNextNonSpaceChar(const char* line)
{
while (*line != '\0' && *line == ' ') {
line++;
}
return line;
while (*line != '\0' && *line == ' ') {
line++;
}
return line;
}
static const char* findNextSpaceChar(const char* line)
{
while (*line != '\0' && *line != ' ') {
line++;
}
return line;
while (*line != '\0' && *line != ' ') {
line++;
}
return line;
}
// Recursive function. Traverses the command-tree
......@@ -1147,57 +1147,57 @@ static char* duplicateString(const char* source)
static void initializeHistory(void)
{
int myErrno;
char* homeDirectory;
int myErrno;
char* homeDirectory;
readlineHistoryPath[0] = '\0';
readlineHistoryPath[0] = '\0';
using_history(); // initialize readline() history.
using_history(); // initialize readline() history.
homeDirectory = getenv("HOME");
if (homeDirectory == NULL) {
debugPrint("Error: HOME directory env variable is not defined.\n");
return;
} else {
debugPrint("Home Directory: %s\n", homeDirectory);
}
homeDirectory = getenv("HOME");
if (homeDirectory == NULL) {
debugPrint("Error: HOME directory env variable is not defined.\n");
return;
} else {
debugPrint("Home Directory: %s\n", homeDirectory);
}
snprintf(readlineHistoryPath,
MAX_STRING_LENGTH - 1,
"%s/%s",
homeDirectory,
readlineHistoryFilename);
readlineHistoryPath[MAX_STRING_LENGTH - 1] = '\0';
myErrno = read_history(readlineHistoryPath);
if (myErrno != 0) {
debugPrint("Could not open history file '%s': %s\n",
readlineHistoryPath,
strerror(myErrno));
} else {
debugPrint("%d history entries read from file '%s'\n",
history_length, // readline global
readlineHistoryPath);
}
snprintf(readlineHistoryPath,
MAX_STRING_LENGTH - 1,
"%s/%s",
homeDirectory,
readlineHistoryFilename);
readlineHistoryPath[MAX_STRING_LENGTH - 1] = '\0';
myErrno = read_history(readlineHistoryPath);
if (myErrno != 0) {
debugPrint("Could not open history file '%s': %s\n",
readlineHistoryPath,
strerror(myErrno));
} else {
debugPrint("%d history entries read from file '%s'\n",
history_length, // readline global
readlineHistoryPath);
}
}
static void writeHistory(void)
{
int myErrno;
if (readlineHistoryPath[0] == '\0') {
debugPrint("Readline history path is empty, not writing history.\n");
return;
}
myErrno = write_history(readlineHistoryPath);
if (myErrno != 0) {
debugPrint("Failed to write history file '%s': %s\n",
readlineHistoryPath,
strerror(myErrno));
} else {
debugPrint("Wrote %d entries to history file '%s'\n",
history_length, // readline global
readlineHistoryPath);
}
int myErrno;
if (readlineHistoryPath[0] == '\0') {
debugPrint("Readline history path is empty, not writing history.\n");
return;
}
myErrno = write_history(readlineHistoryPath);
if (myErrno != 0) {
debugPrint("Failed to write history file '%s': %s\n",
readlineHistoryPath,
strerror(myErrno));
} else {
debugPrint("Wrote %d entries to history file '%s'\n",
history_length, // readline global
readlineHistoryPath);
}
}
#endif // #if READLINE_SUPPORT
......@@ -1227,20 +1227,20 @@ static void writeHistory(void)
*/
static void signalHandler(int signal)
{
static bool reportSigttin = true;
const char* signalName = strsignal(signal);
static bool reportSigttin = true;
const char* signalName = strsignal(signal);
if (signal == SIGTTOU) {
debugOn = false;
return;
}
if (signal == SIGTTOU) {
debugOn = false;
return;
}
if (signal != SIGTTIN || reportSigttin) {
debugPrint("%s caught signal %s (%d)\n",
(amParent ? "Parent" : "Child"),
(signalName == NULL ? "???" : signalName),
signal);
}
if (signal != SIGTTIN || reportSigttin) {
debugPrint("%s caught signal %s (%d)\n",
(amParent ? "Parent" : "Child"),
(signalName == NULL ? "???" : signalName),
signal);
}
if (signal == SIGPIPE) {
// Ignore this.
......@@ -1253,11 +1253,11 @@ static void signalHandler(int signal)
return;
}
if (signal == SIGTTIN) {
reportSigttin = false;
if (signal == SIGTTIN) {
reportSigttin = false;
return;
}
return;
}
/*
if (signal == SIGCHLD) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment