Exportable version of WizziLab's modem driver.

Dependents:   modem_ref_helper

src/alp_payload.cpp

Committer:
marin_wizzi
Date:
2021-10-29
Revision:
67:e458db8402dc
Parent:
65:c7749f9b6009

File content as of revision 67:e458db8402dc:

/// @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 helpers functions.
//                  This code should be disclosable in source.
//  ====================================================================


#include "alp.h"
#include "alp_dbg.h"
#include "kal_math.h"
#include "kal_crypto.h"
#include "d7a_1x_fs.h"

#include "WizziDebug.h"

static int32_t g_payload_nb = 0;

alp_payload_t* alp_payload_new(u32 size)
{
    alp_payload_t* alp = (alp_payload_t*)MALLOC(sizeof(alp_payload_t) - 1 + size);

    alp->len = 0;
    alp->next = NULL;
    
    g_payload_nb++;

    return alp;
}

void alp_payload_free(alp_payload_t* alp)
{
    while (NULL != alp)
    {
        alp_payload_t* alp_next = alp->next;   
        FREE(alp);
        
        g_payload_nb--;

        alp = alp_next;
    }
}

void alp_payload_print_nb(void)
{
    PRINT("ALP: %d payloads assigned\n", g_payload_nb);
}

//======================================================================
// alp_payload_append
//----------------------------------------------------------------------
/// @brief  Appends ALP payload 2 after payload 1
/// @param  alp_payload_t*          Payload 1
/// @param  alp_payload_t*          Payload 2
/// @return alp_payload_t*          Appended payloads
/// @note: XXX payloads MUST be malloced
//======================================================================
alp_payload_t* alp_payload_append(alp_payload_t* alp_1, alp_payload_t* alp_2)
{
    alp_payload_t* alp = alp_1;
    
    if (NULL == alp_1)
    {
        return alp_2;
    }

    if (NULL == alp_2)
    {
        return alp_1;
    }
    
    while (NULL != alp)
    {
        if (NULL == alp->next)
        {
            alp->next = alp_2;
            return alp_1;
        }
        
        alp = alp->next;
    }

    return alp_1;
}

void alp_payload_print(alp_payload_t* alp)
{
    u32 action = 0;
    u32 i;
    
    while (NULL != alp)
    {
        PRINT("ALP[%d]: (0x%x)", action, alp);
        PRINT_DATA("", " %02X", alp->d, alp->len, "\n");
        FLUSH();
        
        action++;
        
        alp = alp->next;
    }
    
    PRINT("---\n");
}

u32 alp_payload_to_buf(alp_payload_t* alp, u8* buf, u32 max)
{
    u32 offset = 0;
    
    while (NULL != alp)
    {
        // End of payload
        ALP_ASSERT((offset + alp->len) <= max, "ALP payload too big for buffer (%d\%d)\n", offset + alp->len, max);
        
        // Copy into buffer
        memcpy(&(buf[offset]), alp->d, alp->len);
        offset += alp->len;
        
        alp = alp->next;
    }
    
    return offset;
}

//======================================================================
// alp_payload_root_auth
//----------------------------------------------------------------------
/// @brief  Add the root access request to the given payload using the authentication protocol
/// @param alp          alp_payload_t*          Payload
/// @return             alp_payload_t*          Root access request + payload
/// @note: XXX payloads MUST be malloced
//======================================================================
alp_payload_t* alp_payload_root_auth(alp_payload_t* alp, u8* root_key)
{
    u8* p;
    alp_payload_t* new_alp;
    alp_payload_t* alp_hash = alp;
    u8 hash[32];

    // Calculate hash on payload
    kal_sha256_init();
    while (NULL != alp_hash)
    {
        kal_sha256_update(alp_hash->d, alp_hash->len);
        alp_hash = alp_hash->next;
    }
    kal_sha256_update(root_key, D7A_FS_ROOT_KEY_SIZE);
    kal_sha256_final(hash);

    new_alp = alp_payload_new(ALP_ACTION_PERM_REQ_SIZE(0));

    p = new_alp->d;
    ALP_ACTION_PERM_REQ(p, FALSE, ALP_PERM_REQ_ROOT, ALP_WIZZILAB_AUTH_PROTOCOL_ID);

    memcpy(p, hash, ALP_ACTION_PERM_REQ_TOKEN_SIZE);
    p += ALP_ACTION_PERM_REQ_TOKEN_SIZE;

    new_alp->len = ALP_ACTION_PERM_REQ_SIZE(0);

    // XXX Prepend
    return alp_payload_append(new_alp, alp);
}

