6.7.4. Implementation

Note

Before approaching this tutorial, it is recommended to get familiar with the Basic implementation chapter. Both examples are very similar, except for the additional elements resulting from the Advanced Firmware Update specification. In this document we will not focus on those elements that are also present in basic Firmware Udpates.

6.7.4.1. Project structure

examples/tutorial/firmware-update/advanced-firmware-update/
├── CMakeLists.txt
└── src
    ├── advanced_firmware_update.c
    ├── advanced_firmware_update.h
    ├── main.c
    ├── time_object.c
    └── time_object.h

6.7.4.2. Advanced Firmware Update API

In order to install the module, we are going to use:

int anjay_advanced_fw_update_install(
        anjay_t *anjay, const anjay_advanced_fw_update_global_config_t *config);

With this call we are passing a config that will affect all instances of the Advanced Firmware Update object.

To add an instance of the Advanced Firmware Update object, we are going to use:

int anjay_advanced_fw_update_instance_add(
        anjay_t *anjay,
        anjay_iid_t iid,
        const char *component_name,
        const anjay_advanced_fw_update_handlers_t *handlers,
        void *user_arg,
        const anjay_advanced_fw_update_initial_state_t *initial_state);

The anjay, handlers, user_arg and initial_state arguments are similar to their equivalents in anjay_fw_update_install function (see Installing the Firmware Update module). There are two major differences between anjay_advanced_fw_update_handlers_t and anjay_fw_update_handlers_t:

  • each callback has an additional iid argument, which corresponds to the object instance number,

  • new callback get_current_version that return the version of current firmware package.

Remember that each instance must have a unique iid number. The component_name holds value of the /33629/x/14 resource. The string is NOT copied, so it needs to remain valid for the lifetime of the object instance.

6.7.4.3. Implementing handlers and installation routine

Compared to a regular Firmware Updates, we need to store more information in the global structure. For each AFU instance, we define: a file handle, an array storing the firmware version (for the /33629/x/15 resource) and an array storing the instance name (component_name argument in anjay_advanced_fw_update_instance_add).

#include "advanced_firmware_update.h"

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

#define AFU_VERSION_STR_MAX_LEN 10
#define AFU_INSTANCE_NAME_STR_MAX_LEN 10
#define AFU_FILE_NAME_STR_MAX_LEN 50

typedef struct {
    anjay_t *anjay;
    char fw_version[AFU_NUMBER_OF_FIRMWARE_INSTANCES]
                   [AFU_VERSION_STR_MAX_LEN + 1];
    char instance_name[AFU_NUMBER_OF_FIRMWARE_INSTANCES]
                      [AFU_INSTANCE_NAME_STR_MAX_LEN + 1];
    FILE *new_firmware_file[AFU_NUMBER_OF_FIRMWARE_INSTANCES];
} advanced_firmware_update_logic_t;

static advanced_firmware_update_logic_t afu_logic;

Number of the firmware instances is defined by AFU_NUMBER_OF_FIRMWARE_INSTANCES. Default instance (AFU_DEFAULT_FIRMWARE_INSTANCE_IID) is the built image of this software. The other instances correspond to the files created by the afu_update_install function for the purpose of this tutorial (those are equivalent of such software images as bootloader image, modem image etc. used in embedded systems).

#ifndef ADVANCED_FIRMWARE_UPDATE_H
#define ADVANCED_FIRMWARE_UPDATE_H

#include <anjay/advanced_fw_update.h>
#include <anjay/anjay.h>

#include <avsystem/commons/avs_log.h>

#define AFU_DEFAULT_FIRMWARE_VERSION "1.0.0"
#define AFU_ADD_FILE_DEFAULT_CONTENT "1.1.1"

#define AFU_DEFAULT_FIRMWARE_INSTANCE_IID 0
#define AFU_NUMBER_OF_FIRMWARE_INSTANCES 3

/**
* Buffer for the endpoint name that will be used when re-launching the client
* after firmware upgrade.
*/
extern const char *ENDPOINT_NAME;

/**
* Installs the advanced firmware update module.
*
* @returns 0 on success, negative value otherwise.
*/
int afu_update_install(anjay_t *anjay);

