/* async.c
 *
 * Copyright (C) 2006-2026 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

#include <wolfssl/wolfcrypt/libwolfssl_sources.h>

#ifdef WOLFSSL_ASYNC_CRYPT

#include <wolfssl/internal.h>
#include <wolfssl/error-ssl.h>

#include <wolfssl/wolfcrypt/async.h>


static WC_ASYNC_DEV* wolfAsync_GetDev(WOLF_EVENT* event)
{
    WC_ASYNC_DEV* dev = NULL;

    if (event && event->context) {
        switch (event->type) {
            /* context is WOLFSSL* */
            case WOLF_EVENT_TYPE_ASYNC_WOLFSSL:
            {
                WOLFSSL* ssl = (WOLFSSL*)event->context;
                dev = ssl->asyncDev;
                break;
            }

            /* context is WC_ASYNC_DEV */
            case WOLF_EVENT_TYPE_ASYNC_WOLFCRYPT:
            {
                dev = (WC_ASYNC_DEV*)event->context;
                break;
            }

            case WOLF_EVENT_TYPE_NONE:
            default:
                WOLFSSL_MSG("Unhandled event->type context!");
                dev = NULL;
                break;
        }
    }

    return dev;
}


#if defined(WOLFSSL_ASYNC_CRYPT_SW)

/* Allow way to have async SW code included, and disabled at run-time */
static int wolfAsyncSwDisabled = 0; /* default off */