//======================================================================
// alp_payload_root_sign
//----------------------------------------------------------------------
/// @brief  Sign payload with root key
/// @param alp          alp_payload_t*          Payload
/// @param iv           u8*                         Initialization vector
/// @return             alp_payload_t*          Root access request + payload
/// @note: XXX payloads MUST be malloced
//======================================================================
alp_payload_t* alp_payload_root_sign(alp_payload_t* alp, u8* root_key, u8* iv)
{
    u8* p;
    alp_payload_t* new_alp;
    alp_payload_t* alp_hash = alp;
    u8 hash[32];

    // Calculate hash on payload
    kal_sha256_init();
    while (NULL != alp_hash)
    {
        kal_sha256_update(alp_hash->d, alp_hash->len);
        alp_hash = alp_hash->next;
    }
    kal_sha256_update(root_key, D7A_FS_ROOT_KEY_SIZE);
    kal_sha256_update(iv, ALP_ACTION_SECURED_IV_SIZE);
    kal_sha256_final(hash);

    new_alp = alp_payload_new(ALP_ACTION_SECURED_SIZE(0));

    p = new_alp->d;
    ALP_ACTION_SECURED(p, ALP_SECURED_ROOT, ALP_WIZZILAB_SIGN_PROTOCOL_ID);

    memcpy(p, iv, ALP_ACTION_SECURED_IV_SIZE);
    p += ALP_ACTION_SECURED_IV_SIZE;

    memcpy(p, hash, ALP_ACTION_SECURED_TOKEN_SIZE);
    p += ALP_ACTION_SECURED_TOKEN_SIZE;

    new_alp->len = ALP_ACTION_SECURED_SIZE(0);

    // XXX Prepend
    return alp_payload_append(new_alp, alp);
}

#if 0
//======================================================================
// alp_payload_root_auth_enc
//----------------------------------------------------------------------
/// @brief  Add the root access request to the given payload and encrypt using the challenge protocol
/// @param alp          alp_payload_t*          Payload
/// @param challenge    u8*                         32-byte challenge
/// @return             alp_payload_t*          Root access request + encrypted payload
/// @note: XXX payloads MUST be malloced
//======================================================================
alp_payload_t* alp_payload_root_auth_enc(alp_payload_t* alp, u8* root_key, u8* challenge)
{
    u8* p;
    alp_payload_t* new_alp;
    alp_payload_t* alp_hash = alp;
    u8 hash[32];

    // encrypt
    u8 key[D7A_FS_ROOT_KEY_SIZE];
    memcpy(key, root_key, D7A_FS_ROOT_KEY_SIZE);
    kal_xor(key, challenge, D7A_FS_ROOT_KEY_SIZE);
    kal_aes128_init(key);
    kal_aes128_ctr(alp->d, alp->len, challenge + D7A_FS_ROOT_KEY_SIZE, TRUE);

    // Calculate hash on payload
    kal_sha256_init();
    while (NULL != alp_hash)
    {
        kal_sha256_update(alp_hash->d, alp_hash->len);
        alp_hash = alp_hash->next;
    }
    kal_sha256_update(root_key, D7A_FS_ROOT_KEY_SIZE);
    kal_sha256_update(challenge, 32);
    kal_sha256_final(hash);

    new_alp = alp_payload_new(ALP_ACTION_PERM_REQ_SIZE(0));

    p = new_alp->d;

    ALP_ACTION_PERM_REQ(p, FALSE, ALP_PERM_REQ_ROOT, ALP_WIZZILAB_CHAL_PROTOCOL_ID);
    memcpy(p, hash, ALP_ACTION_PERM_REQ_TOKEN_SIZE);
    p += ALP_ACTION_PERM_REQ_TOKEN_SIZE;

    new_alp->len = ALP_ACTION_PERM_REQ_SIZE(0);

    // XXX Prepend
    return alp_payload_append(new_alp, alp);
}
#endif

