Helper library to use modem_ref driver.

Dependencies:   WizziCom WizziDebug ram_fs modem_ref

Dependents:   D7A_Localisation D7A_1x_demo_send_file_data_and_forget D7A_1x_demo_CodeUpgradeProtocol D7A_1x_demo_LoRaWAN ... more

modem_ref_helper.cpp

Committer:
Jeej
Date:
2018-12-14
Revision:
50:033e9d2ed22d
Parent:
49:035512e64fdd
Child:
58:f7105abdceed

File content as of revision 50:033e9d2ed22d:

#include "modem_ref_helper.h"

#if 0
    #define HELPER_PRINT(...)  PRINT(__VA_ARGS__)
#else
    #define HELPER_PRINT(...);
#endif

#define MODEM_VERSION_MAJOR         5
#define MODEM_VERSION_MINOR         0
#define MODEM_VERSION_PATCH         36
#define MODEM_DEVICE_ID             0x00001001

static WizziCom* g_modem_com;
static Semaphore g_modem_ready;
static int8_t    g_err;

void modem_print_error(uint8_t itf, int8_t error)
{
    if (ALP_ERR_ITF_START >= error && ALP_ERR_ITF_END < error)
    {
        error -= ALP_ERR_ITF_START;
        // Interface specific error
        if (ALP_ITF_TYPE_HOST == itf)
        {
            PRINT("ITF[%02X] Error %d\r\n", itf, error);  
        }
        else if (ALP_ITF_TYPE_COM == itf)
        {
            PRINT("ITF[%02X] Error %d\r\n", itf, error);  
        }
        else if (ALP_ITF_TYPE_D7A == itf)
        {
            PRINT("ITF[%02X] ", itf);
            switch (error)
            {
                /// No error
                case D7A_ERROR_NO: //            =  0,
                    PRINT("D7A_ERROR_NO\r\n");
                    break;
                /// Resource busy
                case D7A_ERROR_BUSY: //          = -1,
                    PRINT("D7A_ERROR_BUSY\r\n");
                    break;
                /// Bad parameter
                case D7A_ERROR_BAD_PARAM: //     = -2,
                    PRINT("D7A_ERROR_BAD_PARAM\r\n");
                    break;
                /// Duty cycle limit overflow
                case D7A_ERROR_DUTY_CYCLE: //    = -3,
                    PRINT("D7A_ERROR_DUTY_CYCLE\r\n");
                    break;
                /// CCA timeout
                case D7A_ERROR_CCA_TO: //        = -4,
                    PRINT("D7A_ERROR_CCA_TO\r\n");
                    break;
                /// Security frame counter overflow
                case D7A_ERROR_NLS_KEY: //       = -5,
                    PRINT("D7A_ERROR_NLS_KEY\r\n");
                    break;
                /// TX stream underflow
                case D7A_ERROR_TX_UDF: //        = -6,
                    PRINT("D7A_ERROR_TX_UDF\r\n");
                    break;
                /// RX stream overflow
                case D7A_ERROR_RX_OVF: //        = -7,
                    PRINT("D7A_ERROR_RX_OVF\r\n");
                    break;
                /// RX checksum
                case D7A_ERROR_RX_CRC: //        = -8,
                    PRINT("D7A_ERROR_RX_CRC\r\n");
                    break;
                /// Abort
                case D7A_ERROR_ABORT: //         = -9,
                    PRINT("D7A_ERROR_ABORT\r\n");
                    break;
                /// No ACK received
                case D7A_ERROR_NO_ACK: //        = -10,
                    PRINT("D7A_ERROR_NO_ACK\r\n");
                    break;
                /// RX timeout
                case D7A_ERROR_RX_TO: //         = -11,  
                    PRINT("D7A_ERROR_RX_TO\r\n");
                    break;
                default:
                    PRINT("Unknown Error %d\r\n", error);
                    break;
            }
        }
        else if (ALP_ITF_TYPE_LWAN == itf)
        {
            PRINT("ITF[%02X] Error %d\r\n", itf, error);  
        }
        else
        {
            PRINT("ITF[%02X] Error %d\r\n", itf, error);  
        }
    }
    else
    {
        PRINT("ALP: ");
        switch (error)
        {
            // Not really errors, more like status
            case ALP_ERR_ITF_FULL: // 0x02: For interfaces supporting buffering, indicates buffer reached maximum capacity (no data loss)  
                PRINT("ALP_ERR_ITF_FULL\r\n");
                break;
            case ALP_ERR_PARTIAL_COMPLETION: // 0x01: Action received and partially completed at response.  To be completed after response
                PRINT("ALP_ERR_PARTIAL_COMPLETION\r\n");
                break;
                
            // ALP Errors
            case ALP_ERR_NONE: // 0x00: Action completed (OK)
                PRINT("ALP_ERR_NONE\r\n");
                break;
            case ALP_ERR_FILE_NOT_FOUND: // 0xFF: Error access file: File ID does not exist
                PRINT("ALP_ERR_FILE_NOT_FOUND\r\n");
                break;
            case ALP_ERR_FILE_EXIST: // 0xFE: Error create file: File ID already exists
                PRINT("ALP_ERR_FILE_EXIST\r\n");
                break;
            case ALP_ERR_FILE_NOT_RESTORABLE: // 0xFD: Error restore file: File is not restorable
                PRINT("ALP_ERR_FILE_NOT_RESTORABLEr\n");
                break;
            case ALP_ERR_PERMISSION_DENIED: // 0xFC: Error access file: Insufficient permissions
                PRINT("ALP_ERR_PERMISSION_DENIED\r\n");
                break;
            case ALP_ERR_LENGTH_OVERFLOW: // 0xFB: Error create file: Supplied length (in header) is beyond file limits
                PRINT("ALP_ERR_LENGTH_OVERFLOW\r\n");
                break;
            case ALP_ERR_ALLOC_OVERFLOW: // 0xFA: Error create file: Supplied allocation (in header) is beyond file limits
                PRINT("ALP_ERR_ALLOC_OVERFLOW\r\n");
                break;
            case ALP_ERR_OFFSET_OVERFLOW: // 0xF9: Error write: Supplied start offset is out of bounds of file allocation
                PRINT("ALP_ERR_OFFSET_OVERFLOW\r\n");
                break;
            case ALP_ERR_WRITE_OVERFLOW: // 0xF8: Error complete write: Supplied data goes beyond file allocation
                PRINT("ALP_ERR_WRITE_OVERFLOW\r\n");
                break;
            case ALP_ERR_WRITE_ERROR: // 0xF7: Error write: impossible to write in storage location
                PRINT("ALP_ERR_WRITE_ERROR\r\n");
                break;
            case ALP_ERR_OPERATION_UNKNOWN: // 0xF6: Error unknown Operation
                PRINT("ALP_ERR_OPERATION_UNKNOWN\r\n");
                break;
            case ALP_ERR_OPERAND_INCOMPLETE: // 0xF5: Error incomplete Operand
                PRINT("ALP_ERR_OPERAND_INCOMPLETE\r\n");
                break;
            case ALP_ERR_OPERAND_WRONG_FORMAT: // 0xF4: Error wrong Operand format
                PRINT("ALP_ERR_OPERAND_WRONG_FORMAT\r\n");
                break;
            case ALP_ERR_ITF_INVALID: // 0xF3: Error invalid interface
                PRINT("ALP_ERR_ITF_INVALID\r\n");
                break;
            case ALP_ERR_ITF_OVERFLOW: // 0xF2: Error interface overflown (i.e. resources exhausted, buffer full with data discarded)
                PRINT("ALP_ERR_ITF_OVERFLOW\r\n");
                break;
            case ALP_ERR_QUERY_FAIL: // 0xF1: (Group of) Query result was false (Informative error code).
                PRINT("ALP_ERR_QUERY_FAIL\r\n");
                break;
            case ALP_ERR_ITF_NOT_SPECIFIED:
                PRINT("ALP_ERR_ITF_NOT_SPECIFIED\r\n");
                break;
        
            // Other Errors
            case ALP_ERR_UNKNOWN: // 0x80: Unknown error
                PRINT("ALP_ERR_UNKNOWN\r\n");
                break;
            case ALP_ERR_FS_TIMEOUT: // 0x81: Internal FS Error
                PRINT("ALP_ERR_FS_TIMEOUT\r\n");
                break;
            case ALP_ERR_ITF_UNKNOWN: // 0x82: Unknown Interface
                PRINT("ALP_ERR_ITF_UNKNOWN\r\n");
                break;
            case ALP_ERR_ITF_TIMEOUT: // 0x83: Internal ITF Error
                PRINT("ALP_ERR_ITF_TIMEOUT\r\n");
                break;
            default:
                PRINT("Unknown Error %d\r\n", error);
                break;
        }
    }
}

