Exportable version of WizziLab's modem driver.

Dependents:   modem_ref_helper

src/modem_ref.cpp

Committer:
Jeej
Date:
2021-10-01
Revision:
65:c7749f9b6009
Parent:
63:544a488a06d3
Child:
67:e458db8402dc

File content as of revision 65:c7749f9b6009:

#include "mbed.h"
#include "WizziDebug.h"

#include "modem_ref.h"
#include "kal_crypto.h"
#include "kal_codec.h"
#include "d7a_1x_fs.h"

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

typedef struct {
    action_callback_t*      cb;
    alp_payload_t**         alp_rsp;
} modem_ref_user_t;

typedef struct {
    fx_serial_send_t*       send;
    modem_ref_callbacks_t*  cb;
    modem_ref_user_t        user[MAX_USER_NB];
    u8                      state;
} modem_ref_ctx_t;

static modem_ref_ctx_t g_modem;

#define STATE_OPEN      0xab
#define STATE_CLOSED    0

// Flows
#define WC_FLOWID_CMD       0x10
#define WC_FLOWID_ALP       0x20
#define WC_FLOWID_SYS       0x30

// Sub-Flows
#define WC_FLOW_ALP_CMD     0x20
#define WC_FLOW_ALP_RESP    0x21
#define WC_FLOW_ALP_UNS     0x22
#define WC_FLOW_SYS_RST     0x30
#define WC_FLOW_SYS_PING    0x34
#define WC_FLOW_SYS_PONG    0x35
#define WC_FLOW_SYS_CFG     0x36
#define WC_FLOW_SYS_XON     0x39
#define WC_FLOW_SYS_XOFF    0x3a
#define WC_FLOW_SYS_XACK    0x3b

// Misc CMD...
#define WM_BOOT             0x81

#define PKT_MAX_SIZE 255
static void serial_send(WizziComPacketType type, alp_payload_t* alp)
{
    u8 buf[PKT_MAX_SIZE];
    
    //PRINT("<--\n");
    //alp_payload_print(alp);
    
    u32 len = alp_payload_to_buf(alp, buf, PKT_MAX_SIZE);
    
    ASSERT(g_modem.send != NULL, "NULL send function!\r\n");
    g_modem.send(type, buf, len);
    
    alp_payload_free(alp);
}

static void call_fs_user_cb(alp_payload_t* alp)
{
    alp_parsed_chunk_t r;
    u8* p;
    int id = -1;
    u32 action = 0;
    
    while (alp != NULL)
    {
        p = alp->d;
        alp_parse_chunk(&p, &r);
        
        switch (r.type)
        {
            case ALP_OPCODE_TAG:
                id  = r.meta.tag.id;
                REF_PRINT("ACT %d: TAG[%d] ID:%d ERR:%d EOP:%d\n", action, id, r.meta.tag.err, r.meta.tag.eop);
                break;
            case ALP_OPCODE_F_RD_DATA:
                REF_PRINT("ACT %d: F_RD_DATA[%d] @%d %d bytes\n", action, r.meta.f_data.fid, r.meta.f_data.offset, r.meta.f_data.length);
                if (g_modem.cb->read)
                {
                    g_modem.cb->read(action, r.meta.f_data.fid, r.meta.f_data.offset, r.meta.f_data.length, id);
                }
                break;
            case ALP_OPCODE_F_WR_DATA:
                REF_PRINT("ACT %d: F_WR_DATA[%d] @%d %d bytes\n", action, r.meta.f_data.fid, r.meta.f_data.offset, r.meta.f_data.length);
                if (g_modem.cb->write)
                {
                    g_modem.cb->write(action, r.meta.f_data.fid, r.data, r.meta.f_data.offset, r.meta.f_data.length, id);
                }
                break;
            case ALP_OPCODE_F_RD_PROP:
                REF_PRINT("ACT %d: F_RD_PROP[%d]\n", action, r.meta.f_data.fid);
                if (g_modem.cb->read_fprop)
                {
                    g_modem.cb->read_fprop(action, r.meta.f_data.fid, id);
                }
                break;
            case ALP_OPCODE_F_DELETE:
                REF_PRINT("ACT %d: F_DELETE[%d]\n", action, r.meta.f_data.fid);
                if (g_modem.cb->remove)
                {
                    g_modem.cb->remove(action, r.meta.f_data.fid, id);
                }
                break;
            case ALP_OPCODE_F_FLUSH:
                REF_PRINT("ACT %d: F_FLUSH[%d]\n", action, r.meta.f_data.fid);
                if (g_modem.cb->flush)
                {
                    g_modem.cb->flush(action, r.meta.f_data.fid, id);
                }
                break;
            default:
                ASSERT(false, "ASSERT: Unsupported ALP File Operation: %d\n", r.type);
                break;
        }
        
        ASSERT(id >= 0, "ASSERT: Wrong id %d\n", id);
                
        action++;
        
        alp = alp->next;
    }
}