#endif // ADVANCED_FIRMWARE_UPDATE_H

The implementation of fw_stream_open and fw_update_common_write is quite simple. For each iid we open and write to a separate file.

static void get_firmware_download_name(int iid, char *buff) {
    if (iid == AFU_DEFAULT_FIRMWARE_INSTANCE_IID) {
        snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/firmware_image.bin");
    } else {
        snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/add_image_%d", iid);
    }
}
static int fw_stream_open(anjay_iid_t iid, void *user_ptr) {
    (void) user_ptr;

    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
    get_firmware_download_name(iid, file_name);

    if (afu_logic.new_firmware_file[iid]) {
        avs_log(advance_fu, ERROR, "Already open %s", file_name);
        return -1;
    }
    afu_logic.new_firmware_file[iid] = fopen(file_name, "wb");
    if (!afu_logic.new_firmware_file[iid]) {
        avs_log(advance_fu, ERROR, "Could not open %s", file_name);
        return -1;
    }
    return 0;
}

static int fw_update_common_write(anjay_iid_t iid,
                                 void *user_ptr,
                                 const void *data,
                                 size_t length) {
    (void) user_ptr;

    if (!afu_logic.new_firmware_file[iid]) {
        avs_log(advance_fu, ERROR, "Stream not open: object %d", iid);
        return -1;
    }
    if (length
            && (fwrite(data, length, 1, afu_logic.new_firmware_file[iid]) != 1
                || fflush(afu_logic.new_firmware_file[iid]) != 0)) {
        avs_log(advance_fu, ERROR, "fwrite or fflush failed: %s",
                strerror(errno));
        return ANJAY_ADVANCED_FW_UPDATE_ERR_NOT_ENOUGH_SPACE;
    }
    return 0;
}

In fw_update_common_finish after closing the stream, we check the other instances, if any is in DOWNLOADED state then we link them with each other. This is not a requirement, the implementation is free do decide which instances are linked and which are not. With anjay_advanced_fw_update_set_linked_instances we set the Linked Instances (/33629/x/15) resource to inform the server that the upgrade will be performed simultaneously on all linked instances.

static void add_linked_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
    const anjay_iid_t *linked_instances;
    anjay_iid_t linked_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
    size_t linked_iids_count = 0;

    // get linked instances
    anjay_advanced_fw_update_get_linked_instances(
            afu_logic.anjay, iid, &linked_instances, &linked_iids_count);
    // add target_iid to the list
    for (size_t i = 0; i < linked_iids_count; i++) {
        linked_target_iids[i] = linked_instances[i];
    }
    linked_target_iids[linked_iids_count++] = target_iid;
    anjay_advanced_fw_update_set_linked_instances(
            afu_logic.anjay, iid, linked_target_iids, linked_iids_count);
}

static int fw_update_common_finish(anjay_iid_t iid, void *user_ptr) {
    (void) user_ptr;

    if (!afu_logic.new_firmware_file[iid]) {
        avs_log(advance_fu, ERROR, "Stream not open: object %d", iid);
        return -1;
    }

    if (fclose(afu_logic.new_firmware_file[iid])) {
        avs_log(advance_fu, ERROR, "Closing firmware image failed: object %d",
                iid);
        afu_logic.new_firmware_file[iid] = NULL;
        return -1;
    }
    afu_logic.new_firmware_file[iid] = NULL;

    /*
    If other firmware instances are in downloaded state set linked instances,
    based on them, the upgrade will be performed simultaneously in the
    perform_upgrade callback. The reason for setting linked instances may be
    different and depends on the user's implementation, but always mean
    that instances will be updated in a batch if the Update resource is executed
    with no arguments.
    */
    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        if (i != iid) {
            anjay_advanced_fw_update_state_t state;
            anjay_advanced_fw_update_get_state(afu_logic.anjay, i, &state);
            if (state == ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED) {
                add_linked_instance(iid, i);
                add_linked_instance(i, iid);
            }
        }
    }

    return 0;
}
Three new paramteters are passed with fw_update_common_perform_upgrade function:
  • iid,

  • requested_supplemental_iids,

  • requested_supplemental_iids_count.

