Exportable version of WizziLab's modem driver.

Dependents:   modem_ref_helper

src/alp_helpers.cpp

Committer:
Jeej
Date:
2019-07-31
Revision:
46:9b83866cef2c
Parent:
41:6f83174ffed4
Child:
56:67e3d9608403

File content as of revision 46:9b83866cef2c:

/// @copyright
/// ========================================================================={{{
/// Copyright (c) 2012-2017 WizziLab                                           /
/// All rights reserved                                                        /
///                                                                            /
/// Should you have any questions regarding your right to use this Software,   /
/// contact WizziLab at www.wizzilab.com.                                      /
///                                                                            /
/// =========================================================================}}}
/// @endcopyright

//  ====================================================================
//  @file           alp_helpers.c
//  @brief          ALP public helpers functions.
//                  This code should be disclosable in source.
//  ====================================================================


#include "alp.h"
#include "kal_math.h"

//======================================================================
// alp_size
//----------------------------------------------------------------------
/// @brief Return payload length of most ALP operations
/// @param op       : ALP Action amongst alp_opcodes_t
/// @param offset   : Associated Offset if relevant
/// @param size     : Associated Size if relevant
/// @retval payload size in bytes
//======================================================================
public uint alp_size(u8 op, u32 offset, u32 length)
{
    uint len=2;// At least OP+FID / OP+TAG
    switch (op)
    {
        case ALP_OPCODE_NOP:
        case ALP_OPCODE_F_TOUCH:
        case ALP_OPCODE_F_EXIST:
        case ALP_OPCODE_F_DELETE:
        case ALP_OPCODE_F_FLUSH:
        case ALP_OPCODE_RSP_TAG:
        case ALP_OPCODE_F_RD_PROP:
            break;
        case ALP_OPCODE_RSP_STATUS:
            len += 1;
            break;
        case ALP_OPCODE_F_CREATE:
        case ALP_OPCODE_F_DECLARE:
        case ALP_OPCODE_RSP_F_PROP:
            len += ALP_FILE_HEADER_SIZE;
            break;
        case ALP_OPCODE_F_WR_PROP:
        case ALP_OPCODE_F_WR_DATA:
        case ALP_OPCODE_RSP_F_DATA:
            len = length;
            // Fallthrough
        case ALP_OPCODE_F_RD_DATA:
            len += ALP_LFIELD_SIZE(offset) + ALP_LFIELD_SIZE(length);
            break;
        default:
            //ALP_ASSERT(false,"ASSERT: ALP: alp_size unsupported op %d\n",op);
            break;
    }
    return len;
}

//======================================================================
// alp_encode_length
//----------------------------------------------------------------------
/// @brief Encodes an ALP length/offset field
/// @param p        : pointer to the payload buffer
/// @param len      : value to be encoded
/// @retval resulting payload size in bytes
//======================================================================
public u8 alp_encode_length(u8* p, u32 len)
{
    if (len <= 0x3F)
    {
        *p++ = len;
        return 1;
    }
    else if (len <= 0x3FFF)
    {
        *p++ = 0x40 + (u8)(len >> 8);
        *p++ =        (u8)(len & 0xFF);
        return 2;
    }
    else if (len <= 0x3FFFFF)
    {
        *p++ = 0x80 + (u8) (len >> 16);
        *p++ =        (u8)((len >> 8) & 0xFF);
        *p++ =        (u8) (len       & 0xFF);
        return 3;
    }
    else
    {
        *p++ = 0xC0 + (u8) (len >> 24);
        *p++ =        (u8)((len >> 16) & 0xFF);
        *p++ =        (u8)((len >>  8) & 0xFF);
        *p++ =        (u8) (len        & 0xFF);
        return 4;
    }
}


//======================================================================
// alp_decode_length
//----------------------------------------------------------------------
/// @brief Decodes an ALP length/offset field
/// @param p        : pointer to the pointer to payload buffer
/// @param actp     : pointer to ALP's Action Protocol Substitution flag
///                   Result amongst alp_actp_substitution_mode_t
/// @retval decoded value
//======================================================================
public u32 alp_decode_length(u8** p, u8* actp)
{
    u32 tmp = 0;
    switch ((**p) & 0xC0)
    {
        case 0xC0: // 0xCx xx xx xx
            tmp  = ((*(*p)++) & 0x3F) << 24;
            tmp +=  (*(*p)++)         << 16;
            tmp +=  (*(*p)++)         <<  8;
            tmp +=  (*(*p)++)         <<  0;
            break;
        case 0x80: // 0x8x xx xx : 16384 <= Len <4194303
            tmp  = ((*(*p)++) & 0x3F) << 16;
            tmp +=  (*(*p)++)         <<  8;
            if (tmp == 0)
            {
                // 0x8000 ActP special ActP code
                // Do not fetch the extra byte
                *actp = 2;
            }
            else
            {
                tmp +=  (*(*p)++)     <<  0;
            }
            break;
        case 0x40: // 0x4x xx : 64 <= Len < 16383
            tmp  = ((*(*p)++) & 0x3F) <<  8;
            tmp +=  (*(*p)++)         <<  0;
            if (tmp == 0)
            {
                // 0x4000 ActP special ActP code
                *actp = 1;
            }
            break;
        case 0: // Len <63
            tmp  = (*(*p)++ & 0x3F) <<  0;
            break;
    }
    return tmp;
}