static int wolfAsync_DoSw(WC_ASYNC_DEV* asyncDev)
{
    int ret = 0;
    WC_ASYNC_SW* sw;

    if (asyncDev == NULL) {
        return BAD_FUNC_ARG;
    }
    sw = &asyncDev->sw;

    switch (sw->type) {
#ifdef HAVE_ECC
        case ASYNC_SW_ECC_MAKE:
        {
            ret = wc_ecc_make_key_ex(
                (WC_RNG*)sw->eccMake.rng,
                sw->eccMake.size,
                (ecc_key*)sw->eccMake.key,
                sw->eccMake.curve_id
            );
            break;
        }
    #ifdef HAVE_ECC_SIGN
        case ASYNC_SW_ECC_SIGN:
        {
            ret = wc_ecc_sign_hash_ex(
                sw->eccSign.in,
                sw->eccSign.inSz,
                (WC_RNG*)sw->eccSign.rng,
                (ecc_key*)sw->eccSign.key,
                (mp_int*)sw->eccSign.r,
                (mp_int*)sw->eccSign.s
            );
            break;
        }
    #endif /* HAVE_ECC_SIGN */
    #ifdef HAVE_ECC_VERIFY
        case ASYNC_SW_ECC_VERIFY:
        {
            ret = wc_ecc_verify_hash_ex(
                (mp_int*)sw->eccVerify.r,
                (mp_int*)sw->eccVerify.s,
                sw->eccVerify.hash,
                sw->eccVerify.hashlen,
                sw->eccVerify.stat,
                (ecc_key*)sw->eccVerify.key
            );
            break;
        }
    #endif /* HAVE_ECC_VERIFY */
    #ifdef HAVE_ECC_DHE
        case ASYNC_SW_ECC_SHARED_SEC:
        {
            ret = wc_ecc_shared_secret_gen_sync(
                (ecc_key*)sw->eccSharedSec.private_key,
                (ecc_point*)sw->eccSharedSec.public_point,
                sw->eccSharedSec.out,
                sw->eccSharedSec.outLen
            );
            break;
        }
    #endif /* HAVE_ECC_DHE */
#endif /* HAVE_ECC */
#ifndef NO_RSA
    #ifdef WOLFSSL_KEY_GEN
        case ASYNC_SW_RSA_MAKE:
        {
            ret = wc_MakeRsaKey(
                (RsaKey*)sw->rsaMake.key,
                sw->rsaMake.size,
                sw->rsaMake.e,
                (WC_RNG*)sw->rsaMake.rng
            );
            break;
        }
    #endif /* WOLFSSL_KEY_GEN */
        case ASYNC_SW_RSA_FUNC:
        {
            ret = wc_RsaFunction(
                sw->rsaFunc.in,
                sw->rsaFunc.inSz,
                sw->rsaFunc.out,
                sw->rsaFunc.outSz,
                sw->rsaFunc.type,
                (RsaKey*)sw->rsaFunc.key,
                (WC_RNG*)sw->rsaFunc.rng
            );
            break;
        }
#endif /* !NO_RSA */
#ifndef NO_DH
        case ASYNC_SW_DH_AGREE:
        {
            ret = wc_DhAgree(
                (DhKey*)sw->dhAgree.key,
                sw->dhAgree.agree,
                sw->dhAgree.agreeSz,
                sw->dhAgree.priv,
                sw->dhAgree.privSz,
                sw->dhAgree.otherPub,
                sw->dhAgree.pubSz
            );
            break;
        }
        case ASYNC_SW_DH_GEN:
        {
            ret = wc_DhGenerateKeyPair(
                (DhKey*)sw->dhGen.key,
                (WC_RNG*)sw->dhGen.rng,
                sw->dhGen.priv,
                sw->dhGen.privSz,
                sw->dhGen.pub,
                sw->dhGen.pubSz
            );
            break;
        }
#endif /* !NO_DH */
#ifndef NO_AES
        case ASYNC_SW_AES_CBC_ENCRYPT:
        {
            ret = wc_AesCbcEncrypt(
                (Aes*)sw->aes.aes,
                sw->aes.out,
                sw->aes.in,
                sw->aes.sz
            );
            break;
        }
    #ifdef HAVE_AES_DECRYPT
        case ASYNC_SW_AES_CBC_DECRYPT:
        {
            ret = wc_AesCbcDecrypt(
                (Aes*)sw->aes.aes,
                sw->aes.out,
                sw->aes.in,
                sw->aes.sz
            );
            break;
        }
    #endif /* HAVE_AES_DECRYPT */

    #ifdef HAVE_AESGCM
        case ASYNC_SW_AES_GCM_ENCRYPT:
        {
            ret = wc_AesGcmEncrypt(
                (Aes*)sw->aes.aes,
                sw->aes.out,
                sw->aes.in,
                sw->aes.sz,
                sw->aes.iv,
                sw->aes.ivSz,
                sw->aes.authTag,
                sw->aes.authTagSz,
                sw->aes.authIn,
                sw->aes.authInSz
            );
            break;
        }
        #ifdef HAVE_AES_DECRYPT
        case ASYNC_SW_AES_GCM_DECRYPT:
        {
            ret = wc_AesGcmDecrypt(
                (Aes*)sw->aes.aes,
                sw->aes.out,
                sw->aes.in,
                sw->aes.sz,
                sw->aes.iv,
                sw->aes.ivSz,
                sw->aes.authTag,
                sw->aes.authTagSz,
                sw->aes.authIn,
                sw->aes.authInSz
            );
            break;
        }
        #endif /* HAVE_AES_DECRYPT */
    #endif /* HAVE_AESGCM */
#endif /* !NO_AES */
#ifndef NO_DES3
        case ASYNC_SW_DES3_CBC_ENCRYPT:
        {
            ret = wc_Des3_CbcEncrypt(
                (Des3*)sw->des.des,
                sw->des.out,
                sw->des.in,
                sw->des.sz
            );
            break;
        }
        case ASYNC_SW_DES3_CBC_DECRYPT:
        {
            ret = wc_Des3_CbcDecrypt(
                (Des3*)sw->des.des,
                sw->des.out,
                sw->des.in,
                sw->des.sz
            );
            break;
        }
#endif /* !NO_DES3 */
#ifdef HAVE_CURVE25519
        case ASYNC_SW_X25519_MAKE:
        {
            ret = wc_curve25519_make_key(
                (WC_RNG*)sw->x25519Make.rng,
                sw->x25519Make.size,
                (curve25519_key*)sw->x25519Make.key
            );
            break;
        }
        case ASYNC_SW_X25519_SHARED_SEC:
        {
            ret = wc_curve25519_shared_secret_ex(
                (curve25519_key*)sw->x25519SharedSec.priv,
                (curve25519_key*)sw->x25519SharedSec.pub,
                sw->x25519SharedSec.out,
                sw->x25519SharedSec.outLen,
                sw->x25519SharedSec.endian
            );
            break;
        }
#endif /* HAVE_CURVE25519 */
        default:
            WOLFSSL_MSG("Invalid async crypt SW type!");
            ret = BAD_FUNC_ARG;
            break;
    };

    /* Reset test type */
    if (ret == FP_WOULDBLOCK) {
        ret = WC_PENDING_E;
    }
    else if (ret == 0) {
        sw->type = ASYNC_SW_NONE;
    }

    return ret;
}