The iid points to the instance on which Update (/33629/x/2) was called. The requested_supplemental_iids can contain a list of instances for simultaneous upgrade, passed as an argument in the Update (/33629/x/2) command sent by the server. If requested_supplemental_iids is present (different than NULL), specification forces us to upgrade according to it. If this is not possible then we need to return a corresponding error.

First, we check which instances are to be updated. To do this, we create update_iid array and for each instance we set it to true if the conditions for upgrade are met. Conditions are checked inside is_update_requested function. In our example if requested_supplemental_iids == NULL, we use linked_target_iids instead. Then we retrieve the state of each instance that is involved in the upgrade, if it is other than DOWNLOADED then we return a CONFLICTING_STATE error. Before leaving the function, we set conflicting instances to tell the server which instance is causing the problem (add_conflicting_instance function which calls anjay_advanced_fw_update_set_conflicting_instances).

In next step we check version compatibility. For the purposes of this tutorial, we have assumed that the first character in each additional file must have the same value. If we are updating only some of the files, their new versions must have the same value as the old ones. In the case of replacing all files, each new file must match. If a mismatch is detected, an error CONFLICTING_STATE is returned and the add_conflicting_instance function is called, so that the server gets information about the error and the instance that caused it. After that we update the state of each instance from DOWNLOADED to UPDATING.

Note

During upgrade you must remember to change the state of each instance. Anjay will only modify the state of the iid instance. In a typical scenario, the state of each instance must be changed first from DOWNLOADED to UPDATING and then, if reboot does not occur, to SUCCESS.

The last step is to replace the firmware. We start with additional images, if update_iid[i] == true then we use the move_file function to swap files, if the main image does not change then we create a “marker” file for each instance (logic carried over from Basic implementation) and change its state from UPDATING to SUCCESS. If the main image is to be replaced then at this point we create the corresponding “marker” file and start a new application. Otherwise, we call refresh_fw_version to update the instance’s firmware versions and we remove all information about all linked and conflicting instances.

static void get_add_firmware_file_name(int iid, char *buff) {
    snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "ADD_FILE_%d", iid);
}

static void get_marker_file_name(int iid, char *buff) {
    snprintf(buff, AFU_FILE_NAME_STR_MAX_LEN, "/tmp/fw-updated-marker_%d", iid);
}

static int move_file(const char *dest, const char *source) {
    int ret_val = -1;
    FILE *dest_stream = NULL;
    FILE *source_stream = fopen(source, "r");

    if (!source_stream) {
        avs_log(advance_fu, ERROR, "Could not open file: %s", source);
        goto cleanup;
    }
    dest_stream = fopen(dest, "w");
    if (!dest_stream) {
        avs_log(advance_fu, ERROR, "Could not open file: %s", dest);
        fclose(source_stream);
        goto cleanup;
    }

    while (!feof(source_stream)) {
        char buff[1024];
        size_t bytes_read_1 = fread(buff, 1, sizeof(buff), source_stream);
        if (fwrite(buff, 1, bytes_read_1, dest_stream) != bytes_read_1) {
            avs_log(advance_fu, ERROR, "Error during write to file: %s", dest);
            goto cleanup;
        }
    }
    ret_val = 0;

cleanup:
    if (dest_stream) {
        if (fclose(dest_stream)) {
            avs_log(advance_fu, ERROR, "Could not close file: %s", dest);
            ret_val = -1;
        }
    }
    if (source_stream) {
        if (fclose(source_stream)) {
            avs_log(advance_fu, ERROR, "Could not close file: %s", source);
            ret_val = -1;
        }
    }
    unlink(source);

    return ret_val;
}

static void add_conflicting_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
    const anjay_iid_t *conflicting_instances;
    anjay_iid_t conflicting_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
    size_t conflicting_iids_count = 0;

    // get conflicting instances
    anjay_advanced_fw_update_get_conflicting_instances(afu_logic.anjay, iid,
                                                       &conflicting_instances,
                                                       &conflicting_iids_count);
    // add target_iid to the list
    for (size_t i = 0; i < conflicting_iids_count; i++) {
        conflicting_target_iids[i] = conflicting_instances[i];
    }
    conflicting_target_iids[conflicting_iids_count++] = target_iid;
    anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay, iid,
                                                       conflicting_target_iids,
                                                       conflicting_iids_count);
}