int my_alp_itf_d7a_cfg_size(d7a_sp_cfg_t* cfg)
{
    int size = sizeof(d7a_sp_cfg_t) - sizeof(d7a_addressee_t);
    size += D7A_ADDR_LEN(cfg->addressee.ctrl);
    return size;
}

// ============================================================}}}

// Serial adapters to WizziLab's own architecture
// ============================================================{{{

static void modem_serial_input(WizziCom* com, WizziComPacket_t* pkt)
{
    modem_input(wizzicom_type_to_flow(pkt->type), pkt->data, pkt->length);
    FREE(pkt);
}

static int modem_serial_send(uint8_t* data1, uint8_t size1, uint8_t* data2, uint8_t size2)
{
    (void)size1;
    
    // Retrieve Flow ID from header and send packet 
    g_modem_com->send((WizziComPacketType)wizzicom_flow_to_type(data1[4]), size2, data2);

    return (size1 + size2);
}

// Callback for id User
static void my_main_callback(uint8_t terminal, int8_t err, uint8_t id)
{
    (void)id;
    
    g_err = err;
    
    //modem_print_error(0xC0, g_err);
    
    if (terminal)
    {
        g_modem_ready.release();
    }
}

// Misc
// ============================================================{{{
    
void modem_notify_host_rev(revision_t* data, alp_file_header_t* header, uint8_t* root_key)
{
    uint8_t id = modem_get_id(my_main_callback);
    
    // Write the version to the version file.
    // This file is notified on write.
    modem_write_file_root(FID_HOST_REV, (void*)data, 0, HAL_U32_BYTE_SWAP(header->alloc), root_key, id);
    g_modem_ready.wait();
    
    // Error when writing to the file
    if (ALP_ERR_NONE != g_err)
    {
        // File exists
        if (ALP_ERR_FILE_NOT_FOUND != g_err)
        {
            // Delete old file
            modem_delete_file_root(FID_HOST_REV, root_key, id);
            g_modem_ready.wait();
        }
        
        // Recreate file
        modem_create_file(FID_HOST_REV, header, id);
        g_modem_ready.wait();
        
        // Retry writing to the file
        modem_write_file_root(FID_HOST_REV, (void*)data, 0, HAL_U32_BYTE_SWAP(header->alloc), root_key, id);
        g_modem_ready.wait();
    }
    
    modem_free_id(id);
}