int wc_AsyncSwInit(WC_ASYNC_DEV* dev, int type)
{
    if (dev) {
        WC_ASYNC_SW* sw = &dev->sw;
        if (sw->type == ASYNC_SW_NONE) {
            sw->type = type;
            return 1;
        }
    }
    return 0;
}

#endif /* WOLFSSL_ASYNC_CRYPT_SW */

int wolfAsync_DevOpenThread(int *pDevId, void* threadId)
{
    int ret = 0;
    int devId = INVALID_DEVID;

#ifdef HAVE_CAVIUM
    ret = NitroxOpenDeviceDefault();
    if (ret >= 0)
        devId = ret;
    else
        ret = ASYNC_INIT_E;
#elif defined(HAVE_INTEL_QA)
    ret = IntelQaInit(threadId);
    if (ret >= 0)
        devId = ret;
    else
        ret = ASYNC_INIT_E;
#elif defined(WOLFSSL_ASYNC_CRYPT_SW)
    if (!wolfAsyncSwDisabled) {
        /* For SW use any value 0 or greater */
        devId = 0;
    }
#endif

    (void)threadId;

    /* return devId if requested */
    if (*pDevId)
        *pDevId = devId;

    return ret;
}

int wolfAsync_HardwareStart(void)
{
    int ret = 0;

    #ifdef HAVE_CAVIUM
        /* nothing to do */
    #elif defined(HAVE_INTEL_QA)
        ret = IntelQaHardwareStart(QAT_PROCESS_NAME, QAT_LIMIT_DEV_ACCESS);
    #endif

    return ret;
}

void wolfAsync_HardwareStop(void)
{
    #ifdef HAVE_CAVIUM
        /* nothing to do */
    #elif defined(HAVE_INTEL_QA)
        IntelQaHardwareStop();
    #endif
}

int wolfAsync_DevOpen(int *devId)
{
    return wolfAsync_DevOpenThread(devId, NULL);
}

void wolfAsync_DevClose(int *devId)
{
    if (devId && *devId != INVALID_DEVID) {
    #ifdef HAVE_CAVIUM
        NitroxCloseDevice(*devId);
    #elif defined(HAVE_INTEL_QA)
        IntelQaDeInit(*devId);
    #endif
        *devId = INVALID_DEVID;
    }
}

int wolfAsync_DevCtxInit(WC_ASYNC_DEV* asyncDev, word32 marker, void* heap,
    int devId)
{
    int ret = 0;

    if (asyncDev == NULL) {
        return BAD_FUNC_ARG;
    }

    /* always clear async device context */
    XMEMSET(asyncDev, 0, sizeof(WC_ASYNC_DEV));

    /* negative device Id's are invalid */
    if (devId >= 0) {
        asyncDev->marker = marker;
        asyncDev->heap = heap;

    #ifdef HAVE_CAVIUM
        ret = NitroxAllocContext(asyncDev, devId, CONTEXT_SSL);
    #elif defined(HAVE_INTEL_QA)
        ret = IntelQaOpen(asyncDev, devId);
    #endif
    }

    return ret;
}

void wolfAsync_DevCtxFree(WC_ASYNC_DEV* asyncDev, word32 marker)
{
    if (asyncDev && asyncDev->marker == marker) {
    #ifdef HAVE_CAVIUM
        NitroxFreeContext(asyncDev);
    #elif defined(HAVE_INTEL_QA)
        IntelQaClose(asyncDev);
    #endif
        asyncDev->marker = WOLFSSL_ASYNC_MARKER_INVALID;
    }
}

int wolfAsync_DevCopy(WC_ASYNC_DEV* src, WC_ASYNC_DEV* dst)
{
    int ret = 0;

    if (src == NULL || dst == NULL)
        return BAD_FUNC_ARG;

    /* make sure we aren't copying to self */
    if (src == dst)
        return ret;

#ifdef HAVE_CAVIUM
    /* nothing to do here */
#elif defined(HAVE_INTEL_QA)
    ret = IntelQaDevCopy(src, dst);
#endif

    return ret;
}