//======================================================================
// alp_payload_rsp_f_data
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  fid             u8                  file identifier
/// @param  data            void*               file content
/// @param  offset          u32                 file offset
/// @param  length          u32                 file length
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_rsp_f_data(alp_payload_t* alp, u8 fid, void* data, u32 offset, u32 length)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_RSP_F_DATA_SIZE(offset, length));

    p = new_alp->d;

    ALP_ACTION_RSP_F_DATA(p, fid, offset, length, data);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_qbreak
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  fid             u8                  file identifier
/// @param  data            void*               file content
/// @param  offset          u32                 file offset
/// @param  length          u32                 file length
/// @param  group           u8                  group with next OP
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_qbreak(alp_payload_t* alp, u8 fid, void* data, u32 offset, u32 length, u8 group)
{
    u8* p;
    alp_payload_t* new_alp;
    u8 mask = 0;

    new_alp = alp_payload_new(ALP_ACTION_QBREAK_SIZE(offset, length, mask));

    p = new_alp->d;

    ALP_ACTION_QBREAK_STRTOK(p, mask, data, fid, offset, length, group);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

static alp_payload_t* _alp_payload_qbreak_comp(alp_payload_t* alp, u8 fid, u32 mask, u32 data, u32 offset, u32 length, u8 group, u8 comp)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_QBREAK_SIZE(offset, length, (mask)? 1 : 0));

    p = new_alp->d;

    ALP_ACTION_QBREAK_COMP(p, mask, data, comp, fid, offset, length, group);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

alp_payload_t* alp_payload_qbreak_eq(alp_payload_t* alp, u8 fid, u32 data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_qbreak_comp(alp, fid, 0, data, offset, length, group, ALP_QCOMP_EQ);
}

alp_payload_t* alp_payload_qbreak_ne(alp_payload_t* alp, u8 fid, u32 data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_qbreak_comp(alp, fid, 0, data, offset, length, group, ALP_QCOMP_NE);
}

alp_payload_t* alp_payload_qbreak_gt(alp_payload_t* alp, u8 fid, u32 data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_qbreak_comp(alp, fid, 0, data, offset, length, group, ALP_QCOMP_GT);
}

alp_payload_t* alp_payload_qbreak_gte(alp_payload_t* alp, u8 fid, u32 data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_qbreak_comp(alp, fid, 0, data, offset, length, group, ALP_QCOMP_GTE);
}

alp_payload_t* alp_payload_qbreak_lt(alp_payload_t* alp, u8 fid, u32 data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_qbreak_comp(alp, fid, 0, data, offset, length, group, ALP_QCOMP_LT);
}

alp_payload_t* alp_payload_qbreak_lte(alp_payload_t* alp, u8 fid, u32 data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_qbreak_comp(alp, fid, 0, data, offset, length, group, ALP_QCOMP_LTE);
}

alp_payload_t* alp_payload_qbreak_eq_msk(alp_payload_t* alp, u8 fid, u32 mask, u32 data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_qbreak_comp(alp, fid, mask, data, offset, length, group, ALP_QCOMP_EQ);
}

//======================================================================
// alp_payload_query
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  fid             u8                  file identifier
/// @param  data            void*               file content
/// @param  offset          u32                 file offset
/// @param  length          u32                 file length
/// @param  group           u8                  group with next OP
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_query(alp_payload_t* alp, u8 fid, void* data, u32 offset, u32 length, u8 group)
{
    u8* p;
    alp_payload_t* new_alp;
    u8 mask = 0;

    new_alp = alp_payload_new(ALP_ACTION_QUERY_SIZE(offset, length, mask));

    p = new_alp->d;

    ALP_ACTION_QUERY_STRTOK(p, mask, data, fid, offset, length, group);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_nop
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_nop(alp_payload_t* alp, u8 group)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_NOP_SIZE);

    p = new_alp->d;

    ALP_ACTION_NOP(p, true, group); // XXX RESP always true. If we use NOP, it's to get a response (istatus).

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_f_touch
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_f_touch(alp_payload_t* alp, u8 fid, u32 offset, u32 length, u8 group)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_F_TOUCH_SIZE(offset, length));

    p = new_alp->d;

    ALP_ACTION_F_TOUCH(p, true, fid, offset, length, group);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_f_wr_data
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  fid             u8                  file identifier
/// @param  data            void*               file content
/// @param  offset          u32                 file offset
/// @param  length          u32                 file length
/// @param  group           u8                  group with next OP
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
static alp_payload_t* _alp_payload_f_wr_data_resp(alp_payload_t* alp, u8 fid, void* data, u32 offset, u32 length, u8 group, u8 resp)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_F_WR_DATA_SIZE(offset, length));

    p = new_alp->d;

    ALP_ACTION_F_WR_DATA(p, resp, fid, offset, length, data, group);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