static bool is_update_requested(anjay_iid_t iid,
                                anjay_iid_t target_iid,
                                const anjay_iid_t *requested_supplemental_iids,
                                size_t requested_supplemental_iids_count,
                                const anjay_iid_t *linked_target_iids,
                                size_t linked_iids_count) {
    if (iid == target_iid) {
        return true;
    }
    if (requested_supplemental_iids) {
        for (size_t i = 0; i < requested_supplemental_iids_count; i++) {
            if (iid == requested_supplemental_iids[i]) {
                return true;
            }
        }
    } else if (linked_target_iids) {
        for (size_t i = 0; i < linked_iids_count; i++) {
            if (iid == linked_target_iids[i]) {
                return true;
            }
        }
    }
    return false;
}

static char get_firmware_major_version(anjay_iid_t iid, bool is_upgrade) {
    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };

    if (is_upgrade == false) {
        return afu_logic.fw_version[iid][0];
    }

    get_firmware_download_name(iid, file_name);

    // get value from new file
    char buff;
    FILE *stream = fopen(file_name, "r");
    if (!stream) {
        avs_log(advance_fu, ERROR, "Could not open file: %s", file_name);
        return ' ';
    }
    if (!fread(&buff, 1, 1, stream)) {
        avs_log(advance_fu, ERROR, "Could not read from file file: %s",
                file_name);
        fclose(stream);
        return ' ';
    }
    if (fclose(stream)) {
        avs_log(advance_fu, ERROR, "Could not close file: %s", file_name);
    }

    return buff;
}