/* called from `wolfSSL_AsyncPop` to check if event is done and deliver
 * async return code */
int wolfAsync_EventPop(WOLF_EVENT* event, enum WOLF_EVENT_TYPE event_type)
{
    int ret;

    if (event == NULL) {
        return BAD_FUNC_ARG;
    }

    if (event->type == event_type) {
        /* Trap the scenario where event is not done */
        if (event->state == WOLF_EVENT_STATE_PENDING) {
            return WC_PENDING_E;
        }

        /* Get async return code */
        ret = event->ret;

        /* Reset state */
        event->state = WOLF_EVENT_STATE_READY;
    }
    else {
        ret = WC_NO_PENDING_E;
    }

    return ret;
}

int wolfAsync_EventQueuePush(WOLF_EVENT_QUEUE* queue, WOLF_EVENT* event)
{
    if (queue == NULL) {
        return BAD_FUNC_ARG;
    }

    /* Setup event and push to event queue */
    event->dev.async = wolfAsync_GetDev(event);
    return wolfEventQueue_Push(queue, event);
}

#ifdef HAVE_CAVIUM
static int wolfAsync_NitroxCheckReq(WC_ASYNC_DEV* asyncDev, WOLF_EVENT* event)
{
    int ret;

    /* populate event requestId */
    event->reqId = asyncDev->nitrox.reqId;
    if (event->reqId == 0)
        return WC_INIT_E;

    /* poll specific request */
    ret = NitroxCheckRequest(asyncDev, event);

#ifdef WOLFSSL_NITROX_DEBUG
    if (event->ret == WC_NO_ERR_TRACE(WC_PENDING_E))
        event->pendCount++;
    else
        printf("NitroxCheckRequest: ret %x, req %lx, count %u\n",
            ret,
            event->reqId,
            event->pendCount);
#else
    (void)ret;
#endif

    /* if not pending then clear requestId */
    if (event->ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
        event->reqId = 0;
    }

    return 0;
}
#endif /* HAVE_CAVIUM */

int wolfAsync_EventPoll(WOLF_EVENT* event, WOLF_EVENT_FLAG flags)
{
    int ret = 0;
    WC_ASYNC_DEV* asyncDev;

    (void)flags;

    if (event == NULL) {
        return BAD_FUNC_ARG;
    }
    asyncDev = event->dev.async;
    if (asyncDev == NULL) {
        return WC_INIT_E;
    }

    if (flags & WOLF_POLL_FLAG_CHECK_HW) {
    #if defined(HAVE_CAVIUM)
        ret = wolfAsync_NitroxCheckReq(asyncDev, event);
    #elif defined(HAVE_INTEL_QA)
        /* poll QAT hardware, callback returns data, IntelQaPoll sets event */
        ret = IntelQaPoll(asyncDev);
    #elif defined(WOLFSSL_ASYNC_CRYPT_SW)
        event->ret = wolfAsync_DoSw(asyncDev);
    #endif

        /* If not pending then mark as done */
        if (event->ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
            event->state = WOLF_EVENT_STATE_DONE;
        }
    }

    return ret;
}