alp_payload_t* alp_payload_f_wr_data(alp_payload_t* alp, u8 fid, void* data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_f_wr_data_resp(alp, fid, data, offset, length, group, true);
}

alp_payload_t* alp_payload_f_wr_data_nr(alp_payload_t* alp, u8 fid, void* data, u32 offset, u32 length, u8 group)
{
    return _alp_payload_f_wr_data_resp(alp, fid, data, offset, length, group, false);
}

//======================================================================
// alp_payload_f_rd_data
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  fid             u8                  file identifier
/// @param  offset          u32                 file offset
/// @param  length          u32                 file length
/// @param  group           u8                  group with next OP
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_f_rd_data(alp_payload_t* alp, u8 fid, u32 offset, u32 length, u8 group)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_F_RD_DATA_SIZE(offset, length));

    p = new_alp->d;

    ALP_ACTION_F_RD_DATA(p, true, fid, offset, length, group);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_f_flush
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  fid             u8                  file identifier
/// @param  group           u8                  group with next OP
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_f_flush(alp_payload_t* alp, u8 fid, u8 group)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_F_FLUSH_SIZE);

    p = new_alp->d;

    ALP_ACTION_F_FLUSH(p, true, fid, group);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}


//======================================================================
// alp_payload_f_flush
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  fid             u8                  file identifier
/// @param  group           u8                  group with next OP
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_f_declare(alp_payload_t* alp, u8 fid, alp_file_header_t* hdr)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_F_DECLARE_SIZE);

    p = new_alp->d;

    ALP_ACTION_F_DECLARE(p, true, fid, hdr, true);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_forward
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  itf             void*               Forward interface
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_forward(alp_payload_t* alp, void* itf)
{
    u8* p;
    alp_payload_t* new_alp;
    u32 itf_length = alp_itf_size(itf);

    new_alp = alp_payload_new(ALP_ACTION_FORWARD_SIZE(itf_length));

    p = new_alp->d;

    ALP_ACTION_FORWARD(p, itf, itf_length);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_tag
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  tag             u8                  Tag value
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_tag(alp_payload_t* alp, u8 tag)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_TAG_SIZE);

    p = new_alp->d;

    ALP_ACTION_TAG(p, tag, true);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_tag
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  tag             u8                  Tag value
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_rsp_tag(alp_payload_t* alp, u8 tag, u8 eop, u8 err)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_RSP_TAG_SIZE);

    p = new_alp->d;

    ALP_ACTION_RSP_TAG(p, tag, eop, err);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_tag
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  tag             u8                  Action index
/// @param  tag             s8                  Status
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_rsp_status(alp_payload_t* alp, u8 action, s8 status)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_RSP_STATUS_SIZE);

    p = new_alp->d;

    ALP_ACTION_RSP_STATUS(p, action, status);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

//======================================================================
// alp_payload_tag
//----------------------------------------------------------------------
/// @brief  Creates malloc'ed ALP payload
/// @param  alp             alp_payload_t*  Payload to append the ALP message to. A new payload will be malloc'ed if NULL
/// @param  tag             u8                  Action index
/// @param  tag             s8                  Status
/// @return                 alp_payload_t*  New ALP payload
/// @revent                 NONE
//======================================================================
alp_payload_t* alp_payload_rsp_fprop(alp_payload_t* alp, u8 fid, alp_file_header_t* hdr)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_RSP_F_PROP_SIZE);

    p = new_alp->d;

    ALP_ACTION_RSP_F_PROP(p, fid, hdr);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

alp_payload_t* alp_payload_activate_itf(alp_payload_t* alp, u8 type, u8 nb_dev, u8 ifid, u8 flags, u8 enable)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_ACTIVATE_ITF_SIZE);

    p = new_alp->d;

    ALP_ACTION_ACTIVATE_ITF(p, true, enable, type, nb_dev, ifid, flags);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

alp_payload_t* alp_payload_urcc_en(alp_payload_t* alp, u8 type, u8 ifid, u8 val)
{
    u8* p;
    alp_payload_t* new_alp;

    new_alp = alp_payload_new(ALP_ACTION_ACTIVATE_ITF_SIZE);

    p = new_alp->d;

    ALP_ACTION_URCC_EN(p, true, type, ifid, val);

    new_alp->len = (u32)(p - new_alp->d);

    return alp_payload_append(alp, new_alp);
}