static int refresh_fw_version() {
    memcpy(afu_logic.fw_version[AFU_DEFAULT_FIRMWARE_INSTANCE_IID],
        AFU_DEFAULT_FIRMWARE_VERSION, strlen(AFU_DEFAULT_FIRMWARE_VERSION));

    for (int i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        char buff[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
        get_add_firmware_file_name(i, buff);
        FILE *stream = fopen(buff, "r");
        if (!stream) {
            avs_log(advance_fu, ERROR, "Could not open file with iid: %d", i);
            return -1;
        }
        if (!fread(afu_logic.fw_version[i], 1, AFU_VERSION_STR_MAX_LEN,
                stream)) {
            avs_log(advance_fu, ERROR, "Could not read file with iid: %d", i);
            fclose(stream);
            return -1;
        }
        if (fclose(stream)) {
            avs_log(advance_fu, ERROR, "Could not close file with iid: %d", i);
            return -1;
        }
    }

    for (int i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        avs_log(advance_fu, INFO,
                "Firmware version for object with IID %d is: %s", i,
                afu_logic.fw_version[i]);
    }

    return 0;
}
static int
fw_update_common_perform_upgrade(anjay_iid_t iid,
                                 void *user_ptr,
                                 const anjay_iid_t *requested_supplemental_iids,
                                 size_t requested_supplemental_iids_count) {
    (void) user_ptr;

    const anjay_iid_t *linked_target_iids;
    bool update_iid[AFU_NUMBER_OF_FIRMWARE_INSTANCES];
    size_t linked_iids_count = 0;

    // get linked instances
    anjay_advanced_fw_update_get_linked_instances(
            afu_logic.anjay, iid, &linked_target_iids, &linked_iids_count);

    /* Prepare list of iid to update. If requested_supplemental_iids is present
    * use it otherwise use linked_target_iids.*/
    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        if (is_update_requested(i, iid, requested_supplemental_iids,
                                requested_supplemental_iids_count,
                                linked_target_iids, linked_iids_count)) {
            update_iid[i] = true;
            // check if new file is already downloaded
            anjay_advanced_fw_update_state_t state;
            anjay_advanced_fw_update_get_state(afu_logic.anjay, i, &state);
            if ((i != iid)
                    && (state != ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED)) {
                avs_log(advance_fu, ERROR,
                        "Upgrade can't be performed, firmware file with iid %d "
                        "is not ready",
                        i);
                // set conflicting instance
                add_conflicting_instance(iid, i);
                return ANJAY_ADVANCED_FW_UPDATE_ERR_CONFLICTING_STATE;
            }
        } else {
            update_iid[i] = false;
        }
    }

    /*
    Check firmware version compatibility.
    In this example major version number is compare - first character in every
    additional image must have the same value. If new file is given (DOWNLOADED
    STATE), get this value from them, otherwise use the old one.
    */
    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        if (update_iid[i] == true) {
            for (anjay_iid_t j = i + 1; j < AFU_NUMBER_OF_FIRMWARE_INSTANCES;
                j++) {
                if (get_firmware_major_version(i, update_iid[i])
                        != get_firmware_major_version(j, update_iid[j])) {
                    avs_log(advance_fu, ERROR,
                            "Upgrade can't be performed, conflicting firmware "
                            "version between instance %d and %d",
                            i, j);
                    // set conflicting instance due to firmware version
                    // incompatibility
                    add_conflicting_instance(i, j);
                    add_conflicting_instance(j, i);
                    return ANJAY_ADVANCED_FW_UPDATE_ERR_CONFLICTING_STATE;
                }
            }
        }
    }

    /* No errors found, change the status of all requested_supplemental_iids or
    * linked_target_iids to UPDATING before the actual update process.*/
    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        if (update_iid[i] == true) {
            if (i != iid) {
                anjay_advanced_fw_update_set_state_and_result(
                        afu_logic.anjay, i,
                        ANJAY_ADVANCED_FW_UPDATE_STATE_UPDATING,
                        ANJAY_ADVANCED_FW_UPDATE_RESULT_INITIAL);
            }
        }
    }

    // after firmware versions check, start firmware update, first with
    // additional images
    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        if (update_iid[i] == true) {
            avs_log(advance_fu, INFO, "Perform update on %d instance", i);

            char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
            char current_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
            get_firmware_download_name(i, new_firm_name);
            get_add_firmware_file_name(i, current_firm_name);

            if (move_file(current_firm_name, new_firm_name)) {
                avs_log(advance_fu, ERROR,
                        "Error during the %d additional image swapping", i);
                return -1;
            }
            // if main application is restarted, set update marker
            if (update_iid[AFU_DEFAULT_FIRMWARE_INSTANCE_IID] == true) {
                char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
                get_marker_file_name(i, marker_name);
                FILE *marker = fopen(marker_name, "w");
                if (!marker) {
                    avs_log(advance_fu, ERROR,
                            "Marker file could not be created");
                    return -1;
                }
                if (fclose(marker)) {
                    avs_log(advance_fu, ERROR,
                            "Marker file could not be close");
                }
            } // if main application is not restarted, update state
            else {
                anjay_advanced_fw_update_set_state_and_result(
                        afu_logic.anjay, i, ANJAY_ADVANCED_FW_UPDATE_STATE_IDLE,
                        ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS);
            }
        }
    }

    // update application
    if (update_iid[AFU_DEFAULT_FIRMWARE_INSTANCE_IID] == true) {
        avs_log(advance_fu, INFO, "Perform update on default instance");

        char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
        char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
        get_firmware_download_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID,
                                new_firm_name);
        get_marker_file_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID, marker_name);

        if (chmod(new_firm_name, 0700) == -1) {
            avs_log(advance_fu, ERROR, "Could not make firmware executable: %s",
                    strerror(errno));
            return -1;
        }
        // Create a marker file, so that the new process knows it is the
        // "upgraded" one
        FILE *marker = fopen(marker_name, "w");
        if (!marker) {
            avs_log(advance_fu, ERROR, "Marker file could not be created");
            return -1;
        }
        if (fclose(marker)) {
            avs_log(advance_fu, ERROR, "Marker file could not be close");
        }

        assert(ENDPOINT_NAME);

        // If the call below succeeds, the firmware is considered as "upgraded",
        // and we hope the newly started client registers to the Server.
        (void) execl(new_firm_name, new_firm_name, ENDPOINT_NAME, NULL);
        avs_log(advance_fu, ERROR, "execl() failed: %s", strerror(errno));
        // If we are here, it means execl() failed. Marker file MUST now be
        // removed, as the firmware update failed.
        unlink(marker_name);
        return -1;
    }

    // update firmware version
    refresh_fw_version();

    // clear conflicting and linked instances in the objects
    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        if (update_iid[i] == true) {
            anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay,
                                                            i, NULL, 0);
            anjay_advanced_fw_update_set_linked_instances(afu_logic.anjay, i,
                                                        NULL, 0);
            // clear conflicting and linked instances about this object from
            // other objects
            for (anjay_iid_t j = 0; j < AFU_NUMBER_OF_FIRMWARE_INSTANCES; j++) {
                if (i != j) {
                    remove_linked_instance(i, j);
                    remove_conflicting_instance(i, j);
                }
            }
        }
    }

    return 0;
}