void modem_ref_input(u8 flowid, u8* payload, u8 size)
{
    alp_parsed_chunk_t r;
    int id = -1;
    s8 err = ALP_ERR_NONE;
    u8 eop;
    alp_payload_t* alp = NULL;

    REF_PRINT("input 0x%x/%d Bytes\n", flowid, size);
    switch (flowid)
    {
        case WC_FLOW_ALP_UNS:
        case WC_FLOW_ALP_CMD:

            // pre-parse payload
            alp = alp_payload_parse(payload, size);
            
            //PRINT("-->\n");
            //alp_payload_print(alp);
            
            if (ALP_OPCODE_TAG == (alp->d[0] & 0x3F))
            {
                // Payload starts with a tag, this must be a FS command
                call_fs_user_cb(alp);
                alp_payload_free(alp);
            }
            else
            {
                // else this is a 'real' URC (or ISTATUS if enabled on modem)
                if (g_modem.cb->udata)
                {
                    // XXX All URCs go to the user
                    g_modem.cb->udata(alp);
                }
                else
                {
                    alp_payload_free(alp);
                }
            }

            break;
        case WC_FLOW_ALP_RESP:
        
            // pre-parse payload
            alp = alp_payload_parse(payload, size);
            
            //PRINT("-->\n");
            //alp_payload_print(alp);
            
            alp_payload_parse_chunk(alp, &r);

            // This should always be a TAG'ed response as we tag our requests
            ASSERT((r.type == ALP_OPCODE_RSP_TAG), "ASSERT: expecting RESP_TAG got 0x%02X\n", r.type);
            
            id  = r.meta.tag.id;
            eop = r.meta.tag.eop;
            
            if (NULL == g_modem.user[id].cb)
            {
                PRINT("WARNING: NULL Callback for ID %d\n", id);
                alp_payload_print(alp);
                alp_payload_free(alp);
                break;
            }
            //ASSERT(g_modem.user[id].cb != NULL, "ASSERT: NULL Callback for ID %d\n", id);
            
            if (g_modem.user[id].alp_rsp)
            {
                // Give the remaining of the payload to the user to deal with
                *(g_modem.user[id].alp_rsp) = alp_payload_append(*(g_modem.user[id].alp_rsp), alp);
            }
            else
            {
                // Discard payload
                alp_payload_free(alp);
            }
            
            if(eop)
            { // This is mainly for debug, catch old pointers
                //g_modem.user[id].data   = NULL;
                //g_modem.user[id].istatus= NULL;
                REF_PRINT("EOP\n");
                 
                // User Callback
                g_modem.user[id].cb(id);
            }

            break;
        case WC_FLOW_SYS_RST:
            // 'Software' reset request
            if (g_modem.cb->reset)
            {
                g_modem.cb->reset();
            }
            break;
        case WC_FLOWID_CMD:
            if (WM_BOOT == payload[0])
            {
                if (g_modem.cb->boot)
                {
                    uint8_t cause = payload[1];
                    uint16_t nb_boot = HAL_TO_U16(payload[4], payload[3]);
                    
                    g_modem.cb->boot(cause, nb_boot);
                }
            }
            else
            {
                REF_PRINT("Unsupported CMD :0x%x\n", payload[0]);
            }
            break;
        default:
            REF_PRINT("Unsupported WC Flowid:0x%x / %d Bytes\n", flowid, size);
            break;
    }
}

