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_d7a.cpp

Committer:
Jeej
Date:
2021-09-21
Revision:
76:cbe558f136a4
Parent:
75:dad2f09cb870
Child:
77:5146f44cde83

File content as of revision 76:cbe558f136a4:


#include "mbed.h"
#include "modem_d7a.h"
#include "modem_cup.h"

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

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

Semaphore g_s[MAX_USER_NB];

static WizziCom* g_modem_com;

// Callback for id User
static void modem_cb(uint8_t terminal, int8_t err, uint8_t id)
{
    (void)id;
    
    //PRINT("CB EOP:%d ERR:%d ID:%d\n", terminal, err, id);

    if (terminal)
    {
        g_s[id].release();
    }
}

static int g_boot_nb;
static int g_read_nb;
static int g_swr_nb;
static int g_hwr_nb;
static int g_pwc_nb;
static int g_fail_nb;

int modem_wait_resp(Semaphore* s, uint32_t timeout, char* function, uint32_t line)
{
    if (!s->try_acquire_for(timeout))
    {
        PRINT("%s:%d: Modem resp timeout after %d ms\n", function, line, timeout);
        return -1;
    }

    return 0;
}

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_ref_input(wizzicom_type_to_flow(pkt->type), pkt->data, pkt->length);
}

static int modem_serial_send(uint8_t type, uint8_t* data, uint8_t size)
{
    // Retrieve Flow ID from header and send packet
    g_modem_com->send((WizziComPacketType)type, size, data);

    return size;
}

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

int modem_open(modem_ref_callbacks_t* callbacks)
{
    static union {
        uint8_t      b[8];
        uint32_t     w[2];
    } uid;
    revision_t rev;
    int err;
    
    //for (int i = 0; i < MAX_USER_NB; i++)
    //{
        //g_s[i] = Semaphore(0);
    //}

    g_boot_nb++;

    // Override boot callback to catch the first boot message
    modem_ref_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);

    // open with user callbacks
    modem_ref_open(modem_serial_send, callbacks);

    // Redirect All Port traffic to modem_serial_input
    g_modem_com->attach(modem_serial_input, WizziComPacketUntreated);

    // Wait for modem power up
    ThisThread::sleep_for(100);

    // Try reading UID
    err = modem_read_file(D7A_FID_UID, uid.b, 0, 8);

    if (ALP_ERR_NONE > err)
    {
        PRINT("Trying software reset...\n");

        // Open driver to catch boot packet
        modem_ref_close();
        modem_ref_open(NULL, &boot_callbacks);

        // Try software reset
        g_modem_com->send(WizziComPacketSysReset, 0, NULL);

        if (!boot.try_acquire_for(1000))
        {
            PRINT("Trying hardware reset...\n");

            // Assert reset pin
            DigitalOut reset_low(MODEM_PIN_RESET, 0);
            ThisThread::sleep_for(100);

            // Release reset pin
            DigitalIn reset_release(MODEM_PIN_RESET);

            if (!boot.try_acquire_for(1000))
            {
#if 0
                // Modem not responding!
                PRINT("Trying power cycle.\n");

                // Assert power pin
                DigitalOut power_high(D12, 1);
                ThisThread::sleep_for(1000);

                // Release reset pin
                DigitalIn power_release(D12);

                if (boot.try_acquire_for(1000))
                {
                    g_pwc_nb++;
                    PRINT("Modem is up after power cycle.\n");
                }
                else
#endif
                {
                    // Modem not responding!
                    g_fail_nb++;
                    PRINT("Failed to open modem.\n");
                    return -1;
                }
            }
            else
            {
                g_hwr_nb++;
                PRINT("Modem is up after hardware reset.\n");
            }
        }
        else
        {
            g_swr_nb++;
            PRINT("Modem is up after software reset.\n");
        }
        
        // Re-open with user callbacks
        modem_ref_close();
        modem_ref_open(modem_serial_send, callbacks);
    }
    else
    {
        g_read_nb++;
        PRINT("Modem is up.\n");
    }

    PRINT("Boot stats: boot:%d read:%d swr:%d hwr:%d pwc:%d fail:%d\n", g_boot_nb, g_read_nb, g_swr_nb, g_hwr_nb, g_pwc_nb, g_fail_nb);

    ThisThread::sleep_for(100);

    modem_read_file(D7A_FID_UID, uid.b, 0, 8);
    modem_read_file(D7A_FID_FIRMWARE_VERSION, (uint8_t*)&rev, 0, sizeof(revision_t));

    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");
    FLUSH();

    //modem_cup_update(&rev);

    return 0;
}