#ifdef HAVE_CAVIUM
static int wolfAsync_NitroxCheckMultiReqBuf(WC_ASYNC_DEV* asyncDev,
    WOLF_EVENT_QUEUE* queue, void* context_filter,
    CspMultiRequestStatusBuffer* multi_req, int req_count)
{
    WOLF_EVENT* event;
    int ret = 0, i;

    if (asyncDev == NULL || queue == NULL || multi_req == NULL) {
        return BAD_FUNC_ARG;
    }

    /* Perform multi hardware poll */
    ret = NitroxCheckRequests(asyncDev, multi_req);
    if (ret != 0) {
        return ret;
    }

    /* Iterate event queue */
    for (event = queue->head; event != NULL; event = event->next) {
        if (event->type >= WOLF_EVENT_TYPE_ASYNC_FIRST &&
            event->type <= WOLF_EVENT_TYPE_ASYNC_LAST)
        {
            /* optional filter based on context */
            if (context_filter == NULL || event->context == context_filter) {
                /* find request */
                for (i = 0; i < req_count; i++) {
                    if (event->reqId == multi_req->req[i].request_id) {

                        event->ret = NitroxTranslateResponseCode(
                            multi_req->req[i].status);

                    #ifdef WOLFSSL_NITROX_DEBUG
                        if (event->ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                            event->pendCount++;
                        else
                            printf("NitroxCheckRequests: "
                                "ret %x, req %lx, count %u\n",
                                multi_req->req[i].status,
                                multi_req->req[i].request_id,
                                event->pendCount);
                    #endif

                        /* If not pending then mark as done */
                        if (event->ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
                            event->state = WOLF_EVENT_STATE_DONE;
                            event->reqId = 0;
                        }
                        break;
                    }
                }
            }
        }
    }

    /* reset multi request buffer */
    XMEMSET(multi_req, 0, sizeof(CspMultiRequestStatusBuffer));
    multi_req->count = CAVIUM_MAX_POLL;

    return ret;
}
#endif /* HAVE_CAVIUM */

int wolfAsync_EventQueuePoll(WOLF_EVENT_QUEUE* queue, void* context_filter,
    WOLF_EVENT** events, int maxEvents, WOLF_EVENT_FLAG flags, int* eventCount)
{
    WOLF_EVENT* event;
    int ret = 0, count = 0;
    WC_ASYNC_DEV* asyncDev = NULL;
#if defined(HAVE_CAVIUM)
    CspMultiRequestStatusBuffer multi_req;
    int req_count = 0;

    /* reset multi request buffer */
    XMEMSET(&multi_req, 0, sizeof(CspMultiRequestStatusBuffer));
    multi_req.count = CAVIUM_MAX_POLL;
#endif

    /* possible un-used variable */
    (void)asyncDev;

    if (queue == NULL) {
        return BAD_FUNC_ARG;
    }

#ifndef SINGLE_THREADED
    /* In single threaded mode "event_queue.lock" doesn't exist */
    if ((ret = wc_LockMutex(&queue->lock)) != 0) {
        return ret;
    }
#endif

    if (flags & WOLF_POLL_FLAG_CHECK_HW) {
        /* check event queue */
        for (event = queue->head; event != NULL; event = event->next) {
            if (event->type >= WOLF_EVENT_TYPE_ASYNC_FIRST &&
                event->type <= WOLF_EVENT_TYPE_ASYNC_LAST)
            {
                /* optional filter based on context */
                if (context_filter == NULL ||
                    event->context == context_filter) {
                    asyncDev = event->dev.async;

                    if (asyncDev == NULL) {
                        ret = WC_INIT_E;
                        break;
                    }

                    count++;

            #if defined(HAVE_CAVIUM)
                    /* populate event requestId */
                    event->reqId = asyncDev->nitrox.reqId;

                    /* add entry to multi-request buffer for polling */
                    if (event->reqId > 0) {
                        multi_req.req[req_count++].request_id = event->reqId;
                    }
                    /* submit filled multi-request query */
                    if (req_count == CAVIUM_MAX_POLL) {
                        ret = wolfAsync_NitroxCheckMultiReqBuf(asyncDev,
                                queue, context_filter, &multi_req, req_count);
                        if (ret != 0) {
                            break;
                        }
                    }
            #else
                #if defined(HAVE_INTEL_QA)
                    /* poll QAT hardware, callback returns data,
                     * IntelQaPoll sets event */
                    ret = IntelQaPoll(asyncDev);
                    if (ret != 0) {
                        break;
                    }

                #elif defined(WOLFSSL_ASYNC_CRYPT_SW)
                    #ifdef WOLF_ASYNC_SW_SKIP_MOD
                        /* Simulate random hardware not done */
                        if (count % WOLF_ASYNC_SW_SKIP_MOD)
                    #endif
                        {
                            event->ret = wolfAsync_DoSw(asyncDev);
                        }
                #elif defined(WOLF_CRYPTO_CB) || defined(HAVE_PK_CALLBACKS)
                    /* Crypto/PK callbacks manage their own retry state.
                     * Leave event->ret as WC_PENDING_E so that
                     * wolfSSL_AsyncPop can detect the pending state and
                     * remove the event, allowing the operation to be
                     * retried with a fresh callback invocation. */

                #else
                    #warning No async crypt device defined!
                #endif

                    /* If not pending then mark as done */
                    if (event->ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
                        event->state = WOLF_EVENT_STATE_DONE;
                    }
            #endif
                }
            }
        } /* for */

    #if defined(HAVE_CAVIUM)
        /* submit partial multi-request query (if no prev errors) */
        if (ret == 0 && req_count > 0) {
            ret = wolfAsync_NitroxCheckMultiReqBuf(asyncDev,
                            queue, context_filter, &multi_req, req_count);
        }
    #endif
    } /* flag  WOLF_POLL_FLAG_CHECK_HW */

    /* process event queue */
    count = 0;
    for (event = queue->head; event != NULL; event = event->next) {
        if (event->type >= WOLF_EVENT_TYPE_ASYNC_FIRST &&
            event->type <= WOLF_EVENT_TYPE_ASYNC_LAST)
        {
            /* optional filter based on context */
            if (context_filter == NULL || event->context == context_filter) {
                /* If event is done then process */
                if (event->state == WOLF_EVENT_STATE_DONE) {
                    /* remove from queue */
                    ret = wolfEventQueue_Remove(queue, event);
                    if (ret < 0) break; /* exit for */

                    /* return pointer in 'events' arg */
                    if (events) {
                        events[count] = event; /* return pointer */
                    }
                    count++;

                    /* check to make sure our event list isn't full */
                    if (events && count >= maxEvents) {
                        break; /* exit for */
                    }
                }
            }
        }
    }

#ifndef SINGLE_THREADED
    wc_UnLockMutex(&queue->lock);
#endif

    /* Return number of properly populated events */
    if (eventCount) {
        *eventCount = count;
    }

    return ret;
}

int wolfAsync_EventInit(WOLF_EVENT* event, WOLF_EVENT_TYPE type, void* context,
    word32 flags)
{
    int ret = 0;
    WC_ASYNC_DEV* asyncDev;

    if (event == NULL) {
        return BAD_FUNC_ARG;
    }

    event->type = type;
    event->context = context;
#ifndef WC_NO_ASYNC_THREADING
    event->threadId = wc_AsyncThreadId();
#endif
    event->ret = WC_PENDING_E;
    event->state = WOLF_EVENT_STATE_PENDING;

    asyncDev = wolfAsync_GetDev(event);
    event->dev.async = asyncDev;
    event->flags = flags;
#ifdef HAVE_CAVIUM
    event->reqId = 0;
#endif

    return ret;
}

int wolfAsync_EventWait(WOLF_EVENT* event)
{
    int ret = 0;

    if (event == NULL) {
        return BAD_FUNC_ARG;
    }

    /* wait for completion */
    while (ret == 0 && event->ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfAsync_EventPoll(event, WOLF_POLL_FLAG_CHECK_HW);
    }

    return ret;
}

int wc_AsyncHandle(WC_ASYNC_DEV* asyncDev, WOLF_EVENT_QUEUE* queue,
    word32 event_flags)
{
    int ret;
    WOLF_EVENT* event;

    if (asyncDev == NULL || queue == NULL) {
        return BAD_FUNC_ARG;
    }

    /* setup the event and push to queue */
    event = &asyncDev->event;
    ret = wolfAsync_EventInit(event, WOLF_EVENT_TYPE_ASYNC_WOLFCRYPT,
                                                        asyncDev, event_flags);
    if (ret == 0) {
        ret = wolfEventQueue_Push(queue, event);
    }

    /* check for error (helps with debugging) */
    if (ret != 0) {
        WOLFSSL_MSG("wc_AsyncHandle failed");
    }

    return ret;
}

int wc_AsyncWait(int ret, WC_ASYNC_DEV* asyncDev, word32 event_flags)
{
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        WOLF_EVENT* event;

        if (asyncDev == NULL)
            return BAD_FUNC_ARG;

        event = &asyncDev->event;
        ret = wolfAsync_EventInit(event, WOLF_EVENT_TYPE_ASYNC_WOLFCRYPT,
                                                        asyncDev, event_flags);
        if (ret == 0) {
            ret = wolfAsync_EventWait(event);
            if (ret == 0) {
                ret = event->ret;

                /* clear event */
                event->state = WOLF_EVENT_STATE_READY;
            }
        }
    }
    return ret;
}