static u32 modem_ref_add_root_permission(u8* p, u8* root_key, u8* action, u32 action_size)
{
    u8 hash[32];

    // Calculate hash on action
    kal_sha256_init();
    kal_sha256_update(action, action_size);
    kal_sha256_update(root_key, D7A_FS_ROOT_KEY_SIZE);
    kal_sha256_final(hash);
    // Prepend Permission-request...
    ALP_ACTION_PERM_REQ(p, false, ALP_PERM_REQ_ROOT, ALP_WIZZILAB_AUTH_PROTOCOL_ID);
    // ... and hash
    memcpy(p, hash, ALP_ACTION_PERM_REQ_TOKEN_SIZE);

    return ALP_ACTION_PERM_REQ_OFFSET;
}

int modem_ref_get_id(action_callback_t* cb)
{
    int i;
    // Use rolling ids
    static u8 rolling;

    for(i = 0; i < MAX_USER_NB; i++)
    {
        int id = (i + rolling) % MAX_USER_NB;

        if (g_modem.user[id].cb == NULL)
        {
            g_modem.user[id].cb = cb;
            rolling++;
            //PRINT("Get ID:%d/0x%08x\n", id, cb);
            return id;
        }
    }
    ASSERT(i < MAX_USER_NB, "ASSERT: out of users\n");
    return -1;
}

int modem_ref_free_id(u8 id)
{
    if (id < MAX_USER_NB)
    {
        g_modem.user[id].cb     = NULL;
        //PRINT("Free ID:%d\n", id);
        return id;
    }
    return -1;
}

void modem_ref_open(fx_serial_send_t* send, modem_ref_callbacks_t* callbacks)
{
    if (g_modem.state != STATE_OPEN)
    {
        g_modem.send = send;
        g_modem.cb  = callbacks;
        memset(g_modem.user, 0, (MAX_USER_NB * sizeof(modem_ref_user_t)));

        REF_PRINT("Open Modem - Max users:%d\n", MAX_USER_NB);

        g_modem.state = STATE_OPEN;
    }
}

void modem_ref_close(void)
{
    if (g_modem.state == STATE_OPEN)
    {
        g_modem.state = STATE_CLOSED;
    }
}

void modem_ref_reset(void)
{
    serial_send(WizziComPacketSysReset, NULL);
}

void modem_ref_set_boot_cb(fx_boot_t* boot_cb)
{
    g_modem.cb->boot = boot_cb;
}

void modem_ref_alp(void* itf, alp_payload_t* alp_user, alp_payload_t** alp_rsp, int id)
{
    alp_payload_t* alp = NULL;

    if (alp_rsp)
    {
        // Expecting a response

        // Make sure pointer value is NULL
        *alp_rsp = NULL;
    }

    // response pointer
    g_modem.user[id].alp_rsp = alp_rsp;

    // Always a tag
    alp = alp_payload_tag(alp, id);

    // Forward on itf if any
    if (itf)
    {
        alp = alp_payload_forward(alp, itf);
        // NOP action to get istats on ACKs
        alp = alp_payload_nop(alp, false);
    }

    // Add user payload
    alp = alp_payload_append(alp, alp_user);

    // Send to modem
    serial_send(WizziComPacketAlpUns, alp);
}

static void _modem_ref_rsp(alp_payload_t* alp_user, u8 err, int id)
{
    alp_payload_t* alp = NULL;

    // response pointer
    g_modem.user[id].alp_rsp = NULL;

    // Always a tag
    alp = alp_payload_rsp_tag(alp, id, true, err);

    // Add user payload
    alp = alp_payload_append(alp, alp_user);

    // Send to modem
    serial_send(WizziComPacketAlpResp, alp);
}

void modem_ref_respond(u8 action, s8 status, int id)
{
    alp_payload_t* alp = NULL;

    alp = alp_payload_rsp_status(alp, action, status);

    _modem_ref_rsp(alp, (status < ALP_ERR_NONE)? true: false, id);
}

void modem_ref_respond_fprop(u8 fid, alp_file_header_t* hdr, int id)
{
    alp_payload_t* alp = NULL;

    alp = alp_payload_rsp_fprop(alp, fid, hdr);

    _modem_ref_rsp(alp, false, id);
}

void modem_ref_respond_read(u8 fid, void* data, u32 offset, u32 length, int id)
{
    alp_payload_t* alp = NULL;

    alp = alp_payload_rsp_f_data(alp, fid, data, offset, length);

    _modem_ref_rsp(alp, false, id);
}