int modem_close(void)
{
    modem_ref_close();
    delete g_modem_com;

    return 0;
}

static int _modem_d7a_send(void* itf, alp_payload_t* alp, alp_payload_t** alp_rsp, uint32_t timeout)
{
    u8 id = modem_ref_get_id(modem_cb);
    int err = ALP_ERR_UNKNOWN;
    
    // Make sure Semaphore is taken
    g_s[id].try_acquire();

    modem_ref_alp(itf, alp, alp_rsp, id);
    if (!g_s[id].try_acquire_for(timeout))
    {
        err = ALP_ERR_UNKNOWN;
    }
    else
    {
        err = ALP_ERR_NONE;
    }

    modem_ref_free_id(id);

    return err;
}

static int _modem_get_istatus(alp_payload_t* alp, void* istatus)
{
    alp_payload_t* alp_tmp = alp_payload_get(alp, ALP_OPCODE_RSP_ISTATUS);
    
    if (alp_tmp)
    {
        alp_parsed_chunk_t r;
        u8* p = alp_tmp->d;
    
        alp_parse_chunk(&p, &r);
        memcpy(istatus, r.data, r.meta.itf.length);
        
        return ALP_ERR_NONE;
    }
    else
    {
        PRINT("No metadata\n");
    }
    
    return ALP_ERR_UNKNOWN;
}

int modem_raw_alp(alp_payload_t* alp, alp_payload_t** alp_rsp)
{
    return _modem_d7a_send(NULL, alp, alp_rsp, MODEM_TIMEOUT_LOCAL);
}

int modem_remote_raw_alp(void* itf, alp_payload_t* alp, alp_payload_t** alp_rsp)
{
    return _modem_d7a_send(itf, alp, alp_rsp, MODEM_TIMEOUT_DISTANT);
}

static int _modem_send_alp(void* itf, void *istatus, alp_payload_t* alp, uint32_t timeout)
{
    int err = ALP_ERR_UNKNOWN;
    alp_payload_t* alp_rsp = NULL;
    
    err = _modem_d7a_send(itf, alp, &alp_rsp, timeout);
    
    do {
        if (ALP_ERR_NONE > err) { PRINT("Send ALP timeout\n"); break; }

        err = alp_payload_get_err(alp_rsp);
        
        if (ALP_ERR_NONE > err) { PRINT("Send ALP err %d\n", err); break; }
        
        if (istatus)
        {
            _modem_get_istatus(alp_rsp, istatus);
        }
    
    } while (0);

    alp_payload_free(alp_rsp);

    return err;
}

static int _modem_read_file(uint8_t* itf, void *istatus, uint8_t fid, void *data, uint32_t offset, uint32_t length, uint32_t timeout)
{
    int err = ALP_ERR_UNKNOWN;
    alp_payload_t* alp = NULL;
    alp_payload_t* alp_rsp = NULL;

    alp = alp_payload_f_rd_data(NULL, fid, offset, length, false);
    
    err = _modem_d7a_send(NULL, alp, &alp_rsp, timeout);
    
    do {
        if (ALP_ERR_NONE > err) { PRINT("Read file timeout\n"); break; }

        err = alp_payload_get_err(alp_rsp);
        
        if (ALP_ERR_NONE > err) { PRINT("Read file err %d\n", err); break; }
        
        alp = alp_payload_get(alp_rsp, ALP_OPCODE_RSP_F_DATA);
        if (alp)
        {
            alp_parsed_chunk_t r;
            u8* p = alp->d;
            
            alp_parse_chunk(&p, &r);
            memcpy(data, r.data, r.meta.f_data.length);
        }
        else
        {
            PRINT("Read file no payload\n");
            err = ALP_ERR_UNKNOWN;
            break;
        }
        
        if (istatus)
        {
            _modem_get_istatus(alp_rsp, istatus);
        }
    
    } while (0);

    alp_payload_free(alp_rsp);

    return err;
}

int modem_read_file(uint8_t fid, void *data, uint32_t offset, uint32_t length)
{
    return _modem_read_file(NULL, NULL, fid, data, offset, length, MODEM_TIMEOUT_LOCAL);
}