#ifndef WC_NO_ASYNC_SLEEP
int wc_AsyncSleep(word32 ms)
{
    int ret = 0;
    struct timespec resTime, remTime;
    resTime.tv_sec = ms/1000;
    resTime.tv_nsec = (ms%1000)*1000000;
    do {
        ret = nanosleep(&resTime, &remTime);
        resTime = remTime;
    } while ((ret!=0) && (errno == EINTR));

    if (ret != 0) {
        fprintf(stderr, "nanoSleep failed with code %d\n", ret);
        return BAD_FUNC_ARG;
    }

   return ret;
}
#endif

/* Pthread Helpers */
#ifndef WC_NO_ASYNC_THREADING

int wc_AsyncGetNumberOfCpus(void)
{
    int numCpus;

    numCpus = (int)sysconf(_SC_NPROCESSORS_ONLN);

    return numCpus;
}

int wc_AsyncThreadCreate_ex(pthread_t *thread,
    word32 priority, int policy,
    AsyncThreadFunc_t function, void* params)
{
    int status = 1;
    pthread_attr_t attr;
    struct sched_param param;

    status = pthread_attr_init(&attr);
    if (status !=0) {
        fprintf(stderr, "pthread_attr_init error: %d\n", status);
        return ASYNC_OP_E;
    }

    /* Setting scheduling parameter will fail for non root user,
     * as the default value of inheritsched is PTHREAD_EXPLICIT_SCHED in
     * POSIX. It is not required to set it explicitly before setting the
     * scheduling policy */

    /* Set scheduling policy based on values provided */
    if ((policy != SCHED_RR) &&
        (policy != SCHED_FIFO) &&
        (policy != SCHED_OTHER))
    {
        policy = SCHED_OTHER;
    }

    status = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    if (status != 0) {
        goto exit_fail;
    }

    status = pthread_attr_setschedpolicy(&attr, policy);
    if (status != 0) {
        goto exit_fail;
    }

    /* Set priority based on value in threadAttr */
    memset(&param, 0, sizeof(param));
    param.sched_priority = priority;
    if (policy != SCHED_OTHER) {
        status = pthread_attr_setschedparam(&attr, &param);
        if (status != 0) {
            goto exit_fail;
        }
    }

    status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    if (status != 0) {
        goto exit_fail;
    }

    status = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    if (status != 0) {
        goto exit_fail;
    }

    status = pthread_create(thread, &attr, function, params);
    if (status != 0) {
        goto exit_fail;
    }

    /*destroy the thread attributes as they are no longer required, this does
    * not affect the created thread*/
    status = pthread_attr_destroy(&attr);
    if (status != 0) {
        fprintf(stderr, "AsyncThreadCreate error: %d\n", status);
        return ASYNC_OP_E;
    } else {
        return 0;
    }

exit_fail:

    fprintf(stderr, "AsyncThreadCreate error: %d\n", status);
    status = pthread_attr_destroy(&attr);
    if (status != 0)
        fprintf(stderr, "AsyncThreadCreate cleanup error: %d\n", status);
    return ASYNC_OP_E;
}