//======================================================================
// alp_parse_chunk
//----------------------------------------------------------------------
/// @brief Parses an ALP payload and extract a single chunk (action or
///        response) to a more generic alp_parsed_chunk_t structure.
/// @param payload  : pointer to the pointer to payload buffer
/// @param resp     : pointer to alp_parsed_chunk_t structure
/// @retval number of parsed bytes
//======================================================================
public int alp_parse_chunk(u8** payload, alp_parsed_chunk_t* resp) // {{{
{
    u8* p = *payload;
    u8 op = *p++;
    u8 actp;
    u32 bytes;
    resp->type = op & 0x3F;
    resp->data = (u8*)NULL;
    switch(resp->type) // XXX preemption bits squashed
    {
        case ALP_OPCODE_RSP_TAG:
        case ALP_OPCODE_TAG:
            resp->meta.tag.id  = *p++; // PID
            resp->meta.tag.eop = !!(op & ALP_OPCODE_EOP);
            resp->meta.tag.err = !!(op & ALP_OPCODE_ERR);
            break;
        case ALP_OPCODE_RSP_F_DATA:
        case ALP_OPCODE_F_WR_DATA:
            resp->meta.f_data.fid = *p++; // FID
            resp->meta.f_data.offset = alp_decode_length(&p,&actp); // Offset
            resp->meta.f_data.length = alp_decode_length(&p,&actp); // Length
            resp->data = p;
            p += resp->meta.f_data.length;
            break;
        case ALP_OPCODE_F_RD_DATA:
            resp->meta.f_data.fid = *p++; // FID
            resp->meta.f_data.offset = alp_decode_length(&p,&actp); // Offset
            resp->meta.f_data.length = alp_decode_length(&p,&actp); // Length
            break;
        case ALP_OPCODE_F_RD_PROP:
            resp->meta.f_prop.fid = *p++; // FID
            resp->meta.f_prop.offset = 0;
            resp->meta.f_prop.length = ALP_FILE_HEADER_SIZE; // Hardcoded Length
            break;
        case ALP_OPCODE_RSP_F_PROP:
            resp->meta.f_prop.fid = *p++; // FID
            resp->meta.f_prop.offset = 0;
            resp->meta.f_prop.length = ALP_FILE_HEADER_SIZE; // Hardcoded Length
            resp->data = p;
            p += resp->meta.f_prop.length;
            break;
        case ALP_OPCODE_RSP_STATUS:
            if (op == ALP_OPCODE_RSP_ISTATUS)
            {
                resp->type = ALP_OPCODE_RSP_ISTATUS;
                resp->meta.itf.type = *p++; // ITF Type
                resp->meta.itf.length = alp_decode_length(&p,&actp); // Length
                resp->data = p;
                p += resp->meta.itf.length;
            }
            else if (op == ALP_OPCODE_RSP_EOPISTATUS)
            {
                resp->type = ALP_OPCODE_RSP_EOPISTATUS;
                resp->meta.istatus.itf = *p++; // ITF Type
                p++; // Length (always 1)
                resp->meta.istatus.err = *p++; // Interface error
            }
            else
            {
                resp->meta.status.id = *p++; // Action ID
                resp->meta.status.code = *p++; // status
            }
            break;
        case ALP_OPCODE_RSP_URC:
            resp->meta.urc.type = *p++; // Type
            resp->meta.urc.ifid = *p++; // Ifid
            if (resp->meta.urc.type == ALP_URC_TYPE_LQUAL)
            {
                resp->meta.urc.per = *p++; // Per
            }
            else if (resp->meta.urc.type == ALP_URC_TYPE_ITF_BUSY)
            {
                kal_ctf_t to;
                to.byte = *p++; // timeout
                resp->meta.urc.per = (to.byte == 0xff) ? MAX_U32 : kal_ctf_decode(to);
            }
            break;
        case ALP_OPCODE_F_DELETE:
        case ALP_OPCODE_F_FLUSH:
            resp->meta.f_data.fid = *p++; // FID
            break;
        default:
            resp->type = ALP_OPCODE_UNKNOWN;
            *payload = (u8*)NULL;
            return 0;
    }
    bytes = p - *payload;
    *payload = p;
    return bytes;
} // }}}