uint32_t fifo_pop(fifo_t* p_fifo, void* p_elem)
{
    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    if (FIFO_IS_EMPTY(p_fifo))
    {
        _ENABLE_IRQS(was_masked);
        return NRF_ERROR_NULL;
    }

    if (p_elem != NULL)
    {
        void* p_src = FIFO_ELEM_AT(p_fifo, p_fifo->tail & (p_fifo->array_len - 1));

        if (p_fifo->memcpy_fptr)
        {
            p_fifo->memcpy_fptr(p_elem, p_src);
        }
        else
        {
            memcpy(p_elem, p_src, p_fifo->elem_size);
        }
    }

    ++p_fifo->tail;

    _ENABLE_IRQS(was_masked);
    return NRF_SUCCESS;
}
uint32_t fifo_push(fifo_t* p_fifo, const void* p_elem)
{
    if (p_elem == NULL)
    {
        return NRF_ERROR_NULL;
    }
    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    if (FIFO_IS_FULL(p_fifo))
    {
        _ENABLE_IRQS(was_masked);
        return NRF_ERROR_NO_MEM;
    }

    void* p_dest = FIFO_ELEM_AT(p_fifo, p_fifo->head & (p_fifo->array_len - 1));

    if (p_fifo->memcpy_fptr)
        p_fifo->memcpy_fptr(p_dest, p_elem);
    else
        memcpy(p_dest, p_elem, p_fifo->elem_size);

    ++p_fifo->head;
    _ENABLE_IRQS(was_masked);
    return NRF_SUCCESS;
}
void mesh_flash_set_suspended(bool suspend)
{
    static uint32_t suspend_count = 0;
    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    APP_ERROR_CHECK_BOOL(suspend || suspend_count > 0);
    suspend_count += (2 * suspend - 1);
    m_suspended = (suspend_count > 0);
    _ENABLE_IRQS(was_masked);
}
void event_handler_critical_section_end(void)
{
    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    if (!--g_critical)
    {
        NVIC_EnableIRQ(QDEC_IRQn);
    }
    _ENABLE_IRQS(was_masked);
}
void event_handler_critical_section_begin(void)
{
    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    if (!g_critical++)
    {
        NVIC_DisableIRQ(QDEC_IRQn);
    }
    _ENABLE_IRQS(was_masked);
}
void serial_wait_for_completion(void)
{
    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    m_suspend = true;
    while (m_serial_state != SERIAL_STATE_IDLE)
    {
        UART0_IRQHandler();
    }
    m_suspend = false;
    _ENABLE_IRQS(was_masked);
}
bool mesh_packet_acquire(mesh_packet_t** pp_packet)
{
    uint32_t was_masked;
    for (uint32_t i = 0; i < RBC_MESH_PACKET_POOL_SIZE; ++i)
    {
        _DISABLE_IRQS(was_masked);
        if (g_packet_refs[i] == 0) /* no refs, free to use */
        {
            g_packet_refs[i] = 1;
            *pp_packet = &g_packet_pool[i];
            _ENABLE_IRQS(was_masked);
            return true;
        }
        _ENABLE_IRQS(was_masked);
    }
    APP_ERROR_CHECK(NRF_ERROR_NO_MEM);
    return false;
}
bool mesh_packet_ref_count_inc(mesh_packet_t* p_packet)
{
    /* the given pointer may not be aligned, have to force alignment with index */
    uint32_t index = PACKET_INDEX(p_packet);
    if (index >= RBC_MESH_PACKET_POOL_SIZE)
    {
        return false;
    }

    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    if (g_packet_refs[index] == 0x00 || g_packet_refs[index] == 0xFF) /* check for rollover and 0-inc */
    {
        APP_ERROR_CHECK(NRF_ERROR_NO_MEM);
    }
    g_packet_refs[index]++;
    _ENABLE_IRQS(was_masked);
    return true;
}
bool mesh_packet_ref_count_dec(mesh_packet_t* p_packet)
{
    uint32_t index = PACKET_INDEX(p_packet);
    if (index >= RBC_MESH_PACKET_POOL_SIZE)
    {
        return false;
    }

    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    /* make sure that we aren't rolling over the ref count */
    if (g_packet_refs[index] == 0x00)
    {
        APP_ERROR_CHECK(NRF_ERROR_INVALID_STATE);
        _ENABLE_IRQS(was_masked);
        return false;
    }
    g_packet_refs[index]--;
    _ENABLE_IRQS(was_masked);

    return (g_packet_refs[index] > 0);
}
uint32_t fifo_peek_at(fifo_t* p_fifo, void* p_elem, uint32_t elem)
{
    if (p_elem == NULL)
    {
        return NRF_ERROR_NULL;
    }
    uint32_t was_masked;
    _DISABLE_IRQS(was_masked);
    if (fifo_get_len(p_fifo) <= elem)
    {
        _ENABLE_IRQS(was_masked);
        return NRF_ERROR_NULL;
    }

    void* p_src = FIFO_ELEM_AT(p_fifo, (p_fifo->tail + elem) & (p_fifo->array_len - 1));

    if (p_fifo->memcpy_fptr)
        p_fifo->memcpy_fptr(p_elem, p_src);
    else
        memcpy(p_elem, p_src, p_fifo->elem_size);

    _ENABLE_IRQS(was_masked);
    return NRF_SUCCESS;
}
void bootloader_rtc_irq_handler(void)
{
    NRF_RTC0->INTENCLR = (1 << (RTC_BL_STATE_CH + RTC_INTENCLR_COMPARE0_Pos));
    switch (m_state)
    {
        case BL_STATE_FIND_FWID:
            bootloader_abort(BL_END_FWID_VALID);
            break;

        case BL_STATE_DFU_REQ:
        case BL_STATE_DFU_READY:
            bootloader_abort(BL_END_ERROR_NO_START);
            break;

        case BL_STATE_DFU_TARGET:
            start_req(m_transaction.type, true);
            break;

        case BL_STATE_VALIDATE:
            if (signature_check())
            {
                /* Don't want any interrupts disturbing this final stage */
                uint32_t was_masked;
                _DISABLE_IRQS(was_masked);

                /* write new version in bl info: */
                bl_info_entry_t new_version_entry;
                memcpy(&new_version_entry.version, m_bl_info_pointers.p_fwid, sizeof(fwid_t));
                bl_info_type_t sign_info_type = BL_INFO_TYPE_INVALID;

                if (m_bl_info_pointers.p_flags == NULL)
                {
                    APP_ERROR_CHECK(NRF_ERROR_NULL);
                }

                /* copy flags, then mark the type we just verified as intact before reflashing it. */
                bl_info_entry_t flags_entry;
                memcpy(&flags_entry, m_bl_info_pointers.p_flags, ((BL_INFO_LEN_FLAGS + 3) & ~0x03UL));

                switch (m_transaction.type)
                {
                    case DFU_TYPE_APP:
                        memcpy((void*) &new_version_entry.version.app, (void*) &m_transaction.target_fwid_union.app, DFU_FWID_LEN_APP);
                        sign_info_type = BL_INFO_TYPE_SIGNATURE_APP;
                        flags_entry.flags.app_intact = true;
                        break;
                    case DFU_TYPE_SD:
                        memcpy((void*) &new_version_entry.version.sd, (void*) &m_transaction.target_fwid_union.sd, DFU_FWID_LEN_SD);
                        sign_info_type = BL_INFO_TYPE_SIGNATURE_SD;
                        flags_entry.flags.sd_intact = true;
                        break;
                    case DFU_TYPE_BOOTLOADER:
                        memcpy((void*) &new_version_entry.version.bootloader, (void*) &m_transaction.target_fwid_union.bootloader, DFU_FWID_LEN_BL);
                        sign_info_type = BL_INFO_TYPE_SIGNATURE_BL;
                        flags_entry.flags.bl_intact = true;
                        break;
                    default:
                        break;
                }
                m_bl_info_pointers.p_fwid = &bootloader_info_entry_put(BL_INFO_TYPE_VERSION, &new_version_entry, BL_INFO_LEN_FWID)->version;
                m_bl_info_pointers.p_flags = &bootloader_info_entry_put(BL_INFO_TYPE_FLAGS, &flags_entry, BL_INFO_LEN_FLAGS)->flags;

                /* add signature to bl info, if applicable: */
                if (m_transaction.signature_length != 0)
                {
                    bootloader_info_entry_put(sign_info_type, (bl_info_entry_t*) m_transaction.signature, DFU_SIGNATURE_LEN);
                }

                _ENABLE_IRQS(was_masked);

                bootloader_abort(BL_END_SUCCESS);
            }
            else
            {
                /* someone gave us anauthorized firmware, and we're broken.
                   need to reboot and try to request a new transfer */
                bootloader_abort(BL_END_ERROR_UNAUTHORIZED);
            }
            break;
        case BL_STATE_RELAY:
        case BL_STATE_RELAY_CANDIDATE:
            bootloader_abort(BL_END_SUCCESS);
            break;
        default:
            break;
    }
}