int wc_AsyncThreadCreate(pthread_t *thread,
    AsyncThreadFunc_t function, void* params)
{
    return wc_AsyncThreadCreate_ex(thread, THREAD_DEFAULT_PRIORITY,
        THREAD_DEFAULT_POLICY, function, params);
}

#ifdef __MACH__
    #include <mach/mach.h>
    #include <mach/thread_policy.h>

    /* native MACH API wrappers */
    #define SYSCTL_CORE_COUNT   "machdep.cpu.core_count"

    typedef struct cpu_set {
        uint32_t count;
    } cpu_set_t;

    static WC_INLINE void CPU_ZERO(cpu_set_t *cs) {
        cs->count = 0;
    }
    static WC_INLINE void CPU_SET(int num, cpu_set_t *cs) {
        cs->count |= (1 << num);
    }
    static WC_INLINE int CPU_ISSET(int num, cpu_set_t *cs) {
        return (cs->count & (1 << num));
    }

    static int pthread_setaffinity_np(pthread_t thread, size_t cpu_size,
                               cpu_set_t *cpu_set)
    {
        thread_port_t mach_thread;
        thread_affinity_policy_data_t policy;
        int core = 0;

        for (core = 0; core < 8 * (int)cpu_size; core++) {
            if (CPU_ISSET(core, cpu_set))
                break;
        }

        policy.affinity_tag = core;
        mach_thread = pthread_mach_thread_np(thread);
        thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY,
            (thread_policy_t)&policy, 1);

        return 0;
    }