void modem_update_file(uint8_t fid, alp_file_header_t* header, uint8_t* data)
{
    alp_file_header_t remote_header;
    uint8_t id = modem_get_id(my_main_callback);
    
    memset(&remote_header, 0, sizeof(alp_file_header_t));
    
    // Read remote header
    modem_read_fprop(fid, &remote_header, id);
    g_modem_ready.wait();
    
    // Add file in local file system
    ram_fs_new(fid, (uint8_t*)header, data);
    
    // Update file
    if (memcmp(&remote_header, header, sizeof(alp_file_header_t)))
    {
        HELPER_PRINT("Updating file %d\n", fid);
        // Delete
        modem_delete_file(fid, id);
        g_modem_ready.wait();
        // Restore in local file system
        ram_fs_new(fid, (uint8_t*)header, data);
        // Re-create
        if (data)
        {
            modem_declare_file(fid, header, true, id);
        }
        else
        {
            modem_create_file(fid, header, id);
        }
        g_modem_ready.wait();
    }
    else
    {
        HELPER_PRINT("File %d up to date\n", fid);
    }
    
    modem_free_id(id);
}

Semaphore boot(0);
void my_startup_boot(u8 cause, u16 number)
{
    HELPER_PRINT("Modem BOOT[%c] #%d\r\n", cause, number);
    boot.release();
}