int modem_remote_read_file(uint8_t* itf, void *istatus, uint8_t fid, void *data, uint32_t offset, uint32_t length)
{
    return _modem_read_file(itf, istatus, fid, data, offset, length, MODEM_TIMEOUT_DISTANT);
}

int modem_write_file(uint8_t fid, void *data, uint32_t offset, uint32_t length)
{
    alp_payload_t* alp = alp_payload_f_wr_data(NULL, fid, data, offset, length, false);
    return _modem_send_alp(NULL, NULL, alp, MODEM_TIMEOUT_LOCAL);
}

int modem_remote_write_file(uint8_t* itf, void *istatus , uint8_t fid, void *data, uint32_t offset, uint32_t length)
{
    alp_payload_t* alp = alp_payload_f_wr_data(NULL, fid, data, offset, length, false);
    return _modem_send_alp(itf, istatus, alp, MODEM_TIMEOUT_DISTANT);
}

int modem_flush_file(uint8_t fid)
{
    alp_payload_t* alp = alp_payload_f_flush(NULL, fid, false);
    return _modem_send_alp(NULL, NULL, alp, MODEM_TIMEOUT_LOCAL);
}

int modem_declare_file(uint8_t fid, alp_file_header_t* header)
{
    alp_payload_t* alp = alp_payload_f_declare(NULL, fid, header);
    return _modem_send_alp(NULL, NULL, alp, MODEM_TIMEOUT_LOCAL);
}

int modem_notify_file(uint8_t fid, uint32_t offset, uint32_t length)
{
    alp_payload_t* alp = alp_payload_f_touch(NULL, fid, offset, length, false);
    return _modem_send_alp(NULL, NULL, alp, MODEM_TIMEOUT_LOCAL);
}

static int _modem_d7a_itf_enable(u8 enable)
{
    alp_payload_t* alp = alp_payload_activate_itf(NULL, ALP_ITF_TYPE_D7A, 24, 0, ALP_D7A_ISTAT_RESP | ALP_D7A_ISTAT_UNS | ALP_D7A_ISTAT_EOP, enable);
    return _modem_send_alp(NULL, NULL, alp, MODEM_TIMEOUT_LOCAL);
}

int modem_d7a_enable_itf(void)
{
    return _modem_d7a_itf_enable(true);
}

int modem_d7a_disable_itf(void)
{
    return _modem_d7a_itf_enable(false);
}

static int modem_lwan_set_urc(void)
{
    alp_payload_t* alp = alp_payload_urcc_en(NULL, ALP_URC_TYPE_ITF_BUSY, FID_LWAN_ITF, 1);
    return _modem_send_alp(NULL, NULL, alp, MODEM_TIMEOUT_LOCAL);
}

static int modem_lwan_enable_itf(void)
{
    alp_payload_t* alp = alp_payload_activate_itf(NULL, ALP_ITF_TYPE_LWAN, 1, FID_LWAN_ITF, 0, true);
    return _modem_send_alp(NULL, NULL, alp, MODEM_TIMEOUT_LOCAL);
}

int modem_lwan_open(modem_lwan_callbacks_t* callbacks)
{
    int err = ALP_ERR_UNKNOWN;
    
    modem_ref_set_lwan_cb(callbacks);
    
    err = modem_lwan_set_urc();
    if (ALP_ERR_NONE > err) { return err; }
    
    err = modem_lwan_enable_itf();
    
    return err;
}

int modem_lwan_set_cfg(lwan_cfg_t* cfg)
{    
    return modem_write_file(FID_LWAN_CFG, cfg, 0, sizeof(lwan_cfg_t));
}

int modem_lwan_set_nls(lwan_nls_t* nls)
{
    return modem_write_file(FID_LWAN_NLS, nls, 0, sizeof(lwan_nls_t));
}

int modem_lwan_send(alp_payload_t* alp)
{
    alp_itf_lwan_cfg_t itf = { .type = ALP_ITF_TYPE_LWAN };
    return _modem_d7a_send((void*)&itf, alp, NULL, MODEM_TIMEOUT_DISTANT);
}

int modem_lwan_get_status(lwan_status_t* status)
{    
    return modem_read_file(FID_LWAN_STATUS, status, 0, sizeof(lwan_status_t));
}

void modem_print_error(uint8_t itf, int 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("ITF[%02X] ALP: ", itf);
        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;
        }
    }
}