#endif /* __MACH__ */

int wc_AsyncThreadBind(pthread_t *thread, word32 logicalCore)
{
    int status = 0;
    cpu_set_t cpuset;

    if (!thread) return BAD_FUNC_ARG;

    CPU_ZERO(&cpuset);
    CPU_SET(logicalCore, &cpuset);

    status = pthread_setaffinity_np(*thread, sizeof(cpu_set_t), &cpuset);
    if (status != 0) {
        fprintf(stderr, "pthread_setaffinity_np error: %d\n", status);
    }

    return status;
}

int wc_AsyncThreadStart(pthread_t *thread)
{
    (void)thread;
    return 0;
}

__attribute__((noreturn))
void wc_AsyncThreadExit(void *retval)
{
    pthread_exit(retval);
}

int wc_AsyncThreadKill(pthread_t *thread)
{
    int status;

    if (!thread) return BAD_FUNC_ARG;

    status = pthread_cancel(*thread);
    if (status != 0) {
        fprintf(stderr, "pthread_cancel fail with status %d\n", status);
    }

    return status;
}


int wc_AsyncThreadPrioritySet(pthread_t *thread, word32 priority)
{
    int status;
    struct sched_param param;
    int policy;
    word32 minPrio;
    word32 maxPrio;

    if (!thread) return BAD_FUNC_ARG;

    status = pthread_getschedparam(*thread, &policy, &param);
    if (status != 0) {
        fprintf(stderr, "pthread_getschedparam, failed with status %d\n",
            status);
        return status;
    }

    minPrio = sched_get_priority_min(policy);
    maxPrio =  sched_get_priority_max(policy);

    if ((priority < minPrio) || (priority > maxPrio)) {
        fprintf(stderr, "priority %u outside valid range\n", priority);
        return BAD_FUNC_ARG;
    }

    param.sched_priority = priority;

    status = pthread_setschedparam(*thread, policy, &param);
    if (status != 0) {
        fprintf(stderr, "pthread_setschedparam, failed with status %d\n",
            status);
        return status;
    }

    return status;
}

int wc_AsyncThreadSetPolicyAndPriority(pthread_t *thread, word32 policy,
    word32 priority)
{
    int status;
    struct sched_param  param;
    word32 minPrio, maxPrio;
    int policy1;

    if (!thread) return BAD_FUNC_ARG;

    /* check for a valid value for 'policy' */
    if ((policy != SCHED_RR) &&
        (policy != SCHED_FIFO) &&
        (policy != SCHED_OTHER))
    {
        fprintf(stderr, "wc_AsyncThreadSetPolicyAndPriority: "
            "invalid policy %u\n", policy);
        return BAD_FUNC_ARG;
    }

    memset(&param, 0, sizeof(param));

    status = pthread_getschedparam(*thread, &policy1, &param);
    if (status != 0) {
        fprintf(stderr, "pthread_getschedparam error: %d\n", status);
        return status;
    }

    minPrio = sched_get_priority_min(policy);
    maxPrio =  sched_get_priority_max(policy);

    if ((priority < minPrio) || (priority > maxPrio)) {
        return BAD_FUNC_ARG;
    }

    param.sched_priority = priority;

    status = pthread_setschedparam(*thread, policy, &param);
    if (status != 0) {
        fprintf(stderr, "pthread_setschedparam error: %d\n", status);
        return status;
    }

    return 0;
}

int wc_AsyncThreadJoin(pthread_t *thread)
{
    int status;
    status = pthread_join(*thread, NULL);
    if (status != 0) {
        fprintf(stderr, "pthread_join failed, status: %d\n", status);
    }
    return status;
}

void wc_AsyncThreadYield(void)
{
    sched_yield();
}

pthread_t wc_AsyncThreadId(void)
{
    return pthread_self();
}

#endif /* WC_NO_ASYNC_THREADING */

#endif /* WOLFSSL_ASYNC_CRYPT */