void modem_helper_open(modem_callbacks_t* callbacks)
{
    static union {
        uint8_t      b[8];
        uint32_t     w[2];
    } uid;
    revision_t rev;
    
    // Override boot callback to catch the first boot message
    modem_callbacks_t boot_callbacks = {
        .read       = NULL,
        .write      = NULL,
        .read_fprop = NULL,
        .flush      = NULL,
        .remove     = NULL,
        .udata      = NULL,
        .lqual      = NULL,
        .ldown      = NULL,
        .reset      = NULL,
        .boot       = my_startup_boot,
        .busy       = NULL,
        .itf_busy   = NULL,
    };
    
    // Open modem Com port
    g_modem_com = new WizziCom(MODEM_PIN_RX, MODEM_PIN_TX, MODEM_PIN_IRQ_IN, MODEM_PIN_IRQ_OUT);
    
    // Hardware reset
    DigitalOut reset_low(MODEM_PIN_RESET, 0);
    Thread::wait(100);
    
    // Open driver to catch boot packet
    modem_open(NULL, &boot_callbacks);
    
    // Redirect All Port traffic to modem_serial_input
    g_modem_com->attach(modem_serial_input, WizziComPacketUntreated);
    
    // Release reset
    DigitalIn reset_release(MODEM_PIN_RESET);
    
    // Wait for first boot packet
    HELPER_PRINT("Waiting boot packet...\n");
    boot.wait();
    
    // Re-open with user callbacks
    modem_close();
    modem_open(modem_serial_send, callbacks);
    
    uint8_t id = modem_get_id(my_main_callback);

    HELPER_PRINT("Start Modem Process (id=%d)\n", id);
    Thread::wait(1000);
    
    modem_read_file(D7A_FID_UID, uid.b, 0, 8, id);
    g_modem_ready.wait();
    
    modem_read_file(D7A_FID_FIRMWARE_VERSION, (uint8_t*)&rev, 0, sizeof(revision_t), id);
    g_modem_ready.wait();
    
    PRINT("------------ D7A Modem infos ------------\r\n");
    PRINT_DATA(" - UID:              ", "%02X", uid.b, 8, "\r\n");
    PRINT(" - Manufacturer ID:  %08X\r\n", rev.manufacturer_id);
    PRINT(" - Device ID:        %08X\r\n", rev.device_id);
    PRINT(" - Hardware version: %08X\r\n", rev.hw_version);
    PRINT(" - Firmware version: v%d.%d.%d [%02X]\r\n", rev.fw_version.major, rev.fw_version.minor, rev.fw_version.patch, rev.fw_version.id);
    PRINT(" - CUP max size:     %d\r\n", rev.cup_max_size);
    PRINT("-----------------------------------------\r\n");
    
    if (MODEM_DEVICE_ID == rev.device_id)
    {
        // Check version
        uint32_t rev_sum1 = (rev.fw_version.major << 24) | (rev.fw_version.minor << 16) | rev.fw_version.patch;
        uint32_t rev_sum2 = (MODEM_VERSION_MAJOR << 24) | (MODEM_VERSION_MINOR << 16) | MODEM_VERSION_PATCH;
        if (rev_sum1 < rev_sum2)
        {
            PRINT("\r\nYou need a modem at version %d.%d.%d or above to use this Driver.\r\n"
                  "You can:\r\n"
                  " - Check modem updates on the Dash7Board:\r\n"
                  "   Go to your site's version manager.\r\n"
                  " - Check modem updates by importing:\r\n"
                  "   https://developer.mbed.org/teams/WizziLab/code/D7A_WM_Updater\r\n"
                  " - Choose the right 'modem_ref_helper' revision.\r\n"
                  "   Right click on modem_ref_helper -> Revision.\r\n"
                  , MODEM_VERSION_MAJOR, MODEM_VERSION_MINOR, MODEM_VERSION_PATCH);
    
            Thread::wait(osWaitForever);
        }
    }
    else
    {
        // Do not check version
        PRINT("/!\\ Not a modem firmware /!\\\r\n");
    }
    
    modem_free_id(id);
}