At the very end we will look at the implementation of fw_update_common_reset. Note that this function will be called not only in case of failure but also after a successful update if there is no reboot. In addition to removing the downloaded files, we clean linked and conflicting resources from the instance and using the remove_conflicting_instance and remove_linked_instance functions, we remove the information about given instance from the other instances. Please note that as in the case of fw_update_common_perform_upgrade we do not check the state of each instance, perhaps in your implementation before clearing the linked and conflicting lists, you will need to include logic to check the status of the update.

static void remove_linked_instance(anjay_iid_t iid, anjay_iid_t target_iid) {
    const anjay_iid_t *linked_instances;
    anjay_iid_t linked_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
    size_t linked_iids_count = 0;
    size_t new_linked_iids_count = 0;

    // get linked instances
    anjay_advanced_fw_update_get_linked_instances(
            afu_logic.anjay, iid, &linked_instances, &linked_iids_count);
    // remove target_iid from the list
    for (size_t i = 0; i < linked_iids_count; i++) {
        if (linked_instances[i] != target_iid) {
            linked_target_iids[new_linked_iids_count++] = linked_instances[i];
        }
    }
    // update linked list
    anjay_advanced_fw_update_set_linked_instances(
            afu_logic.anjay, iid, linked_target_iids, new_linked_iids_count);
}

static void remove_conflicting_instance(anjay_iid_t iid,
                                        anjay_iid_t target_iid) {
    const anjay_iid_t *conflicting_instances;
    anjay_iid_t conflicting_target_iids[AFU_NUMBER_OF_FIRMWARE_INSTANCES - 1];
    size_t conflicting_iids_count = 0;
    size_t new_conflicting_iids_count = 0;

    // get conflicting instances
    anjay_advanced_fw_update_get_conflicting_instances(afu_logic.anjay, iid,
                                                    &conflicting_instances,
                                                    &conflicting_iids_count);
    // remove target_iid from the list
    for (size_t i = 0; i < conflicting_iids_count; i++) {
        if (conflicting_instances[i] != target_iid) {
            conflicting_target_iids[new_conflicting_iids_count++] =
                    conflicting_instances[i];
        }
    }
    // update conflicting list
    anjay_advanced_fw_update_set_conflicting_instances(
            afu_logic.anjay, iid, conflicting_target_iids,
            new_conflicting_iids_count);
}
void fw_update_common_reset(anjay_iid_t iid, void *user_ptr) {
    (void) user_ptr;

    char new_firm_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
    get_firmware_download_name(iid, new_firm_name);

    // Reset can be issued even if the download never started
    if (afu_logic.new_firmware_file[iid]) {
        // We ignore the result code of fclose(), as fw_reset() can't fail
        (void) fclose(afu_logic.new_firmware_file[iid]);
        // and reset our global state to initial value
        afu_logic.new_firmware_file[iid] = NULL;
    }
    // Finally, let's remove any downloaded payload
    unlink(new_firm_name);

    // clear conflicting and linked instances in the object
    anjay_advanced_fw_update_set_conflicting_instances(afu_logic.anjay, iid,
                                                    NULL, 0);
    anjay_advanced_fw_update_set_linked_instances(afu_logic.anjay, iid, NULL,
                                                0);
    // clear conflicting and linked instances about this object from other
    // objects
    for (anjay_iid_t i = 0; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        if (i != iid) {
            remove_linked_instance(i, iid);
            remove_conflicting_instance(i, iid);
        }
    }
}