alp_payload_t* alp_payload_extract(alp_payload_t** alp, u8 op)
{
    if (NULL == *alp)
    {
        return NULL;
    }

    if ((*alp)->d[0] == op)
    {
        // Save alp
        alp_payload_t* alp_old = *alp;

        // Remove it from chain
        *alp = alp_old->next;
        alp_old->next = NULL;

        // Return it
        return alp_old;
    }

    return alp_payload_extract((alp_payload_t**)&((*alp)->next), op);
}

alp_payload_t* alp_payload_get(alp_payload_t* alp, u8 op)
{
    while (NULL != alp)
    {
        if (alp->d[0] == op)
        {
            // Return it
            return alp;
        }
        
        alp = alp->next;
    }
    
    return NULL;
}

bool alp_payload_extract_data(alp_payload_t** alp, u8 op, void* data)
{
    alp_payload_t* alp_data = alp_payload_extract(alp, op);

    if (alp_data)
    {
        alp_parsed_chunk_t r;
        
        alp_payload_parse_chunk(alp_data, &r);
    
        switch (op)
        {
            case ALP_OPCODE_RSP_ISTATUS:
                memcpy(data, r.data, r.meta.itf.length);
                break;;
            case ALP_OPCODE_RSP_F_DATA:
                memcpy(data, r.data, r.meta.f_data.length);
                break;
            default:
                alp_payload_free(alp_data);
                return false;
                break;
        }
    
        alp_payload_free(alp_data);
        
        return true;
    }
    
    return false;
}

enum {
    ERR_PRIO_EOPISTATUS,
    ERR_PRIO_STATUS,
    ERR_PRIO_TAG,
    ERR_PRIO_NONE,
};

int alp_payload_get_err(alp_payload_t* alp)
{
    int err = ALP_ERR_NONE;
    uint8_t err_prio = ERR_PRIO_NONE;
    alp_parsed_chunk_t r;

    while (NULL != alp)
    {
        switch (alp->d[0])
        {
        case ALP_OPCODE_RSP_TAG: // Fallthrough
        case ALP_OPCODE_RSP_EOPTAG: // Fallthrough
        case ALP_OPCODE_RSP_ERRTAG:
        {
            alp_payload_parse_chunk(alp, &r);
            if (ERR_PRIO_TAG < err_prio && r.meta.tag.err)
            {
                err = ALP_ERR_UNKNOWN;
                err_prio = ERR_PRIO_TAG;
            }
            break;
        }
        case ALP_OPCODE_RSP_STATUS:
        {
            alp_payload_parse_chunk(alp, &r);
            if (ERR_PRIO_STATUS < err_prio && r.meta.status.code < ALP_ERR_NONE)
            {
                err = r.meta.status.code;
                err_prio = ERR_PRIO_STATUS;
            }
            break;
        }
        case ALP_OPCODE_RSP_EOPISTATUS:
        {
            alp_payload_parse_chunk(alp, &r);
            if (ERR_PRIO_EOPISTATUS < err_prio && r.meta.istatus.err < ALP_ERR_NONE)
            {
                err = r.meta.istatus.err + ALP_ERR_ITF_START;
                err_prio = ERR_PRIO_EOPISTATUS;
            }
            break;
        }
        default:
            break;
        }
        
        alp = alp->next;
    }

    return err;
}

int alp_payload_parse_chunk(alp_payload_t* alp, alp_parsed_chunk_t* r)
{
    if (NULL == alp)
    {
        return 0;
    }
    
    uint8_t* p = alp->d;
    return alp_parse_chunk(&p, r);
}

alp_payload_t* alp_payload_parse(u8* d, int length)
{
    u8* data_start;
    int len;
    alp_parsed_chunk_t r;
    alp_payload_t* alp = NULL;
    alp_payload_t* alp_new = NULL;
    
    while (length > 0)
    {
        data_start = d;
        len = (int)alp_parse_chunk(&d, &r);
        
        // Malloc payload
        alp_new = alp_payload_new(len);
    
        // Fill payload
        alp_new->len = len;
        memcpy(alp_new->d, data_start, len);
        
        alp = alp_payload_append(alp, alp_new);
        
        length -= len;
    }

    return alp;
}