6.7.4.4. Installing the Advanced Firmware Update module

The afu_update_install function is similar to fw_update_install from Basic implementation. First we call anjay_advanced_fw_update_install then for each instance we check the existence of a “marker” file, based on which we call anjay_advanced_fw_update_instance_add with the appropriate arguments. For additional files, we create them if they do not exist and fill them with the default content (AFU_ADD_FILE_DEFAULT_CONTENT). Finally, we call the refresh_fw_version function so that the fw_update_common_get_current_version callback can return the correct value.

static const char *fw_update_common_get_current_version(anjay_iid_t iid,
                                                        void *user_ptr) {
    (void) user_ptr;

    return (const char *) afu_logic.fw_version[iid];
}

static const anjay_advanced_fw_update_handlers_t handlers = {
    .stream_open = fw_stream_open,
    .stream_write = fw_update_common_write,
    .stream_finish = fw_update_common_finish,
    .reset = fw_update_common_reset,
    .get_current_version = fw_update_common_get_current_version,
    .perform_upgrade = fw_update_common_perform_upgrade
};

int afu_update_install(anjay_t *anjay) {
    anjay_advanced_fw_update_initial_state_t state;
    char marker_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };
    char file_name[AFU_FILE_NAME_STR_MAX_LEN] = { 0 };

    memset(&state, 0, sizeof(state));

    afu_logic.anjay = anjay;

    anjay_advanced_fw_update_global_config_t config = {
        .prefer_same_socket_downloads = true
    };
    int result = anjay_advanced_fw_update_install(anjay, &config);
    if (result) {
        avs_log(advance_fu, ERROR,
                "Could not install advanced firmware object: %d", result);
        return -1;
    }

    // check if application was updated
    get_marker_file_name(AFU_DEFAULT_FIRMWARE_INSTANCE_IID, marker_name);
    if (access(marker_name, F_OK) != -1) {
        avs_log(advance_fu, INFO, "Application update succeded");
        state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS;
        unlink(marker_name);
    }
    result = anjay_advanced_fw_update_instance_add(
            anjay, AFU_DEFAULT_FIRMWARE_INSTANCE_IID, "application", &handlers,
            NULL, &state);
    if (result) {
        avs_log(advance_fu, ERROR,
                "Could not add default application instance: %d", result);
        return -1;
    }

    // check if additional files were updated, if not create it with default
    // value
    for (anjay_iid_t i = 1; i < AFU_NUMBER_OF_FIRMWARE_INSTANCES; i++) {
        memset(marker_name, 0, sizeof(marker_name));
        get_marker_file_name(i, marker_name);
        if (access(marker_name, F_OK) != -1) {
            avs_log(advance_fu, INFO,
                    "Additional file with idd: %d update succeded", i);
            state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS;
            unlink(marker_name);
        } else {
            state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_INITIAL;
        }

        memset(file_name, 0, sizeof(file_name));
        get_add_firmware_file_name(i, file_name);
        // create file only if not exist
        if (access(file_name, F_OK) != 0) {
            FILE *stream = fopen(file_name, "wb");
            if (!stream) {
                avs_log(advance_fu, ERROR, "Could not open %s", file_name);
                return -1;
            }
            if (fwrite(AFU_ADD_FILE_DEFAULT_CONTENT,
                    strlen(AFU_ADD_FILE_DEFAULT_CONTENT), 1, stream)
                    != 1) {
                avs_log(advance_fu, ERROR, "Could not write to %s", file_name);
                fclose(stream);
                return -1;
            }
            if (fclose(stream)) {
                avs_log(advance_fu, ERROR, "Could not close %s", file_name);
                return -1;
            }
        }

        snprintf(afu_logic.instance_name[i], AFU_INSTANCE_NAME_STR_MAX_LEN,
                "add_img_%d", i);
        result = anjay_advanced_fw_update_instance_add(
                anjay, i, afu_logic.instance_name[i], &handlers, NULL, &state);
        if (result) {
            avs_log(advance_fu, ERROR,
                    "Could not add the additional image instance: %d", result);
            return -1;
        }
    }

    if (refresh_fw_version()) {
        return -1;
    }

    return 0;
}